mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-27 06:04:23 +08:00
ASoC: samsung: remove unused drivers
The s3c24xx SoC platform was completely removed, as were most of the s3c64xx based board files, leaving only the DT based machines as well as the MACH_WLF_CRAGG_6410 machine. All other board specific ASoC driver can can now be recycled. Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
parent
2e3ee090cd
commit
503278c127
@ -1,30 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright 2008 Simtec Electronics
|
||||
* http://armlinux.simtec.co.uk/
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*
|
||||
* Simtec Audio support.
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct s3c24xx_audio_simtec_pdata - platform data for simtec audio
|
||||
* @use_mpllin: Select codec clock from MPLLin
|
||||
* @output_cdclk: Need to output CDCLK to the codec
|
||||
* @have_mic: Set if we have a MIC socket
|
||||
* @have_lout: Set if we have a LineOut socket
|
||||
* @amp_gpio: GPIO pin to enable the AMP
|
||||
* @amp_gain: Option GPIO to control AMP gain
|
||||
*/
|
||||
struct s3c24xx_audio_simtec_pdata {
|
||||
unsigned int use_mpllin:1;
|
||||
unsigned int output_cdclk:1;
|
||||
|
||||
unsigned int have_mic:1;
|
||||
unsigned int have_lout:1;
|
||||
|
||||
int amp_gpio;
|
||||
int amp_gain[2];
|
||||
|
||||
void (*startup)(void);
|
||||
};
|
@ -1,14 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _S3C24XX_UDA134X_H_
|
||||
#define _S3C24XX_UDA134X_H_ 1
|
||||
|
||||
#include <sound/uda134x.h>
|
||||
|
||||
struct s3c24xx_uda134x_platform_data {
|
||||
int l3_clk;
|
||||
int l3_mode;
|
||||
int l3_data;
|
||||
int model;
|
||||
};
|
||||
|
||||
#endif
|
@ -11,16 +11,6 @@ menuconfig SND_SOC_SAMSUNG
|
||||
|
||||
if SND_SOC_SAMSUNG
|
||||
|
||||
config SND_S3C24XX_I2S
|
||||
tristate
|
||||
|
||||
config SND_S3C_I2SV2_SOC
|
||||
tristate
|
||||
|
||||
config SND_S3C2412_SOC_I2S
|
||||
tristate
|
||||
select SND_S3C_I2SV2_SOC
|
||||
|
||||
config SND_SAMSUNG_PCM
|
||||
tristate "Samsung PCM interface support"
|
||||
|
||||
@ -31,35 +21,6 @@ config SND_SAMSUNG_SPDIF
|
||||
config SND_SAMSUNG_I2S
|
||||
tristate "Samsung I2S interface support"
|
||||
|
||||
config SND_SOC_SAMSUNG_NEO1973_WM8753
|
||||
tristate "Audio support for Openmoko Neo1973 Smartphones (GTA02)"
|
||||
depends on MACH_NEO1973_GTA02 || COMPILE_TEST
|
||||
depends on SND_SOC_I2C_AND_SPI
|
||||
select SND_S3C24XX_I2S
|
||||
select SND_SOC_WM8753
|
||||
select SND_SOC_BT_SCO
|
||||
help
|
||||
Say Y here to enable audio support for the Openmoko Neo1973
|
||||
Smartphones.
|
||||
|
||||
config SND_SOC_SAMSUNG_JIVE_WM8750
|
||||
tristate "SoC I2S Audio support for Jive"
|
||||
depends on MACH_JIVE && I2C || COMPILE_TEST && ARM
|
||||
depends on SND_SOC_I2C_AND_SPI
|
||||
select SND_SOC_WM8750
|
||||
select SND_S3C2412_SOC_I2S
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on the Jive.
|
||||
|
||||
config SND_SOC_SAMSUNG_SMDK_WM8580
|
||||
tristate "SoC I2S Audio support for WM8580 on SMDK"
|
||||
depends on MACH_SMDK6410 || COMPILE_TEST
|
||||
depends on I2C
|
||||
select SND_SOC_WM8580
|
||||
select SND_SAMSUNG_I2S
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on the SMDKs.
|
||||
|
||||
config SND_SOC_SAMSUNG_SMDK_WM8994
|
||||
tristate "SoC I2S Audio support for WM8994 on SMDK"
|
||||
depends on I2C=y
|
||||
@ -69,60 +30,6 @@ config SND_SOC_SAMSUNG_SMDK_WM8994
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on the SMDKs.
|
||||
|
||||
config SND_SOC_SAMSUNG_S3C24XX_UDA134X
|
||||
tristate "SoC I2S Audio support UDA134X wired to a S3C24XX"
|
||||
depends on ARCH_S3C24XX || COMPILE_TEST
|
||||
select SND_S3C24XX_I2S
|
||||
select SND_SOC_L3
|
||||
select SND_SOC_UDA134X
|
||||
|
||||
config SND_SOC_SAMSUNG_SIMTEC
|
||||
tristate
|
||||
help
|
||||
Internal node for common S3C24XX/Simtec support.
|
||||
|
||||
config SND_SOC_SAMSUNG_SIMTEC_TLV320AIC23
|
||||
tristate "SoC I2S Audio support for TLV320AIC23 on Simtec boards"
|
||||
depends on ARCH_S3C24XX || COMPILE_TEST
|
||||
depends on I2C
|
||||
select SND_S3C24XX_I2S
|
||||
select SND_SOC_TLV320AIC23_I2C
|
||||
select SND_SOC_SAMSUNG_SIMTEC
|
||||
|
||||
config SND_SOC_SAMSUNG_SIMTEC_HERMES
|
||||
tristate "SoC I2S Audio support for Simtec Hermes board"
|
||||
depends on ARCH_S3C24XX || COMPILE_TEST
|
||||
depends on I2C
|
||||
select SND_S3C24XX_I2S
|
||||
select SND_SOC_TLV320AIC3X
|
||||
select SND_SOC_SAMSUNG_SIMTEC
|
||||
|
||||
config SND_SOC_SAMSUNG_H1940_UDA1380
|
||||
tristate "Audio support for the HP iPAQ H1940"
|
||||
depends on ARCH_H1940 || COMPILE_TEST
|
||||
depends on I2C
|
||||
select SND_S3C24XX_I2S
|
||||
select SND_SOC_UDA1380
|
||||
help
|
||||
This driver provides audio support for HP iPAQ h1940 PDA.
|
||||
|
||||
config SND_SOC_SAMSUNG_RX1950_UDA1380
|
||||
tristate "Audio support for the HP iPAQ RX1950"
|
||||
depends on MACH_RX1950 || COMPILE_TEST
|
||||
depends on I2C
|
||||
select SND_S3C24XX_I2S
|
||||
select SND_SOC_UDA1380
|
||||
help
|
||||
This driver provides audio support for HP iPAQ RX1950 PDA.
|
||||
|
||||
config SND_SOC_SMARTQ
|
||||
tristate "SoC I2S Audio support for SmartQ board"
|
||||
depends on MACH_SMARTQ || COMPILE_TEST
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
depends on I2C
|
||||
select SND_SAMSUNG_I2S
|
||||
select SND_SOC_WM8750
|
||||
|
||||
config SND_SOC_SAMSUNG_SMDK_SPDIF
|
||||
tristate "SoC S/PDIF Audio support for SMDK"
|
||||
select SND_SAMSUNG_SPDIF
|
||||
|
@ -2,35 +2,19 @@
|
||||
# S3c24XX Platform Support
|
||||
snd-soc-s3c-dma-objs := dmaengine.o
|
||||
snd-soc-idma-objs := idma.o
|
||||
snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
|
||||
snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o
|
||||
snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
|
||||
snd-soc-samsung-spdif-objs := spdif.o
|
||||
snd-soc-pcm-objs := pcm.o
|
||||
snd-soc-i2s-objs := i2s.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_SAMSUNG) += snd-soc-s3c-dma.o
|
||||
obj-$(CONFIG_SND_S3C24XX_I2S) += snd-soc-s3c24xx-i2s.o
|
||||
obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
|
||||
obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
|
||||
obj-$(CONFIG_SND_SAMSUNG_SPDIF) += snd-soc-samsung-spdif.o
|
||||
obj-$(CONFIG_SND_SAMSUNG_PCM) += snd-soc-pcm.o
|
||||
obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o
|
||||
obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-idma.o
|
||||
|
||||
# S3C24XX Machine Support
|
||||
snd-soc-jive-wm8750-objs := jive_wm8750.o
|
||||
snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
|
||||
snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
|
||||
snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o
|
||||
snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o
|
||||
snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o
|
||||
snd-soc-h1940-uda1380-objs := h1940_uda1380.o
|
||||
snd-soc-rx1950-uda1380-objs := rx1950_uda1380.o
|
||||
snd-soc-smdk-wm8580-objs := smdk_wm8580.o
|
||||
snd-soc-smdk-wm8994-objs := smdk_wm8994.o
|
||||
snd-soc-snow-objs := snow.o
|
||||
snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o
|
||||
snd-soc-smdk-spdif-objs := smdk_spdif.o
|
||||
snd-soc-smdk-wm8994pcm-objs := smdk_wm8994pcm.o
|
||||
snd-soc-speyside-objs := speyside.o
|
||||
@ -44,18 +28,8 @@ snd-soc-tm2-wm5110-objs := tm2_wm5110.o
|
||||
snd-soc-aries-wm8994-objs := aries_wm8994.o
|
||||
snd-soc-midas-wm1811-objs := midas_wm1811.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
|
||||
obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
|
||||
obj-$(CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o
|
||||
obj-$(CONFIG_SND_SOC_SAMSUNG_SIMTEC) += snd-soc-s3c24xx-simtec.o
|
||||
obj-$(CONFIG_SND_SOC_SAMSUNG_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
|
||||
obj-$(CONFIG_SND_SOC_SAMSUNG_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
|
||||
obj-$(CONFIG_SND_SOC_SAMSUNG_H1940_UDA1380) += snd-soc-h1940-uda1380.o
|
||||
obj-$(CONFIG_SND_SOC_SAMSUNG_RX1950_UDA1380) += snd-soc-rx1950-uda1380.o
|
||||
obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM8580) += snd-soc-smdk-wm8580.o
|
||||
obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM8994) += snd-soc-smdk-wm8994.o
|
||||
obj-$(CONFIG_SND_SOC_SNOW) += snd-soc-snow.o
|
||||
obj-$(CONFIG_SND_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o
|
||||
obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF) += snd-soc-smdk-spdif.o
|
||||
obj-$(CONFIG_SND_SOC_SMDK_WM8994_PCM) += snd-soc-smdk-wm8994pcm.o
|
||||
obj-$(CONFIG_SND_SOC_SPEYSIDE) += snd-soc-speyside.o
|
||||
|
@ -1,224 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// h1940_uda1380.c - ALSA SoC Audio Layer
|
||||
//
|
||||
// Copyright (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
|
||||
// Copyright (c) 2010 Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
//
|
||||
// Based on version from Arnaud Patard <arnaud.patard@rtp-net.org>
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
|
||||
#include "regs-iis.h"
|
||||
#include "s3c24xx-i2s.h"
|
||||
|
||||
static const unsigned int rates[] = {
|
||||
11025,
|
||||
22050,
|
||||
44100,
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hw_constraint_list hw_rates = {
|
||||
.count = ARRAY_SIZE(rates),
|
||||
.list = rates,
|
||||
};
|
||||
|
||||
static struct gpio_desc *gpiod_speaker_power;
|
||||
|
||||
static struct snd_soc_jack hp_jack;
|
||||
|
||||
static struct snd_soc_jack_pin hp_jack_pins[] = {
|
||||
{
|
||||
.pin = "Headphone Jack",
|
||||
.mask = SND_JACK_HEADPHONE,
|
||||
},
|
||||
{
|
||||
.pin = "Speaker",
|
||||
.mask = SND_JACK_HEADPHONE,
|
||||
.invert = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_jack_gpio hp_jack_gpios[] = {
|
||||
{
|
||||
.name = "hp-gpio",
|
||||
.report = SND_JACK_HEADPHONE,
|
||||
.invert = 1,
|
||||
.debounce_time = 200,
|
||||
},
|
||||
};
|
||||
|
||||
static int h1940_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
return snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&hw_rates);
|
||||
}
|
||||
|
||||
static int h1940_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
|
||||
int div;
|
||||
int ret;
|
||||
unsigned int rate = params_rate(params);
|
||||
|
||||
switch (rate) {
|
||||
case 11025:
|
||||
case 22050:
|
||||
case 44100:
|
||||
div = s3c24xx_i2s_get_clockrate() / (384 * rate);
|
||||
if (s3c24xx_i2s_get_clockrate() % (384 * rate) > (192 * rate))
|
||||
div++;
|
||||
break;
|
||||
default:
|
||||
dev_err(rtd->dev, "%s: rate %d is not supported\n",
|
||||
__func__, rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* select clock source */
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_PCLK, rate,
|
||||
SND_SOC_CLOCK_OUT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set MCLK division for sample rate */
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
|
||||
S3C2410_IISMOD_384FS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set BCLK division for sample rate */
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
|
||||
S3C2410_IISMOD_32FS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set prescaler division for sample rate */
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
|
||||
S3C24XX_PRESCALE(div, div));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops h1940_ops = {
|
||||
.startup = h1940_startup,
|
||||
.hw_params = h1940_hw_params,
|
||||
};
|
||||
|
||||
static int h1940_spk_power(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
if (SND_SOC_DAPM_EVENT_ON(event))
|
||||
gpiod_set_value(gpiod_speaker_power, 1);
|
||||
else
|
||||
gpiod_set_value(gpiod_speaker_power, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* h1940 machine dapm widgets */
|
||||
static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("Mic Jack", NULL),
|
||||
SND_SOC_DAPM_SPK("Speaker", h1940_spk_power),
|
||||
};
|
||||
|
||||
/* h1940 machine audio_map */
|
||||
static const struct snd_soc_dapm_route audio_map[] = {
|
||||
/* headphone connected to VOUTLHP, VOUTRHP */
|
||||
{"Headphone Jack", NULL, "VOUTLHP"},
|
||||
{"Headphone Jack", NULL, "VOUTRHP"},
|
||||
|
||||
/* ext speaker connected to VOUTL, VOUTR */
|
||||
{"Speaker", NULL, "VOUTL"},
|
||||
{"Speaker", NULL, "VOUTR"},
|
||||
|
||||
/* mic is connected to VINM */
|
||||
{"VINM", NULL, "Mic Jack"},
|
||||
};
|
||||
|
||||
static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
|
||||
SND_JACK_HEADPHONE,
|
||||
&hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins));
|
||||
|
||||
snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
|
||||
hp_jack_gpios);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* s3c24xx digital audio interface glue - connects codec <--> CPU */
|
||||
SND_SOC_DAILINK_DEFS(uda1380,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("s3c24xx-iis")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("uda1380-codec.0-001a", "uda1380-hifi")),
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("s3c24xx-iis")));
|
||||
|
||||
static struct snd_soc_dai_link h1940_uda1380_dai[] = {
|
||||
{
|
||||
.name = "uda1380",
|
||||
.stream_name = "UDA1380 Duplex",
|
||||
.init = h1940_uda1380_init,
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.ops = &h1940_ops,
|
||||
SND_SOC_DAILINK_REG(uda1380),
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card h1940_asoc = {
|
||||
.name = "h1940",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = h1940_uda1380_dai,
|
||||
.num_links = ARRAY_SIZE(h1940_uda1380_dai),
|
||||
|
||||
.dapm_widgets = uda1380_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
|
||||
.dapm_routes = audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(audio_map),
|
||||
};
|
||||
|
||||
static int h1940_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
h1940_asoc.dev = dev;
|
||||
hp_jack_gpios[0].gpiod_dev = dev;
|
||||
gpiod_speaker_power = devm_gpiod_get(&pdev->dev, "speaker-power",
|
||||
GPIOD_OUT_LOW);
|
||||
|
||||
if (IS_ERR(gpiod_speaker_power)) {
|
||||
dev_err(dev, "Could not get gpio\n");
|
||||
return PTR_ERR(gpiod_speaker_power);
|
||||
}
|
||||
|
||||
return devm_snd_soc_register_card(dev, &h1940_asoc);
|
||||
}
|
||||
|
||||
static struct platform_driver h1940_audio_driver = {
|
||||
.driver = {
|
||||
.name = "h1940-audio",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
.probe = h1940_probe,
|
||||
};
|
||||
module_platform_driver(h1940_audio_driver);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Arnaud Patard, Vasily Khoruzhick");
|
||||
MODULE_DESCRIPTION("ALSA SoC H1940");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:h1940-audio");
|
@ -1,143 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Copyright 2007,2008 Simtec Electronics
|
||||
//
|
||||
// Based on sound/soc/pxa/spitz.c
|
||||
// Copyright 2005 Wolfson Microelectronics PLC.
|
||||
// Copyright 2005 Openedhand Ltd.
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
#include "s3c2412-i2s.h"
|
||||
#include "../codecs/wm8750.h"
|
||||
|
||||
static const struct snd_soc_dapm_route audio_map[] = {
|
||||
{ "Headphone Jack", NULL, "LOUT1" },
|
||||
{ "Headphone Jack", NULL, "ROUT1" },
|
||||
{ "Internal Speaker", NULL, "LOUT2" },
|
||||
{ "Internal Speaker", NULL, "ROUT2" },
|
||||
{ "LINPUT1", NULL, "Line Input" },
|
||||
{ "RINPUT1", NULL, "Line Input" },
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_SPK("Internal Speaker", NULL),
|
||||
SND_SOC_DAPM_LINE("Line In", NULL),
|
||||
};
|
||||
|
||||
static int jive_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
|
||||
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
|
||||
struct s3c_i2sv2_rate_calc div;
|
||||
unsigned int clk = 0;
|
||||
int ret = 0;
|
||||
|
||||
switch (params_rate(params)) {
|
||||
case 8000:
|
||||
case 16000:
|
||||
case 48000:
|
||||
case 96000:
|
||||
clk = 12288000;
|
||||
break;
|
||||
case 11025:
|
||||
case 22050:
|
||||
case 44100:
|
||||
clk = 11289600;
|
||||
break;
|
||||
}
|
||||
|
||||
s3c_i2sv2_iis_calc_rate(&div, NULL, params_rate(params),
|
||||
s3c_i2sv2_get_clock(cpu_dai));
|
||||
|
||||
/* set the codec system clock for DAC and ADC */
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C2412_DIV_RCLK, div.fs_div);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C2412_DIV_PRESCALER,
|
||||
div.clk_div - 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops jive_ops = {
|
||||
.hw_params = jive_hw_params,
|
||||
};
|
||||
|
||||
SND_SOC_DAILINK_DEFS(wm8750,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("s3c2412-i2s")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("wm8750.0-001a", "wm8750-hifi")),
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("s3c2412-i2s")));
|
||||
|
||||
static struct snd_soc_dai_link jive_dai = {
|
||||
.name = "wm8750",
|
||||
.stream_name = "WM8750",
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.ops = &jive_ops,
|
||||
SND_SOC_DAILINK_REG(wm8750),
|
||||
};
|
||||
|
||||
/* jive audio machine driver */
|
||||
static struct snd_soc_card snd_soc_machine_jive = {
|
||||
.name = "Jive",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = &jive_dai,
|
||||
.num_links = 1,
|
||||
|
||||
.dapm_widgets = wm8750_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
|
||||
.dapm_routes = audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(audio_map),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static struct platform_device *jive_snd_device;
|
||||
|
||||
static int __init jive_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!machine_is_jive())
|
||||
return 0;
|
||||
|
||||
printk("JIVE WM8750 Audio support\n");
|
||||
|
||||
jive_snd_device = platform_device_alloc("soc-audio", -1);
|
||||
if (!jive_snd_device)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(jive_snd_device, &snd_soc_machine_jive);
|
||||
ret = platform_device_add(jive_snd_device);
|
||||
|
||||
if (ret)
|
||||
platform_device_put(jive_snd_device);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit jive_exit(void)
|
||||
{
|
||||
platform_device_unregister(jive_snd_device);
|
||||
}
|
||||
|
||||
module_init(jive_init);
|
||||
module_exit(jive_exit);
|
||||
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
||||
MODULE_DESCRIPTION("ALSA SoC Jive Audio support");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,360 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// neo1973_wm8753.c - SoC audio for Openmoko Neo1973 and Freerunner devices
|
||||
//
|
||||
// Copyright 2007 Openmoko Inc
|
||||
// Author: Graeme Gregory <graeme@openmoko.org>
|
||||
// Copyright 2007 Wolfson Microelectronics PLC.
|
||||
// Author: Graeme Gregory
|
||||
// graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
|
||||
// Copyright 2009 Wolfson Microelectronics
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "regs-iis.h"
|
||||
#include "../codecs/wm8753.h"
|
||||
#include "s3c24xx-i2s.h"
|
||||
|
||||
static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
|
||||
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
|
||||
unsigned int pll_out = 0, bclk = 0;
|
||||
int ret = 0;
|
||||
unsigned long iis_clkrate;
|
||||
|
||||
iis_clkrate = s3c24xx_i2s_get_clockrate();
|
||||
|
||||
switch (params_rate(params)) {
|
||||
case 8000:
|
||||
case 16000:
|
||||
pll_out = 12288000;
|
||||
break;
|
||||
case 48000:
|
||||
bclk = WM8753_BCLK_DIV_4;
|
||||
pll_out = 12288000;
|
||||
break;
|
||||
case 96000:
|
||||
bclk = WM8753_BCLK_DIV_2;
|
||||
pll_out = 12288000;
|
||||
break;
|
||||
case 11025:
|
||||
bclk = WM8753_BCLK_DIV_16;
|
||||
pll_out = 11289600;
|
||||
break;
|
||||
case 22050:
|
||||
bclk = WM8753_BCLK_DIV_8;
|
||||
pll_out = 11289600;
|
||||
break;
|
||||
case 44100:
|
||||
bclk = WM8753_BCLK_DIV_4;
|
||||
pll_out = 11289600;
|
||||
break;
|
||||
case 88200:
|
||||
bclk = WM8753_BCLK_DIV_2;
|
||||
pll_out = 11289600;
|
||||
break;
|
||||
}
|
||||
|
||||
/* set the codec system clock for DAC and ADC */
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set MCLK division for sample rate */
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
|
||||
S3C2410_IISMOD_32FS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set codec BCLK division for sample rate */
|
||||
ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set prescaler division for sample rate */
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
|
||||
S3C24XX_PRESCALE(4, 4));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* codec PLL input is PCLK/4 */
|
||||
ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
|
||||
iis_clkrate / 4, pll_out);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
|
||||
|
||||
/* disable the PLL */
|
||||
return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Neo1973 WM8753 HiFi DAI opserations.
|
||||
*/
|
||||
static const struct snd_soc_ops neo1973_hifi_ops = {
|
||||
.hw_params = neo1973_hifi_hw_params,
|
||||
.hw_free = neo1973_hifi_hw_free,
|
||||
};
|
||||
|
||||
static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
|
||||
unsigned int pcmdiv = 0;
|
||||
int ret = 0;
|
||||
unsigned long iis_clkrate;
|
||||
|
||||
iis_clkrate = s3c24xx_i2s_get_clockrate();
|
||||
|
||||
if (params_rate(params) != 8000)
|
||||
return -EINVAL;
|
||||
if (params_channels(params) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
|
||||
|
||||
/* set the codec system clock for DAC and ADC */
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set codec PCM division for sample rate */
|
||||
ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* configure and enable PLL for 12.288MHz output */
|
||||
ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
|
||||
iis_clkrate / 4, 12288000);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
|
||||
|
||||
/* disable the PLL */
|
||||
return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops neo1973_voice_ops = {
|
||||
.hw_params = neo1973_voice_hw_params,
|
||||
.hw_free = neo1973_voice_hw_free,
|
||||
};
|
||||
|
||||
static struct gpio_desc *gpiod_hp_in, *gpiod_amp_shut;
|
||||
static int gta02_speaker_enabled;
|
||||
|
||||
static int lm4853_set_spk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
gta02_speaker_enabled = ucontrol->value.integer.value[0];
|
||||
|
||||
gpiod_set_value(gpiod_hp_in, !gta02_speaker_enabled);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lm4853_get_spk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = gta02_speaker_enabled;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lm4853_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
gpiod_set_value(gpiod_amp_shut, SND_SOC_DAPM_EVENT_OFF(event));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget neo1973_wm8753_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_LINE("GSM Line Out", NULL),
|
||||
SND_SOC_DAPM_LINE("GSM Line In", NULL),
|
||||
SND_SOC_DAPM_MIC("Headset Mic", NULL),
|
||||
SND_SOC_DAPM_MIC("Handset Mic", NULL),
|
||||
SND_SOC_DAPM_SPK("Handset Spk", NULL),
|
||||
SND_SOC_DAPM_SPK("Stereo Out", lm4853_event),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route neo1973_wm8753_routes[] = {
|
||||
/* Connections to the GSM Module */
|
||||
{"GSM Line Out", NULL, "MONO1"},
|
||||
{"GSM Line Out", NULL, "MONO2"},
|
||||
{"RXP", NULL, "GSM Line In"},
|
||||
{"RXN", NULL, "GSM Line In"},
|
||||
|
||||
/* Connections to Headset */
|
||||
{"MIC1", NULL, "Mic Bias"},
|
||||
{"Mic Bias", NULL, "Headset Mic"},
|
||||
|
||||
/* Call Mic */
|
||||
{"MIC2", NULL, "Mic Bias"},
|
||||
{"MIC2N", NULL, "Mic Bias"},
|
||||
{"Mic Bias", NULL, "Handset Mic"},
|
||||
|
||||
/* Connect the ALC pins */
|
||||
{"ACIN", NULL, "ACOP"},
|
||||
|
||||
/* Connections to the amp */
|
||||
{"Stereo Out", NULL, "LOUT1"},
|
||||
{"Stereo Out", NULL, "ROUT1"},
|
||||
|
||||
/* Call Speaker */
|
||||
{"Handset Spk", NULL, "LOUT2"},
|
||||
{"Handset Spk", NULL, "ROUT2"},
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new neo1973_wm8753_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("GSM Line Out"),
|
||||
SOC_DAPM_PIN_SWITCH("GSM Line In"),
|
||||
SOC_DAPM_PIN_SWITCH("Headset Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Handset Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Handset Spk"),
|
||||
SOC_DAPM_PIN_SWITCH("Stereo Out"),
|
||||
|
||||
SOC_SINGLE_BOOL_EXT("Amp Spk Switch", 0,
|
||||
lm4853_get_spk,
|
||||
lm4853_set_spk),
|
||||
};
|
||||
|
||||
static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
|
||||
/* set endpoints to default off mode */
|
||||
snd_soc_dapm_disable_pin(&card->dapm, "GSM Line Out");
|
||||
snd_soc_dapm_disable_pin(&card->dapm, "GSM Line In");
|
||||
snd_soc_dapm_disable_pin(&card->dapm, "Headset Mic");
|
||||
snd_soc_dapm_disable_pin(&card->dapm, "Handset Mic");
|
||||
snd_soc_dapm_disable_pin(&card->dapm, "Stereo Out");
|
||||
snd_soc_dapm_disable_pin(&card->dapm, "Handset Spk");
|
||||
|
||||
/* allow audio paths from the GSM modem to run during suspend */
|
||||
snd_soc_dapm_ignore_suspend(&card->dapm, "GSM Line Out");
|
||||
snd_soc_dapm_ignore_suspend(&card->dapm, "GSM Line In");
|
||||
snd_soc_dapm_ignore_suspend(&card->dapm, "Headset Mic");
|
||||
snd_soc_dapm_ignore_suspend(&card->dapm, "Handset Mic");
|
||||
snd_soc_dapm_ignore_suspend(&card->dapm, "Stereo Out");
|
||||
snd_soc_dapm_ignore_suspend(&card->dapm, "Handset Spk");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SND_SOC_DAILINK_DEFS(wm8753,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("s3c24xx-iis")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("wm8753.0-001a", "wm8753-hifi")),
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("s3c24xx-iis")));
|
||||
|
||||
SND_SOC_DAILINK_DEFS(bluetooth,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("bt-sco-pcm")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("wm8753.0-001a", "wm8753-voice")));
|
||||
|
||||
static struct snd_soc_dai_link neo1973_dai[] = {
|
||||
{ /* Hifi Playback - for similatious use with voice below */
|
||||
.name = "WM8753",
|
||||
.stream_name = "WM8753 HiFi",
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBM_CFM,
|
||||
.init = neo1973_wm8753_init,
|
||||
.ops = &neo1973_hifi_ops,
|
||||
SND_SOC_DAILINK_REG(wm8753),
|
||||
},
|
||||
{ /* Voice via BT */
|
||||
.name = "Bluetooth",
|
||||
.stream_name = "Voice",
|
||||
.dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.ops = &neo1973_voice_ops,
|
||||
SND_SOC_DAILINK_REG(bluetooth),
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_aux_dev neo1973_aux_devs[] = {
|
||||
{
|
||||
.dlc = COMP_AUX("dfbmcs320.0"),
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_codec_conf neo1973_codec_conf[] = {
|
||||
{
|
||||
.dlc = COMP_CODEC_CONF("lm4857.0-007c"),
|
||||
.name_prefix = "Amp",
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card neo1973 = {
|
||||
.name = "neo1973gta02",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = neo1973_dai,
|
||||
.num_links = ARRAY_SIZE(neo1973_dai),
|
||||
.aux_dev = neo1973_aux_devs,
|
||||
.num_aux_devs = ARRAY_SIZE(neo1973_aux_devs),
|
||||
.codec_conf = neo1973_codec_conf,
|
||||
.num_configs = ARRAY_SIZE(neo1973_codec_conf),
|
||||
|
||||
.controls = neo1973_wm8753_controls,
|
||||
.num_controls = ARRAY_SIZE(neo1973_wm8753_controls),
|
||||
.dapm_widgets = neo1973_wm8753_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(neo1973_wm8753_dapm_widgets),
|
||||
.dapm_routes = neo1973_wm8753_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(neo1973_wm8753_routes),
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
static int neo1973_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
gpiod_hp_in = devm_gpiod_get(dev, "hp", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(gpiod_hp_in)) {
|
||||
dev_err(dev, "missing gpio %s\n", "hp");
|
||||
return PTR_ERR(gpiod_hp_in);
|
||||
}
|
||||
gpiod_amp_shut = devm_gpiod_get(dev, "amp-shut", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(gpiod_amp_shut)) {
|
||||
dev_err(dev, "missing gpio %s\n", "amp-shut");
|
||||
return PTR_ERR(gpiod_amp_shut);
|
||||
}
|
||||
|
||||
neo1973.dev = dev;
|
||||
return devm_snd_soc_register_card(dev, &neo1973);
|
||||
}
|
||||
|
||||
static struct platform_driver neo1973_audio = {
|
||||
.driver = {
|
||||
.name = "neo1973-audio",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
.probe = neo1973_probe,
|
||||
};
|
||||
module_platform_driver(neo1973_audio);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org, www.openmoko.org");
|
||||
MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 and Frerunner");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:neo1973-audio");
|
@ -1,111 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2007 Simtec Electronics <linux@simtec.co.uk>
|
||||
* http://armlinux.simtec.co.uk/
|
||||
*
|
||||
* S3C2412 IIS register definition
|
||||
*/
|
||||
|
||||
#ifndef __ASM_ARCH_REGS_S3C2412_IIS_H
|
||||
#define __ASM_ARCH_REGS_S3C2412_IIS_H
|
||||
|
||||
#define S3C2412_IISCON (0x00)
|
||||
#define S3C2412_IISMOD (0x04)
|
||||
#define S3C2412_IISFIC (0x08)
|
||||
#define S3C2412_IISPSR (0x0C)
|
||||
#define S3C2412_IISTXD (0x10)
|
||||
#define S3C2412_IISRXD (0x14)
|
||||
|
||||
#define S5PC1XX_IISFICS 0x18
|
||||
#define S5PC1XX_IISTXDS 0x1C
|
||||
|
||||
#define S5PC1XX_IISCON_SW_RST (1 << 31)
|
||||
#define S5PC1XX_IISCON_FRXOFSTATUS (1 << 26)
|
||||
#define S5PC1XX_IISCON_FRXORINTEN (1 << 25)
|
||||
#define S5PC1XX_IISCON_FTXSURSTAT (1 << 24)
|
||||
#define S5PC1XX_IISCON_FTXSURINTEN (1 << 23)
|
||||
#define S5PC1XX_IISCON_TXSDMAPAUSE (1 << 20)
|
||||
#define S5PC1XX_IISCON_TXSDMACTIVE (1 << 18)
|
||||
|
||||
#define S3C64XX_IISCON_FTXURSTATUS (1 << 17)
|
||||
#define S3C64XX_IISCON_FTXURINTEN (1 << 16)
|
||||
#define S3C64XX_IISCON_TXFIFO2_EMPTY (1 << 15)
|
||||
#define S3C64XX_IISCON_TXFIFO1_EMPTY (1 << 14)
|
||||
#define S3C64XX_IISCON_TXFIFO2_FULL (1 << 13)
|
||||
#define S3C64XX_IISCON_TXFIFO1_FULL (1 << 12)
|
||||
|
||||
#define S3C2412_IISCON_LRINDEX (1 << 11)
|
||||
#define S3C2412_IISCON_TXFIFO_EMPTY (1 << 10)
|
||||
#define S3C2412_IISCON_RXFIFO_EMPTY (1 << 9)
|
||||
#define S3C2412_IISCON_TXFIFO_FULL (1 << 8)
|
||||
#define S3C2412_IISCON_RXFIFO_FULL (1 << 7)
|
||||
#define S3C2412_IISCON_TXDMA_PAUSE (1 << 6)
|
||||
#define S3C2412_IISCON_RXDMA_PAUSE (1 << 5)
|
||||
#define S3C2412_IISCON_TXCH_PAUSE (1 << 4)
|
||||
#define S3C2412_IISCON_RXCH_PAUSE (1 << 3)
|
||||
#define S3C2412_IISCON_TXDMA_ACTIVE (1 << 2)
|
||||
#define S3C2412_IISCON_RXDMA_ACTIVE (1 << 1)
|
||||
#define S3C2412_IISCON_IIS_ACTIVE (1 << 0)
|
||||
|
||||
#define S5PC1XX_IISMOD_OPCLK_CDCLK_OUT (0 << 30)
|
||||
#define S5PC1XX_IISMOD_OPCLK_CDCLK_IN (1 << 30)
|
||||
#define S5PC1XX_IISMOD_OPCLK_BCLK_OUT (2 << 30)
|
||||
#define S5PC1XX_IISMOD_OPCLK_PCLK (3 << 30)
|
||||
#define S5PC1XX_IISMOD_OPCLK_MASK (3 << 30)
|
||||
#define S5PC1XX_IISMOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */
|
||||
#define S5PC1XX_IISMOD_BLCS_MASK 0x3
|
||||
#define S5PC1XX_IISMOD_BLCS_SHIFT 26
|
||||
#define S5PC1XX_IISMOD_BLCP_MASK 0x3
|
||||
#define S5PC1XX_IISMOD_BLCP_SHIFT 24
|
||||
|
||||
#define S3C64XX_IISMOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */
|
||||
#define S3C64XX_IISMOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */
|
||||
#define S3C64XX_IISMOD_C1DD_HHALF (1 << 19)
|
||||
#define S3C64XX_IISMOD_C1DD_LHALF (1 << 18)
|
||||
#define S3C64XX_IISMOD_DC2_EN (1 << 17)
|
||||
#define S3C64XX_IISMOD_DC1_EN (1 << 16)
|
||||
#define S3C64XX_IISMOD_BLC_16BIT (0 << 13)
|
||||
#define S3C64XX_IISMOD_BLC_8BIT (1 << 13)
|
||||
#define S3C64XX_IISMOD_BLC_24BIT (2 << 13)
|
||||
#define S3C64XX_IISMOD_BLC_MASK (3 << 13)
|
||||
|
||||
#define S3C2412_IISMOD_IMS_SYSMUX (1 << 10)
|
||||
#define S3C2412_IISMOD_SLAVE (1 << 11)
|
||||
#define S3C2412_IISMOD_MODE_TXONLY (0 << 8)
|
||||
#define S3C2412_IISMOD_MODE_RXONLY (1 << 8)
|
||||
#define S3C2412_IISMOD_MODE_TXRX (2 << 8)
|
||||
#define S3C2412_IISMOD_MODE_MASK (3 << 8)
|
||||
#define S3C2412_IISMOD_LR_LLOW (0 << 7)
|
||||
#define S3C2412_IISMOD_LR_RLOW (1 << 7)
|
||||
#define S3C2412_IISMOD_SDF_IIS (0 << 5)
|
||||
#define S3C2412_IISMOD_SDF_MSB (1 << 5)
|
||||
#define S3C2412_IISMOD_SDF_LSB (2 << 5)
|
||||
#define S3C2412_IISMOD_SDF_MASK (3 << 5)
|
||||
#define S3C2412_IISMOD_RCLK_256FS (0 << 3)
|
||||
#define S3C2412_IISMOD_RCLK_512FS (1 << 3)
|
||||
#define S3C2412_IISMOD_RCLK_384FS (2 << 3)
|
||||
#define S3C2412_IISMOD_RCLK_768FS (3 << 3)
|
||||
#define S3C2412_IISMOD_RCLK_MASK (3 << 3)
|
||||
#define S3C2412_IISMOD_BCLK_32FS (0 << 1)
|
||||
#define S3C2412_IISMOD_BCLK_48FS (1 << 1)
|
||||
#define S3C2412_IISMOD_BCLK_16FS (2 << 1)
|
||||
#define S3C2412_IISMOD_BCLK_24FS (3 << 1)
|
||||
#define S3C2412_IISMOD_BCLK_MASK (3 << 1)
|
||||
#define S3C2412_IISMOD_8BIT (1 << 0)
|
||||
|
||||
#define S3C64XX_IISMOD_CDCLKCON (1 << 12)
|
||||
|
||||
#define S3C2412_IISPSR_PSREN (1 << 15)
|
||||
|
||||
#define S3C64XX_IISFIC_TX2COUNT(x) (((x) >> 24) & 0xf)
|
||||
#define S3C64XX_IISFIC_TX1COUNT(x) (((x) >> 16) & 0xf)
|
||||
|
||||
#define S3C2412_IISFIC_TXFLUSH (1 << 15)
|
||||
#define S3C2412_IISFIC_RXFLUSH (1 << 7)
|
||||
#define S3C2412_IISFIC_TXCOUNT(x) (((x) >> 8) & 0xf)
|
||||
#define S3C2412_IISFIC_RXCOUNT(x) (((x) >> 0) & 0xf)
|
||||
|
||||
#define S5PC1XX_IISFICS_TXFLUSH (1 << 15)
|
||||
#define S5PC1XX_IISFICS_TXCOUNT(x) (((x) >> 8) & 0x7f)
|
||||
|
||||
#endif /* __ASM_ARCH_REGS_S3C2412_IIS_H */
|
@ -1,66 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
|
||||
* http://www.simtec.co.uk/products/SWLINUX/
|
||||
*
|
||||
* S3C2410 IIS register definition
|
||||
*/
|
||||
|
||||
#ifndef __SAMSUNG_REGS_IIS_H__
|
||||
#define __SAMSUNG_REGS_IIS_H__
|
||||
|
||||
#define S3C2410_IISCON (0x00)
|
||||
|
||||
#define S3C2410_IISCON_LRINDEX (1 << 8)
|
||||
#define S3C2410_IISCON_TXFIFORDY (1 << 7)
|
||||
#define S3C2410_IISCON_RXFIFORDY (1 << 6)
|
||||
#define S3C2410_IISCON_TXDMAEN (1 << 5)
|
||||
#define S3C2410_IISCON_RXDMAEN (1 << 4)
|
||||
#define S3C2410_IISCON_TXIDLE (1 << 3)
|
||||
#define S3C2410_IISCON_RXIDLE (1 << 2)
|
||||
#define S3C2410_IISCON_PSCEN (1 << 1)
|
||||
#define S3C2410_IISCON_IISEN (1 << 0)
|
||||
|
||||
#define S3C2410_IISMOD (0x04)
|
||||
|
||||
#define S3C2440_IISMOD_MPLL (1 << 9)
|
||||
#define S3C2410_IISMOD_SLAVE (1 << 8)
|
||||
#define S3C2410_IISMOD_NOXFER (0 << 6)
|
||||
#define S3C2410_IISMOD_RXMODE (1 << 6)
|
||||
#define S3C2410_IISMOD_TXMODE (2 << 6)
|
||||
#define S3C2410_IISMOD_TXRXMODE (3 << 6)
|
||||
#define S3C2410_IISMOD_LR_LLOW (0 << 5)
|
||||
#define S3C2410_IISMOD_LR_RLOW (1 << 5)
|
||||
#define S3C2410_IISMOD_IIS (0 << 4)
|
||||
#define S3C2410_IISMOD_MSB (1 << 4)
|
||||
#define S3C2410_IISMOD_8BIT (0 << 3)
|
||||
#define S3C2410_IISMOD_16BIT (1 << 3)
|
||||
#define S3C2410_IISMOD_BITMASK (1 << 3)
|
||||
#define S3C2410_IISMOD_256FS (0 << 2)
|
||||
#define S3C2410_IISMOD_384FS (1 << 2)
|
||||
#define S3C2410_IISMOD_16FS (0 << 0)
|
||||
#define S3C2410_IISMOD_32FS (1 << 0)
|
||||
#define S3C2410_IISMOD_48FS (2 << 0)
|
||||
#define S3C2410_IISMOD_FS_MASK (3 << 0)
|
||||
|
||||
#define S3C2410_IISPSR (0x08)
|
||||
|
||||
#define S3C2410_IISPSR_INTMASK (31 << 5)
|
||||
#define S3C2410_IISPSR_INTSHIFT (5)
|
||||
#define S3C2410_IISPSR_EXTMASK (31 << 0)
|
||||
#define S3C2410_IISPSR_EXTSHFIT (0)
|
||||
|
||||
#define S3C2410_IISFCON (0x0c)
|
||||
|
||||
#define S3C2410_IISFCON_TXDMA (1 << 15)
|
||||
#define S3C2410_IISFCON_RXDMA (1 << 14)
|
||||
#define S3C2410_IISFCON_TXENABLE (1 << 13)
|
||||
#define S3C2410_IISFCON_RXENABLE (1 << 12)
|
||||
#define S3C2410_IISFCON_TXMASK (0x3f << 6)
|
||||
#define S3C2410_IISFCON_TXSHIFT (6)
|
||||
#define S3C2410_IISFCON_RXMASK (0x3f)
|
||||
#define S3C2410_IISFCON_RXSHIFT (0)
|
||||
|
||||
#define S3C2410_IISFIFO (0x10)
|
||||
|
||||
#endif /* __SAMSUNG_REGS_IIS_H__ */
|
@ -1,245 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// rx1950.c - ALSA SoC Audio Layer
|
||||
//
|
||||
// Copyright (c) 2010 Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
//
|
||||
// Based on smdk2440.c and magician.c
|
||||
//
|
||||
// Authors: Graeme Gregory graeme.gregory@wolfsonmicro.com
|
||||
// Philipp Zabel <philipp.zabel@gmail.com>
|
||||
// Denis Grigoriev <dgreenday@gmail.com>
|
||||
// Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
|
||||
#include "regs-iis.h"
|
||||
#include "s3c24xx-i2s.h"
|
||||
|
||||
static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd);
|
||||
static int rx1950_startup(struct snd_pcm_substream *substream);
|
||||
static int rx1950_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params);
|
||||
static int rx1950_spk_power(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event);
|
||||
|
||||
static const unsigned int rates[] = {
|
||||
16000,
|
||||
44100,
|
||||
48000,
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hw_constraint_list hw_rates = {
|
||||
.count = ARRAY_SIZE(rates),
|
||||
.list = rates,
|
||||
};
|
||||
|
||||
static struct snd_soc_jack hp_jack;
|
||||
|
||||
static struct snd_soc_jack_pin hp_jack_pins[] = {
|
||||
{
|
||||
.pin = "Headphone Jack",
|
||||
.mask = SND_JACK_HEADPHONE,
|
||||
},
|
||||
{
|
||||
.pin = "Speaker",
|
||||
.mask = SND_JACK_HEADPHONE,
|
||||
.invert = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_jack_gpio hp_jack_gpios[] = {
|
||||
[0] = {
|
||||
.name = "hp-gpio",
|
||||
.report = SND_JACK_HEADPHONE,
|
||||
.invert = 1,
|
||||
.debounce_time = 200,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_soc_ops rx1950_ops = {
|
||||
.startup = rx1950_startup,
|
||||
.hw_params = rx1950_hw_params,
|
||||
};
|
||||
|
||||
/* s3c24xx digital audio interface glue - connects codec <--> CPU */
|
||||
SND_SOC_DAILINK_DEFS(uda1380,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("s3c24xx-iis")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("uda1380-codec.0-001a",
|
||||
"uda1380-hifi")),
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("s3c24xx-iis")));
|
||||
|
||||
static struct snd_soc_dai_link rx1950_uda1380_dai[] = {
|
||||
{
|
||||
.name = "uda1380",
|
||||
.stream_name = "UDA1380 Duplex",
|
||||
.init = rx1950_uda1380_init,
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.ops = &rx1950_ops,
|
||||
SND_SOC_DAILINK_REG(uda1380),
|
||||
},
|
||||
};
|
||||
|
||||
/* rx1950 machine dapm widgets */
|
||||
static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("Mic Jack", NULL),
|
||||
SND_SOC_DAPM_SPK("Speaker", rx1950_spk_power),
|
||||
};
|
||||
|
||||
/* rx1950 machine audio_map */
|
||||
static const struct snd_soc_dapm_route audio_map[] = {
|
||||
/* headphone connected to VOUTLHP, VOUTRHP */
|
||||
{"Headphone Jack", NULL, "VOUTLHP"},
|
||||
{"Headphone Jack", NULL, "VOUTRHP"},
|
||||
|
||||
/* ext speaker connected to VOUTL, VOUTR */
|
||||
{"Speaker", NULL, "VOUTL"},
|
||||
{"Speaker", NULL, "VOUTR"},
|
||||
|
||||
/* mic is connected to VINM */
|
||||
{"VINM", NULL, "Mic Jack"},
|
||||
};
|
||||
|
||||
static struct snd_soc_card rx1950_asoc = {
|
||||
.name = "rx1950",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = rx1950_uda1380_dai,
|
||||
.num_links = ARRAY_SIZE(rx1950_uda1380_dai),
|
||||
|
||||
.dapm_widgets = uda1380_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
|
||||
.dapm_routes = audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(audio_map),
|
||||
};
|
||||
|
||||
static int rx1950_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
return snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&hw_rates);
|
||||
}
|
||||
|
||||
static struct gpio_desc *gpiod_speaker_power;
|
||||
|
||||
static int rx1950_spk_power(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
if (SND_SOC_DAPM_EVENT_ON(event))
|
||||
gpiod_set_value(gpiod_speaker_power, 1);
|
||||
else
|
||||
gpiod_set_value(gpiod_speaker_power, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rx1950_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
|
||||
int div;
|
||||
int ret;
|
||||
unsigned int rate = params_rate(params);
|
||||
int clk_source, fs_mode;
|
||||
|
||||
switch (rate) {
|
||||
case 16000:
|
||||
case 48000:
|
||||
clk_source = S3C24XX_CLKSRC_PCLK;
|
||||
fs_mode = S3C2410_IISMOD_256FS;
|
||||
div = s3c24xx_i2s_get_clockrate() / (256 * rate);
|
||||
if (s3c24xx_i2s_get_clockrate() % (256 * rate) > (128 * rate))
|
||||
div++;
|
||||
break;
|
||||
case 44100:
|
||||
case 88200:
|
||||
clk_source = S3C24XX_CLKSRC_MPLL;
|
||||
fs_mode = S3C2410_IISMOD_384FS;
|
||||
div = 1;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s: rate %d is not supported\n",
|
||||
__func__, rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* select clock source */
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source, rate,
|
||||
SND_SOC_CLOCK_OUT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set MCLK division for sample rate */
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
|
||||
fs_mode);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set BCLK division for sample rate */
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
|
||||
S3C2410_IISMOD_32FS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set prescaler division for sample rate */
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
|
||||
S3C24XX_PRESCALE(div, div));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
|
||||
SND_JACK_HEADPHONE,
|
||||
&hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins));
|
||||
|
||||
snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
|
||||
hp_jack_gpios);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rx1950_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
/* configure some gpios */
|
||||
gpiod_speaker_power = devm_gpiod_get(dev, "speaker-power", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpiod_speaker_power)) {
|
||||
dev_err(dev, "cannot get gpio\n");
|
||||
return PTR_ERR(gpiod_speaker_power);
|
||||
}
|
||||
|
||||
hp_jack_gpios[0].gpiod_dev = dev;
|
||||
rx1950_asoc.dev = dev;
|
||||
|
||||
return devm_snd_soc_register_card(dev, &rx1950_asoc);
|
||||
}
|
||||
|
||||
static struct platform_driver rx1950_audio = {
|
||||
.driver = {
|
||||
.name = "rx1950-audio",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
.probe = rx1950_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(rx1950_audio);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Vasily Khoruzhick");
|
||||
MODULE_DESCRIPTION("ALSA SoC RX1950");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:rx1950-audio");
|
@ -1,670 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// ALSA Soc Audio Layer - I2S core for newer Samsung SoCs.
|
||||
//
|
||||
// Copyright (c) 2006 Wolfson Microelectronics PLC.
|
||||
// Graeme Gregory graeme.gregory@wolfsonmicro.com
|
||||
// linux@wolfsonmicro.com
|
||||
//
|
||||
// Copyright (c) 2008, 2007, 2004-2005 Simtec Electronics
|
||||
// http://armlinux.simtec.co.uk/
|
||||
// Ben Dooks <ben@simtec.co.uk>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "regs-i2s-v2.h"
|
||||
#include "s3c-i2s-v2.h"
|
||||
|
||||
#define S3C2412_I2S_DEBUG_CON 0
|
||||
|
||||
static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
return snd_soc_dai_get_drvdata(cpu_dai);
|
||||
}
|
||||
|
||||
#define bit_set(v, b) (((v) & (b)) ? 1 : 0)
|
||||
|
||||
#if S3C2412_I2S_DEBUG_CON
|
||||
static void dbg_showcon(const char *fn, u32 con)
|
||||
{
|
||||
printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn,
|
||||
bit_set(con, S3C2412_IISCON_LRINDEX),
|
||||
bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY),
|
||||
bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY),
|
||||
bit_set(con, S3C2412_IISCON_TXFIFO_FULL),
|
||||
bit_set(con, S3C2412_IISCON_RXFIFO_FULL));
|
||||
|
||||
printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n",
|
||||
fn,
|
||||
bit_set(con, S3C2412_IISCON_TXDMA_PAUSE),
|
||||
bit_set(con, S3C2412_IISCON_RXDMA_PAUSE),
|
||||
bit_set(con, S3C2412_IISCON_TXCH_PAUSE),
|
||||
bit_set(con, S3C2412_IISCON_RXCH_PAUSE));
|
||||
printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn,
|
||||
bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE),
|
||||
bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE),
|
||||
bit_set(con, S3C2412_IISCON_IIS_ACTIVE));
|
||||
}
|
||||
#else
|
||||
static inline void dbg_showcon(const char *fn, u32 con)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Turn on or off the transmission path. */
|
||||
static void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
|
||||
{
|
||||
void __iomem *regs = i2s->regs;
|
||||
u32 fic, con, mod;
|
||||
|
||||
pr_debug("%s(%d)\n", __func__, on);
|
||||
|
||||
fic = readl(regs + S3C2412_IISFIC);
|
||||
con = readl(regs + S3C2412_IISCON);
|
||||
mod = readl(regs + S3C2412_IISMOD);
|
||||
|
||||
pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
|
||||
|
||||
if (on) {
|
||||
con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
|
||||
con &= ~S3C2412_IISCON_TXDMA_PAUSE;
|
||||
con &= ~S3C2412_IISCON_TXCH_PAUSE;
|
||||
|
||||
switch (mod & S3C2412_IISMOD_MODE_MASK) {
|
||||
case S3C2412_IISMOD_MODE_TXONLY:
|
||||
case S3C2412_IISMOD_MODE_TXRX:
|
||||
/* do nothing, we are in the right mode */
|
||||
break;
|
||||
|
||||
case S3C2412_IISMOD_MODE_RXONLY:
|
||||
mod &= ~S3C2412_IISMOD_MODE_MASK;
|
||||
mod |= S3C2412_IISMOD_MODE_TXRX;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(i2s->dev, "TXEN: Invalid MODE %x in IISMOD\n",
|
||||
mod & S3C2412_IISMOD_MODE_MASK);
|
||||
break;
|
||||
}
|
||||
|
||||
writel(con, regs + S3C2412_IISCON);
|
||||
writel(mod, regs + S3C2412_IISMOD);
|
||||
} else {
|
||||
/* Note, we do not have any indication that the FIFO problems
|
||||
* tha the S3C2410/2440 had apply here, so we should be able
|
||||
* to disable the DMA and TX without resetting the FIFOS.
|
||||
*/
|
||||
|
||||
con |= S3C2412_IISCON_TXDMA_PAUSE;
|
||||
con |= S3C2412_IISCON_TXCH_PAUSE;
|
||||
con &= ~S3C2412_IISCON_TXDMA_ACTIVE;
|
||||
|
||||
switch (mod & S3C2412_IISMOD_MODE_MASK) {
|
||||
case S3C2412_IISMOD_MODE_TXRX:
|
||||
mod &= ~S3C2412_IISMOD_MODE_MASK;
|
||||
mod |= S3C2412_IISMOD_MODE_RXONLY;
|
||||
break;
|
||||
|
||||
case S3C2412_IISMOD_MODE_TXONLY:
|
||||
mod &= ~S3C2412_IISMOD_MODE_MASK;
|
||||
con &= ~S3C2412_IISCON_IIS_ACTIVE;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(i2s->dev, "TXDIS: Invalid MODE %x in IISMOD\n",
|
||||
mod & S3C2412_IISMOD_MODE_MASK);
|
||||
break;
|
||||
}
|
||||
|
||||
writel(mod, regs + S3C2412_IISMOD);
|
||||
writel(con, regs + S3C2412_IISCON);
|
||||
}
|
||||
|
||||
fic = readl(regs + S3C2412_IISFIC);
|
||||
dbg_showcon(__func__, con);
|
||||
pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
|
||||
}
|
||||
|
||||
static void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
|
||||
{
|
||||
void __iomem *regs = i2s->regs;
|
||||
u32 fic, con, mod;
|
||||
|
||||
pr_debug("%s(%d)\n", __func__, on);
|
||||
|
||||
fic = readl(regs + S3C2412_IISFIC);
|
||||
con = readl(regs + S3C2412_IISCON);
|
||||
mod = readl(regs + S3C2412_IISMOD);
|
||||
|
||||
pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
|
||||
|
||||
if (on) {
|
||||
con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
|
||||
con &= ~S3C2412_IISCON_RXDMA_PAUSE;
|
||||
con &= ~S3C2412_IISCON_RXCH_PAUSE;
|
||||
|
||||
switch (mod & S3C2412_IISMOD_MODE_MASK) {
|
||||
case S3C2412_IISMOD_MODE_TXRX:
|
||||
case S3C2412_IISMOD_MODE_RXONLY:
|
||||
/* do nothing, we are in the right mode */
|
||||
break;
|
||||
|
||||
case S3C2412_IISMOD_MODE_TXONLY:
|
||||
mod &= ~S3C2412_IISMOD_MODE_MASK;
|
||||
mod |= S3C2412_IISMOD_MODE_TXRX;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(i2s->dev, "RXEN: Invalid MODE %x in IISMOD\n",
|
||||
mod & S3C2412_IISMOD_MODE_MASK);
|
||||
}
|
||||
|
||||
writel(mod, regs + S3C2412_IISMOD);
|
||||
writel(con, regs + S3C2412_IISCON);
|
||||
} else {
|
||||
/* See txctrl notes on FIFOs. */
|
||||
|
||||
con &= ~S3C2412_IISCON_RXDMA_ACTIVE;
|
||||
con |= S3C2412_IISCON_RXDMA_PAUSE;
|
||||
con |= S3C2412_IISCON_RXCH_PAUSE;
|
||||
|
||||
switch (mod & S3C2412_IISMOD_MODE_MASK) {
|
||||
case S3C2412_IISMOD_MODE_RXONLY:
|
||||
con &= ~S3C2412_IISCON_IIS_ACTIVE;
|
||||
mod &= ~S3C2412_IISMOD_MODE_MASK;
|
||||
break;
|
||||
|
||||
case S3C2412_IISMOD_MODE_TXRX:
|
||||
mod &= ~S3C2412_IISMOD_MODE_MASK;
|
||||
mod |= S3C2412_IISMOD_MODE_TXONLY;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(i2s->dev, "RXDIS: Invalid MODE %x in IISMOD\n",
|
||||
mod & S3C2412_IISMOD_MODE_MASK);
|
||||
}
|
||||
|
||||
writel(con, regs + S3C2412_IISCON);
|
||||
writel(mod, regs + S3C2412_IISMOD);
|
||||
}
|
||||
|
||||
fic = readl(regs + S3C2412_IISFIC);
|
||||
pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
|
||||
}
|
||||
|
||||
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
|
||||
|
||||
/*
|
||||
* Wait for the LR signal to allow synchronisation to the L/R clock
|
||||
* from the codec. May only be needed for slave mode.
|
||||
*/
|
||||
static int s3c2412_snd_lrsync(struct s3c_i2sv2_info *i2s)
|
||||
{
|
||||
u32 iiscon;
|
||||
unsigned long loops = msecs_to_loops(5);
|
||||
|
||||
pr_debug("Entered %s\n", __func__);
|
||||
|
||||
while (--loops) {
|
||||
iiscon = readl(i2s->regs + S3C2412_IISCON);
|
||||
if (iiscon & S3C2412_IISCON_LRINDEX)
|
||||
break;
|
||||
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
if (!loops) {
|
||||
printk(KERN_ERR "%s: timeout\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set S3C2412 I2S DAI format
|
||||
*/
|
||||
static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
|
||||
u32 iismod;
|
||||
|
||||
pr_debug("Entered %s\n", __func__);
|
||||
|
||||
iismod = readl(i2s->regs + S3C2412_IISMOD);
|
||||
pr_debug("hw_params r: IISMOD: %x \n", iismod);
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
|
||||
case SND_SOC_DAIFMT_BC_FC:
|
||||
i2s->master = 0;
|
||||
iismod |= S3C2412_IISMOD_SLAVE;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_BP_FP:
|
||||
i2s->master = 1;
|
||||
iismod &= ~S3C2412_IISMOD_SLAVE;
|
||||
break;
|
||||
default:
|
||||
pr_err("unknown master/slave format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
iismod &= ~S3C2412_IISMOD_SDF_MASK;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
iismod |= S3C2412_IISMOD_LR_RLOW;
|
||||
iismod |= S3C2412_IISMOD_SDF_MSB;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
iismod |= S3C2412_IISMOD_LR_RLOW;
|
||||
iismod |= S3C2412_IISMOD_SDF_LSB;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
iismod &= ~S3C2412_IISMOD_LR_RLOW;
|
||||
iismod |= S3C2412_IISMOD_SDF_IIS;
|
||||
break;
|
||||
default:
|
||||
pr_err("Unknown data format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(iismod, i2s->regs + S3C2412_IISMOD);
|
||||
pr_debug("hw_params w: IISMOD: %x \n", iismod);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c_i2sv2_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct s3c_i2sv2_info *i2s = to_info(dai);
|
||||
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
u32 iismod;
|
||||
|
||||
pr_debug("Entered %s\n", __func__);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dma_data = i2s->dma_playback;
|
||||
else
|
||||
dma_data = i2s->dma_capture;
|
||||
|
||||
snd_soc_dai_set_dma_data(dai, substream, dma_data);
|
||||
|
||||
/* Working copies of register */
|
||||
iismod = readl(i2s->regs + S3C2412_IISMOD);
|
||||
pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);
|
||||
|
||||
iismod &= ~S3C64XX_IISMOD_BLC_MASK;
|
||||
/* Sample size */
|
||||
switch (params_width(params)) {
|
||||
case 8:
|
||||
iismod |= S3C64XX_IISMOD_BLC_8BIT;
|
||||
break;
|
||||
case 16:
|
||||
break;
|
||||
case 24:
|
||||
iismod |= S3C64XX_IISMOD_BLC_24BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
writel(iismod, i2s->regs + S3C2412_IISMOD);
|
||||
pr_debug("%s: w: IISMOD: %x\n", __func__, iismod);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c_i2sv2_set_sysclk(struct snd_soc_dai *cpu_dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
|
||||
u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
|
||||
|
||||
pr_debug("Entered %s\n", __func__);
|
||||
pr_debug("%s r: IISMOD: %x\n", __func__, iismod);
|
||||
|
||||
switch (clk_id) {
|
||||
case S3C_I2SV2_CLKSRC_PCLK:
|
||||
iismod &= ~S3C2412_IISMOD_IMS_SYSMUX;
|
||||
break;
|
||||
|
||||
case S3C_I2SV2_CLKSRC_AUDIOBUS:
|
||||
iismod |= S3C2412_IISMOD_IMS_SYSMUX;
|
||||
break;
|
||||
|
||||
case S3C_I2SV2_CLKSRC_CDCLK:
|
||||
/* Error if controller doesn't have the CDCLKCON bit */
|
||||
if (!(i2s->feature & S3C_FEATURE_CDCLKCON))
|
||||
return -EINVAL;
|
||||
|
||||
switch (dir) {
|
||||
case SND_SOC_CLOCK_IN:
|
||||
iismod |= S3C64XX_IISMOD_CDCLKCON;
|
||||
break;
|
||||
case SND_SOC_CLOCK_OUT:
|
||||
iismod &= ~S3C64XX_IISMOD_CDCLKCON;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(iismod, i2s->regs + S3C2412_IISMOD);
|
||||
pr_debug("%s w: IISMOD: %x\n", __func__, iismod);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct s3c_i2sv2_info *i2s = to_info(asoc_rtd_to_cpu(rtd, 0));
|
||||
int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
|
||||
unsigned long irqs;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("Entered %s\n", __func__);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
/* On start, ensure that the FIFOs are cleared and reset. */
|
||||
|
||||
writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH,
|
||||
i2s->regs + S3C2412_IISFIC);
|
||||
|
||||
/* clear again, just in case */
|
||||
writel(0x0, i2s->regs + S3C2412_IISFIC);
|
||||
|
||||
fallthrough;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if (!i2s->master) {
|
||||
ret = s3c2412_snd_lrsync(i2s);
|
||||
if (ret)
|
||||
goto exit_err;
|
||||
}
|
||||
|
||||
local_irq_save(irqs);
|
||||
|
||||
if (capture)
|
||||
s3c2412_snd_rxctrl(i2s, 1);
|
||||
else
|
||||
s3c2412_snd_txctrl(i2s, 1);
|
||||
|
||||
local_irq_restore(irqs);
|
||||
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
local_irq_save(irqs);
|
||||
|
||||
if (capture)
|
||||
s3c2412_snd_rxctrl(i2s, 0);
|
||||
else
|
||||
s3c2412_snd_txctrl(i2s, 0);
|
||||
|
||||
local_irq_restore(irqs);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
exit_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set S3C2412 Clock dividers
|
||||
*/
|
||||
static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
|
||||
int div_id, int div)
|
||||
{
|
||||
struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
|
||||
u32 reg;
|
||||
|
||||
pr_debug("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div);
|
||||
|
||||
switch (div_id) {
|
||||
case S3C_I2SV2_DIV_BCLK:
|
||||
switch (div) {
|
||||
case 16:
|
||||
div = S3C2412_IISMOD_BCLK_16FS;
|
||||
break;
|
||||
|
||||
case 32:
|
||||
div = S3C2412_IISMOD_BCLK_32FS;
|
||||
break;
|
||||
|
||||
case 24:
|
||||
div = S3C2412_IISMOD_BCLK_24FS;
|
||||
break;
|
||||
|
||||
case 48:
|
||||
div = S3C2412_IISMOD_BCLK_48FS;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = readl(i2s->regs + S3C2412_IISMOD);
|
||||
reg &= ~S3C2412_IISMOD_BCLK_MASK;
|
||||
writel(reg | div, i2s->regs + S3C2412_IISMOD);
|
||||
|
||||
pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
|
||||
break;
|
||||
|
||||
case S3C_I2SV2_DIV_RCLK:
|
||||
switch (div) {
|
||||
case 256:
|
||||
div = S3C2412_IISMOD_RCLK_256FS;
|
||||
break;
|
||||
|
||||
case 384:
|
||||
div = S3C2412_IISMOD_RCLK_384FS;
|
||||
break;
|
||||
|
||||
case 512:
|
||||
div = S3C2412_IISMOD_RCLK_512FS;
|
||||
break;
|
||||
|
||||
case 768:
|
||||
div = S3C2412_IISMOD_RCLK_768FS;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = readl(i2s->regs + S3C2412_IISMOD);
|
||||
reg &= ~S3C2412_IISMOD_RCLK_MASK;
|
||||
writel(reg | div, i2s->regs + S3C2412_IISMOD);
|
||||
pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
|
||||
break;
|
||||
|
||||
case S3C_I2SV2_DIV_PRESCALER:
|
||||
if (div >= 0) {
|
||||
writel((div << 8) | S3C2412_IISPSR_PSREN,
|
||||
i2s->regs + S3C2412_IISPSR);
|
||||
} else {
|
||||
writel(0x0, i2s->regs + S3C2412_IISPSR);
|
||||
}
|
||||
pr_debug("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR));
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t s3c2412_i2s_delay(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct s3c_i2sv2_info *i2s = to_info(dai);
|
||||
u32 reg = readl(i2s->regs + S3C2412_IISFIC);
|
||||
snd_pcm_sframes_t delay;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
delay = S3C2412_IISFIC_TXCOUNT(reg);
|
||||
else
|
||||
delay = S3C2412_IISFIC_RXCOUNT(reg);
|
||||
|
||||
return delay;
|
||||
}
|
||||
|
||||
struct clk *s3c_i2sv2_get_clock(struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
|
||||
u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
|
||||
|
||||
if (iismod & S3C2412_IISMOD_IMS_SYSMUX)
|
||||
return i2s->iis_cclk;
|
||||
else
|
||||
return i2s->iis_pclk;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s3c_i2sv2_get_clock);
|
||||
|
||||
/* default table of all avaialable root fs divisors */
|
||||
static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 };
|
||||
|
||||
int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
|
||||
unsigned int *fstab,
|
||||
unsigned int rate, struct clk *clk)
|
||||
{
|
||||
unsigned long clkrate = clk_get_rate(clk);
|
||||
unsigned int div;
|
||||
unsigned int fsclk;
|
||||
unsigned int actual;
|
||||
unsigned int fs;
|
||||
unsigned int fsdiv;
|
||||
signed int deviation = 0;
|
||||
unsigned int best_fs = 0;
|
||||
unsigned int best_div = 0;
|
||||
unsigned int best_rate = 0;
|
||||
unsigned int best_deviation = INT_MAX;
|
||||
|
||||
pr_debug("Input clock rate %ldHz\n", clkrate);
|
||||
|
||||
if (fstab == NULL)
|
||||
fstab = iis_fs_tab;
|
||||
|
||||
for (fs = 0; fs < ARRAY_SIZE(iis_fs_tab); fs++) {
|
||||
fsdiv = iis_fs_tab[fs];
|
||||
|
||||
fsclk = clkrate / fsdiv;
|
||||
div = fsclk / rate;
|
||||
|
||||
if ((fsclk % rate) > (rate / 2))
|
||||
div++;
|
||||
|
||||
if (div <= 1)
|
||||
continue;
|
||||
|
||||
actual = clkrate / (fsdiv * div);
|
||||
deviation = actual - rate;
|
||||
|
||||
printk(KERN_DEBUG "%ufs: div %u => result %u, deviation %d\n",
|
||||
fsdiv, div, actual, deviation);
|
||||
|
||||
deviation = abs(deviation);
|
||||
|
||||
if (deviation < best_deviation) {
|
||||
best_fs = fsdiv;
|
||||
best_div = div;
|
||||
best_rate = actual;
|
||||
best_deviation = deviation;
|
||||
}
|
||||
|
||||
if (deviation == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "best: fs=%u, div=%u, rate=%u\n",
|
||||
best_fs, best_div, best_rate);
|
||||
|
||||
info->fs_div = best_fs;
|
||||
info->clk_div = best_div;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s3c_i2sv2_iis_calc_rate);
|
||||
|
||||
int s3c_i2sv2_probe(struct snd_soc_dai *dai,
|
||||
struct s3c_i2sv2_info *i2s)
|
||||
{
|
||||
struct device *dev = dai->dev;
|
||||
unsigned int iismod;
|
||||
|
||||
i2s->dev = dev;
|
||||
|
||||
/* record our i2s structure for later use in the callbacks */
|
||||
snd_soc_dai_set_drvdata(dai, i2s);
|
||||
|
||||
i2s->iis_pclk = clk_get(dev, "iis");
|
||||
if (IS_ERR(i2s->iis_pclk)) {
|
||||
dev_err(dev, "failed to get iis_clock\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
clk_prepare_enable(i2s->iis_pclk);
|
||||
|
||||
/* Mark ourselves as in TXRX mode so we can run through our cleanup
|
||||
* process without warnings. */
|
||||
iismod = readl(i2s->regs + S3C2412_IISMOD);
|
||||
iismod |= S3C2412_IISMOD_MODE_TXRX;
|
||||
writel(iismod, i2s->regs + S3C2412_IISMOD);
|
||||
s3c2412_snd_txctrl(i2s, 0);
|
||||
s3c2412_snd_rxctrl(i2s, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s3c_i2sv2_probe);
|
||||
|
||||
void s3c_i2sv2_cleanup(struct snd_soc_dai *dai,
|
||||
struct s3c_i2sv2_info *i2s)
|
||||
{
|
||||
clk_disable_unprepare(i2s->iis_pclk);
|
||||
clk_put(i2s->iis_pclk);
|
||||
i2s->iis_pclk = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s3c_i2sv2_cleanup);
|
||||
|
||||
int s3c_i2sv2_register_component(struct device *dev, int id,
|
||||
const struct snd_soc_component_driver *cmp_drv,
|
||||
struct snd_soc_dai_driver *dai_drv)
|
||||
{
|
||||
struct snd_soc_dai_ops *ops = (struct snd_soc_dai_ops *)dai_drv->ops;
|
||||
|
||||
ops->trigger = s3c2412_i2s_trigger;
|
||||
if (!ops->hw_params)
|
||||
ops->hw_params = s3c_i2sv2_hw_params;
|
||||
ops->set_fmt = s3c2412_i2s_set_fmt;
|
||||
ops->set_clkdiv = s3c2412_i2s_set_clkdiv;
|
||||
ops->set_sysclk = s3c_i2sv2_set_sysclk;
|
||||
|
||||
/* Allow overriding by (for example) IISv4 */
|
||||
if (!ops->delay)
|
||||
ops->delay = s3c2412_i2s_delay;
|
||||
|
||||
return devm_snd_soc_register_component(dev, cmp_drv, dai_drv, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s3c_i2sv2_register_component);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
@ -1,108 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* ALSA Soc Audio Layer - S3C_I2SV2 I2S driver
|
||||
*
|
||||
* Copyright (c) 2007 Simtec Electronics
|
||||
* http://armlinux.simtec.co.uk/
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*/
|
||||
|
||||
/* This code is the core support for the I2S block found in a number of
|
||||
* Samsung SoC devices which is unofficially named I2S-V2. Currently the
|
||||
* S3C2412 and the S3C64XX series use this block to provide 1 or 2 I2S
|
||||
* channels via configurable GPIO.
|
||||
*/
|
||||
|
||||
#ifndef __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H
|
||||
#define __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H __FILE__
|
||||
|
||||
#define S3C_I2SV2_DIV_BCLK (1)
|
||||
#define S3C_I2SV2_DIV_RCLK (2)
|
||||
#define S3C_I2SV2_DIV_PRESCALER (3)
|
||||
|
||||
#define S3C_I2SV2_CLKSRC_PCLK 0
|
||||
#define S3C_I2SV2_CLKSRC_AUDIOBUS 1
|
||||
#define S3C_I2SV2_CLKSRC_CDCLK 2
|
||||
|
||||
/* Set this flag for I2S controllers that have the bit IISMOD[12]
|
||||
* bridge/break RCLK signal and external Xi2sCDCLK pin.
|
||||
*/
|
||||
#define S3C_FEATURE_CDCLKCON (1 << 0)
|
||||
|
||||
/**
|
||||
* struct s3c_i2sv2_info - S3C I2S-V2 information
|
||||
* @dev: The parent device passed to use from the probe.
|
||||
* @regs: The pointer to the device registe block.
|
||||
* @feature: Set of bit-flags indicating features of the controller.
|
||||
* @master: True if the I2S core is the I2S bit clock master.
|
||||
* @dma_playback: DMA information for playback channel.
|
||||
* @dma_capture: DMA information for capture channel.
|
||||
* @suspend_iismod: PM save for the IISMOD register.
|
||||
* @suspend_iiscon: PM save for the IISCON register.
|
||||
* @suspend_iispsr: PM save for the IISPSR register.
|
||||
*
|
||||
* This is the private codec state for the hardware associated with an
|
||||
* I2S channel such as the register mappings and clock sources.
|
||||
*/
|
||||
struct s3c_i2sv2_info {
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
|
||||
u32 feature;
|
||||
|
||||
struct clk *iis_pclk;
|
||||
struct clk *iis_cclk;
|
||||
|
||||
unsigned char master;
|
||||
|
||||
struct snd_dmaengine_dai_dma_data *dma_playback;
|
||||
struct snd_dmaengine_dai_dma_data *dma_capture;
|
||||
|
||||
u32 suspend_iismod;
|
||||
u32 suspend_iiscon;
|
||||
u32 suspend_iispsr;
|
||||
|
||||
unsigned long base;
|
||||
};
|
||||
|
||||
extern struct clk *s3c_i2sv2_get_clock(struct snd_soc_dai *cpu_dai);
|
||||
|
||||
struct s3c_i2sv2_rate_calc {
|
||||
unsigned int clk_div; /* for prescaler */
|
||||
unsigned int fs_div; /* for root frame clock */
|
||||
};
|
||||
|
||||
extern int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
|
||||
unsigned int *fstab,
|
||||
unsigned int rate, struct clk *clk);
|
||||
|
||||
/**
|
||||
* s3c_i2sv2_probe - probe for i2s device helper
|
||||
* @dai: The ASoC DAI structure supplied to the original probe.
|
||||
* @i2s: Our local i2s structure to fill in.
|
||||
* @base: The base address for the registers.
|
||||
*/
|
||||
extern int s3c_i2sv2_probe(struct snd_soc_dai *dai,
|
||||
struct s3c_i2sv2_info *i2s);
|
||||
|
||||
/**
|
||||
* s3c_i2sv2_cleanup - cleanup resources allocated in s3c_i2sv2_probe
|
||||
* @dai: The ASoC DAI structure supplied to the original probe.
|
||||
* @i2s: Our local i2s structure to fill in.
|
||||
*/
|
||||
extern void s3c_i2sv2_cleanup(struct snd_soc_dai *dai,
|
||||
struct s3c_i2sv2_info *i2s);
|
||||
/**
|
||||
* s3c_i2sv2_register_component - register component and dai with soc core
|
||||
* @dev: DAI device
|
||||
* @id: DAI ID
|
||||
* @drv: The driver structure to register
|
||||
*
|
||||
* Fill in any missing fields and then register the given dai with the
|
||||
* soc core.
|
||||
*/
|
||||
extern int s3c_i2sv2_register_component(struct device *dev, int id,
|
||||
const struct snd_soc_component_driver *cmp_drv,
|
||||
struct snd_soc_dai_driver *dai_drv);
|
||||
|
||||
#endif /* __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H */
|
@ -1,251 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// ALSA Soc Audio Layer - S3C2412 I2S driver
|
||||
//
|
||||
// Copyright (c) 2006 Wolfson Microelectronics PLC.
|
||||
// Graeme Gregory graeme.gregory@wolfsonmicro.com
|
||||
// linux@wolfsonmicro.com
|
||||
//
|
||||
// Copyright (c) 2007, 2004-2005 Simtec Electronics
|
||||
// http://armlinux.simtec.co.uk/
|
||||
// Ben Dooks <ben@simtec.co.uk>
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "dma.h"
|
||||
#include "regs-i2s-v2.h"
|
||||
#include "s3c2412-i2s.h"
|
||||
|
||||
#include <linux/platform_data/asoc-s3c.h>
|
||||
|
||||
static struct snd_dmaengine_dai_dma_data s3c2412_i2s_pcm_stereo_out = {
|
||||
.chan_name = "tx",
|
||||
.addr_width = 4,
|
||||
};
|
||||
|
||||
static struct snd_dmaengine_dai_dma_data s3c2412_i2s_pcm_stereo_in = {
|
||||
.chan_name = "rx",
|
||||
.addr_width = 4,
|
||||
};
|
||||
|
||||
static struct s3c_i2sv2_info s3c2412_i2s;
|
||||
|
||||
static int s3c2412_i2s_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_debug("Entered %s\n", __func__);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, &s3c2412_i2s_pcm_stereo_out,
|
||||
&s3c2412_i2s_pcm_stereo_in);
|
||||
|
||||
ret = s3c_i2sv2_probe(dai, &s3c2412_i2s);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
s3c2412_i2s.dma_capture = &s3c2412_i2s_pcm_stereo_in;
|
||||
s3c2412_i2s.dma_playback = &s3c2412_i2s_pcm_stereo_out;
|
||||
|
||||
s3c2412_i2s.iis_cclk = devm_clk_get(dai->dev, "i2sclk");
|
||||
if (IS_ERR(s3c2412_i2s.iis_cclk)) {
|
||||
pr_err("failed to get i2sclk clock\n");
|
||||
ret = PTR_ERR(s3c2412_i2s.iis_cclk);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Set MPLL as the source for IIS CLK */
|
||||
|
||||
clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll"));
|
||||
ret = clk_prepare_enable(s3c2412_i2s.iis_cclk);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
s3c_i2sv2_cleanup(dai, &s3c2412_i2s);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s3c2412_i2s_remove(struct snd_soc_dai *dai)
|
||||
{
|
||||
clk_disable_unprepare(s3c2412_i2s.iis_cclk);
|
||||
s3c_i2sv2_cleanup(dai, &s3c2412_i2s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct s3c_i2sv2_info *i2s = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
u32 iismod;
|
||||
|
||||
pr_debug("Entered %s\n", __func__);
|
||||
|
||||
iismod = readl(i2s->regs + S3C2412_IISMOD);
|
||||
pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);
|
||||
|
||||
switch (params_width(params)) {
|
||||
case 8:
|
||||
iismod |= S3C2412_IISMOD_8BIT;
|
||||
break;
|
||||
case 16:
|
||||
iismod &= ~S3C2412_IISMOD_8BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
writel(iismod, i2s->regs + S3C2412_IISMOD);
|
||||
pr_debug("%s: w: IISMOD: %x\n", __func__, iismod);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int s3c2412_i2s_suspend(struct snd_soc_component *component)
|
||||
{
|
||||
struct s3c_i2sv2_info *i2s = snd_soc_component_get_drvdata(component);
|
||||
u32 iismod;
|
||||
|
||||
if (component->active) {
|
||||
i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
|
||||
i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
|
||||
i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
|
||||
|
||||
/* some basic suspend checks */
|
||||
|
||||
iismod = readl(i2s->regs + S3C2412_IISMOD);
|
||||
|
||||
if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
|
||||
pr_warn("%s: RXDMA active?\n", __func__);
|
||||
|
||||
if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
|
||||
pr_warn("%s: TXDMA active?\n", __func__);
|
||||
|
||||
if (iismod & S3C2412_IISCON_IIS_ACTIVE)
|
||||
pr_warn("%s: IIS active\n", __func__);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c2412_i2s_resume(struct snd_soc_component *component)
|
||||
{
|
||||
struct s3c_i2sv2_info *i2s = snd_soc_component_get_drvdata(component);
|
||||
|
||||
pr_info("component_active %d, IISMOD %08x, IISCON %08x\n",
|
||||
component->active, i2s->suspend_iismod, i2s->suspend_iiscon);
|
||||
|
||||
if (component->active) {
|
||||
writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
|
||||
writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
|
||||
writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
|
||||
|
||||
writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
|
||||
i2s->regs + S3C2412_IISFIC);
|
||||
|
||||
ndelay(250);
|
||||
writel(0x0, i2s->regs + S3C2412_IISFIC);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define s3c2412_i2s_suspend NULL
|
||||
#define s3c2412_i2s_resume NULL
|
||||
#endif
|
||||
|
||||
#define S3C2412_I2S_RATES \
|
||||
(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
|
||||
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
|
||||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
|
||||
|
||||
static const struct snd_soc_dai_ops s3c2412_i2s_dai_ops = {
|
||||
.hw_params = s3c2412_i2s_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver s3c2412_i2s_dai = {
|
||||
.probe = s3c2412_i2s_probe,
|
||||
.remove = s3c2412_i2s_remove,
|
||||
.playback = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = S3C2412_I2S_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.capture = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = S3C2412_I2S_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
.ops = &s3c2412_i2s_dai_ops,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver s3c2412_i2s_component = {
|
||||
.name = "s3c2412-i2s",
|
||||
.suspend = s3c2412_i2s_suspend,
|
||||
.resume = s3c2412_i2s_resume,
|
||||
.legacy_dai_naming = 1,
|
||||
};
|
||||
|
||||
static int s3c2412_iis_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct resource *res;
|
||||
struct s3c_audio_pdata *pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "missing platform data");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
s3c2412_i2s.regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(s3c2412_i2s.regs))
|
||||
return PTR_ERR(s3c2412_i2s.regs);
|
||||
|
||||
s3c2412_i2s_pcm_stereo_out.addr = res->start + S3C2412_IISTXD;
|
||||
s3c2412_i2s_pcm_stereo_out.filter_data = pdata->dma_playback;
|
||||
s3c2412_i2s_pcm_stereo_in.addr = res->start + S3C2412_IISRXD;
|
||||
s3c2412_i2s_pcm_stereo_in.filter_data = pdata->dma_capture;
|
||||
|
||||
ret = samsung_asoc_dma_platform_register(&pdev->dev,
|
||||
pdata->dma_filter,
|
||||
"tx", "rx", NULL);
|
||||
if (ret) {
|
||||
pr_err("failed to register the DMA: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = s3c_i2sv2_register_component(&pdev->dev, -1,
|
||||
&s3c2412_i2s_component,
|
||||
&s3c2412_i2s_dai);
|
||||
if (ret)
|
||||
pr_err("failed to register the dai\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver s3c2412_iis_driver = {
|
||||
.probe = s3c2412_iis_dev_probe,
|
||||
.driver = {
|
||||
.name = "s3c2412-iis",
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(s3c2412_iis_driver);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
|
||||
MODULE_DESCRIPTION("S3C2412 I2S SoC Interface");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:s3c2412-iis");
|
@ -1,22 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* ALSA Soc Audio Layer - S3C2412 I2S driver
|
||||
*
|
||||
* Copyright (c) 2007 Simtec Electronics
|
||||
* http://armlinux.simtec.co.uk/
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*/
|
||||
|
||||
#ifndef __SND_SOC_S3C24XX_S3C2412_I2S_H
|
||||
#define __SND_SOC_S3C24XX_S3C2412_I2S_H __FILE__
|
||||
|
||||
#include "s3c-i2s-v2.h"
|
||||
|
||||
#define S3C2412_DIV_BCLK S3C_I2SV2_DIV_BCLK
|
||||
#define S3C2412_DIV_RCLK S3C_I2SV2_DIV_RCLK
|
||||
#define S3C2412_DIV_PRESCALER S3C_I2SV2_DIV_PRESCALER
|
||||
|
||||
#define S3C2412_CLKSRC_PCLK S3C_I2SV2_CLKSRC_PCLK
|
||||
#define S3C2412_CLKSRC_I2SCLK S3C_I2SV2_CLKSRC_AUDIOBUS
|
||||
|
||||
#endif /* __SND_SOC_S3C24XX_S3C2412_I2S_H */
|
@ -1,463 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// s3c24xx-i2s.c -- ALSA Soc Audio Layer
|
||||
//
|
||||
// (c) 2006 Wolfson Microelectronics PLC.
|
||||
// Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
|
||||
//
|
||||
// Copyright 2004-2005 Simtec Electronics
|
||||
// http://armlinux.simtec.co.uk/
|
||||
// Ben Dooks <ben@simtec.co.uk>
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "regs-iis.h"
|
||||
#include "dma.h"
|
||||
#include "s3c24xx-i2s.h"
|
||||
|
||||
static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_out = {
|
||||
.chan_name = "tx",
|
||||
.addr_width = 2,
|
||||
};
|
||||
|
||||
static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_in = {
|
||||
.chan_name = "rx",
|
||||
.addr_width = 2,
|
||||
};
|
||||
|
||||
struct s3c24xx_i2s_info {
|
||||
void __iomem *regs;
|
||||
struct clk *iis_clk;
|
||||
u32 iiscon;
|
||||
u32 iismod;
|
||||
u32 iisfcon;
|
||||
u32 iispsr;
|
||||
};
|
||||
static struct s3c24xx_i2s_info s3c24xx_i2s;
|
||||
|
||||
static void s3c24xx_snd_txctrl(int on)
|
||||
{
|
||||
u32 iisfcon;
|
||||
u32 iiscon;
|
||||
u32 iismod;
|
||||
|
||||
iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
|
||||
iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
|
||||
iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
|
||||
pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
|
||||
|
||||
if (on) {
|
||||
iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
|
||||
iiscon |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;
|
||||
iiscon &= ~S3C2410_IISCON_TXIDLE;
|
||||
iismod |= S3C2410_IISMOD_TXMODE;
|
||||
|
||||
writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
|
||||
writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
|
||||
} else {
|
||||
/* note, we have to disable the FIFOs otherwise bad things
|
||||
* seem to happen when the DMA stops. According to the
|
||||
* Samsung supplied kernel, this should allow the DMA
|
||||
* engine and FIFOs to reset. If this isn't allowed, the
|
||||
* DMA engine will simply freeze randomly.
|
||||
*/
|
||||
|
||||
iisfcon &= ~S3C2410_IISFCON_TXENABLE;
|
||||
iisfcon &= ~S3C2410_IISFCON_TXDMA;
|
||||
iiscon |= S3C2410_IISCON_TXIDLE;
|
||||
iiscon &= ~S3C2410_IISCON_TXDMAEN;
|
||||
iismod &= ~S3C2410_IISMOD_TXMODE;
|
||||
|
||||
writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
|
||||
writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
|
||||
writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
}
|
||||
|
||||
pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
|
||||
}
|
||||
|
||||
static void s3c24xx_snd_rxctrl(int on)
|
||||
{
|
||||
u32 iisfcon;
|
||||
u32 iiscon;
|
||||
u32 iismod;
|
||||
|
||||
iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
|
||||
iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
|
||||
iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
|
||||
pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
|
||||
|
||||
if (on) {
|
||||
iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
|
||||
iiscon |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN;
|
||||
iiscon &= ~S3C2410_IISCON_RXIDLE;
|
||||
iismod |= S3C2410_IISMOD_RXMODE;
|
||||
|
||||
writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
|
||||
writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
|
||||
} else {
|
||||
/* note, we have to disable the FIFOs otherwise bad things
|
||||
* seem to happen when the DMA stops. According to the
|
||||
* Samsung supplied kernel, this should allow the DMA
|
||||
* engine and FIFOs to reset. If this isn't allowed, the
|
||||
* DMA engine will simply freeze randomly.
|
||||
*/
|
||||
|
||||
iisfcon &= ~S3C2410_IISFCON_RXENABLE;
|
||||
iisfcon &= ~S3C2410_IISFCON_RXDMA;
|
||||
iiscon |= S3C2410_IISCON_RXIDLE;
|
||||
iiscon &= ~S3C2410_IISCON_RXDMAEN;
|
||||
iismod &= ~S3C2410_IISMOD_RXMODE;
|
||||
|
||||
writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
|
||||
writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
|
||||
writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
}
|
||||
|
||||
pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for the LR signal to allow synchronisation to the L/R clock
|
||||
* from the codec. May only be needed for slave mode.
|
||||
*/
|
||||
static int s3c24xx_snd_lrsync(void)
|
||||
{
|
||||
u32 iiscon;
|
||||
int timeout = 50; /* 5ms */
|
||||
|
||||
while (1) {
|
||||
iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
|
||||
if (iiscon & S3C2410_IISCON_LRINDEX)
|
||||
break;
|
||||
|
||||
if (!timeout--)
|
||||
return -ETIMEDOUT;
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether CPU is the master or slave
|
||||
*/
|
||||
static inline int s3c24xx_snd_is_clkmaster(void)
|
||||
{
|
||||
return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set S3C24xx I2S DAI format
|
||||
*/
|
||||
static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
u32 iismod;
|
||||
|
||||
iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
pr_debug("hw_params r: IISMOD: %x \n", iismod);
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
|
||||
case SND_SOC_DAIFMT_BC_FC:
|
||||
iismod |= S3C2410_IISMOD_SLAVE;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_BP_FP:
|
||||
iismod &= ~S3C2410_IISMOD_SLAVE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
iismod |= S3C2410_IISMOD_MSB;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
iismod &= ~S3C2410_IISMOD_MSB;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
pr_debug("hw_params w: IISMOD: %x \n", iismod);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
u32 iismod;
|
||||
|
||||
dma_data = snd_soc_dai_get_dma_data(dai, substream);
|
||||
|
||||
/* Working copies of register */
|
||||
iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
pr_debug("hw_params r: IISMOD: %x\n", iismod);
|
||||
|
||||
switch (params_width(params)) {
|
||||
case 8:
|
||||
iismod &= ~S3C2410_IISMOD_16BIT;
|
||||
dma_data->addr_width = 1;
|
||||
break;
|
||||
case 16:
|
||||
iismod |= S3C2410_IISMOD_16BIT;
|
||||
dma_data->addr_width = 2;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
pr_debug("hw_params w: IISMOD: %x\n", iismod);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if (!s3c24xx_snd_is_clkmaster()) {
|
||||
ret = s3c24xx_snd_lrsync();
|
||||
if (ret)
|
||||
goto exit_err;
|
||||
}
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
s3c24xx_snd_rxctrl(1);
|
||||
else
|
||||
s3c24xx_snd_txctrl(1);
|
||||
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
s3c24xx_snd_rxctrl(0);
|
||||
else
|
||||
s3c24xx_snd_txctrl(0);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
exit_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set S3C24xx Clock source
|
||||
*/
|
||||
static int s3c24xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
|
||||
iismod &= ~S3C2440_IISMOD_MPLL;
|
||||
|
||||
switch (clk_id) {
|
||||
case S3C24XX_CLKSRC_PCLK:
|
||||
break;
|
||||
case S3C24XX_CLKSRC_MPLL:
|
||||
iismod |= S3C2440_IISMOD_MPLL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set S3C24xx Clock dividers
|
||||
*/
|
||||
static int s3c24xx_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
|
||||
int div_id, int div)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
switch (div_id) {
|
||||
case S3C24XX_DIV_BCLK:
|
||||
reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
|
||||
writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
break;
|
||||
case S3C24XX_DIV_MCLK:
|
||||
reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
|
||||
writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
break;
|
||||
case S3C24XX_DIV_PRESCALER:
|
||||
writel(div, s3c24xx_i2s.regs + S3C2410_IISPSR);
|
||||
reg = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
|
||||
writel(reg | S3C2410_IISCON_PSCEN, s3c24xx_i2s.regs + S3C2410_IISCON);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* To avoid duplicating clock code, allow machine driver to
|
||||
* get the clockrate from here.
|
||||
*/
|
||||
u32 s3c24xx_i2s_get_clockrate(void)
|
||||
{
|
||||
return clk_get_rate(s3c24xx_i2s.iis_clk);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate);
|
||||
|
||||
static int s3c24xx_i2s_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
int ret;
|
||||
snd_soc_dai_init_dma_data(dai, &s3c24xx_i2s_pcm_stereo_out,
|
||||
&s3c24xx_i2s_pcm_stereo_in);
|
||||
|
||||
s3c24xx_i2s.iis_clk = devm_clk_get(dai->dev, "iis");
|
||||
if (IS_ERR(s3c24xx_i2s.iis_clk)) {
|
||||
pr_err("failed to get iis_clock\n");
|
||||
return PTR_ERR(s3c24xx_i2s.iis_clk);
|
||||
}
|
||||
ret = clk_prepare_enable(s3c24xx_i2s.iis_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON);
|
||||
|
||||
s3c24xx_snd_txctrl(0);
|
||||
s3c24xx_snd_rxctrl(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int s3c24xx_i2s_suspend(struct snd_soc_component *component)
|
||||
{
|
||||
s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
|
||||
s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
|
||||
s3c24xx_i2s.iispsr = readl(s3c24xx_i2s.regs + S3C2410_IISPSR);
|
||||
|
||||
clk_disable_unprepare(s3c24xx_i2s.iis_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c24xx_i2s_resume(struct snd_soc_component *component)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(s3c24xx_i2s.iis_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
|
||||
writel(s3c24xx_i2s.iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
writel(s3c24xx_i2s.iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
|
||||
writel(s3c24xx_i2s.iispsr, s3c24xx_i2s.regs + S3C2410_IISPSR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define s3c24xx_i2s_suspend NULL
|
||||
#define s3c24xx_i2s_resume NULL
|
||||
#endif
|
||||
|
||||
#define S3C24XX_I2S_RATES \
|
||||
(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
|
||||
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
|
||||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
|
||||
|
||||
static const struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = {
|
||||
.trigger = s3c24xx_i2s_trigger,
|
||||
.hw_params = s3c24xx_i2s_hw_params,
|
||||
.set_fmt = s3c24xx_i2s_set_fmt,
|
||||
.set_clkdiv = s3c24xx_i2s_set_clkdiv,
|
||||
.set_sysclk = s3c24xx_i2s_set_sysclk,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver s3c24xx_i2s_dai = {
|
||||
.probe = s3c24xx_i2s_probe,
|
||||
.playback = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = S3C24XX_I2S_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
|
||||
.capture = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = S3C24XX_I2S_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
|
||||
.ops = &s3c24xx_i2s_dai_ops,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver s3c24xx_i2s_component = {
|
||||
.name = "s3c24xx-i2s",
|
||||
.suspend = s3c24xx_i2s_suspend,
|
||||
.resume = s3c24xx_i2s_resume,
|
||||
.legacy_dai_naming = 1,
|
||||
};
|
||||
|
||||
static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
s3c24xx_i2s.regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(s3c24xx_i2s.regs))
|
||||
return PTR_ERR(s3c24xx_i2s.regs);
|
||||
|
||||
s3c24xx_i2s_pcm_stereo_out.addr = res->start + S3C2410_IISFIFO;
|
||||
s3c24xx_i2s_pcm_stereo_in.addr = res->start + S3C2410_IISFIFO;
|
||||
|
||||
ret = samsung_asoc_dma_platform_register(&pdev->dev, NULL,
|
||||
"tx", "rx", NULL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to register the DMA: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||
&s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "Failed to register the DAI\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver s3c24xx_iis_driver = {
|
||||
.probe = s3c24xx_iis_dev_probe,
|
||||
.driver = {
|
||||
.name = "s3c24xx-iis",
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(s3c24xx_iis_driver);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
|
||||
MODULE_DESCRIPTION("s3c24xx I2S SoC Interface");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:s3c24xx-iis");
|
@ -1,31 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* s3c24xx-i2s.c -- ALSA Soc Audio Layer
|
||||
*
|
||||
* Copyright 2005 Wolfson Microelectronics PLC.
|
||||
* Author: Graeme Gregory
|
||||
* graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
|
||||
*
|
||||
* Revision history
|
||||
* 10th Nov 2006 Initial version.
|
||||
*/
|
||||
|
||||
#ifndef S3C24XXI2S_H_
|
||||
#define S3C24XXI2S_H_
|
||||
|
||||
/* clock sources */
|
||||
#define S3C24XX_CLKSRC_PCLK 0
|
||||
#define S3C24XX_CLKSRC_MPLL 1
|
||||
|
||||
/* Clock dividers */
|
||||
#define S3C24XX_DIV_MCLK 0
|
||||
#define S3C24XX_DIV_BCLK 1
|
||||
#define S3C24XX_DIV_PRESCALER 2
|
||||
|
||||
/* prescaler */
|
||||
#define S3C24XX_PRESCALE(a,b) \
|
||||
(((a - 1) << S3C2410_IISPSR_INTSHIFT) | ((b - 1) << S3C2410_IISPSR_EXTSHFIT))
|
||||
|
||||
u32 s3c24xx_i2s_get_clockrate(void);
|
||||
|
||||
#endif /*S3C24XXI2S_H_*/
|
@ -1,372 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Copyright 2009 Simtec Electronics
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <linux/platform_data/asoc-s3c24xx_simtec.h>
|
||||
|
||||
#include "s3c24xx-i2s.h"
|
||||
#include "s3c24xx_simtec.h"
|
||||
|
||||
static struct s3c24xx_audio_simtec_pdata *pdata;
|
||||
static struct clk *xtal_clk;
|
||||
|
||||
static int spk_gain;
|
||||
static int spk_unmute;
|
||||
|
||||
/**
|
||||
* speaker_gain_get - read the speaker gain setting.
|
||||
* @kcontrol: The control for the speaker gain.
|
||||
* @ucontrol: The value that needs to be updated.
|
||||
*
|
||||
* Read the value for the AMP gain control.
|
||||
*/
|
||||
static int speaker_gain_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = spk_gain;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* speaker_gain_set - set the value of the speaker amp gain
|
||||
* @value: The value to write.
|
||||
*/
|
||||
static void speaker_gain_set(int value)
|
||||
{
|
||||
gpio_set_value_cansleep(pdata->amp_gain[0], value & 1);
|
||||
gpio_set_value_cansleep(pdata->amp_gain[1], value >> 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* speaker_gain_put - set the speaker gain setting.
|
||||
* @kcontrol: The control for the speaker gain.
|
||||
* @ucontrol: The value that needs to be set.
|
||||
*
|
||||
* Set the value of the speaker gain from the specified
|
||||
* @ucontrol setting.
|
||||
*
|
||||
* Note, if the speaker amp is muted, then we do not set a gain value
|
||||
* as at-least one of the ICs that is fitted will try and power up even
|
||||
* if the main control is set to off.
|
||||
*/
|
||||
static int speaker_gain_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int value = ucontrol->value.integer.value[0];
|
||||
|
||||
spk_gain = value;
|
||||
|
||||
if (!spk_unmute)
|
||||
speaker_gain_set(value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new amp_gain_controls[] = {
|
||||
SOC_SINGLE_EXT("Speaker Gain", 0, 0, 3, 0,
|
||||
speaker_gain_get, speaker_gain_put),
|
||||
};
|
||||
|
||||
/**
|
||||
* spk_unmute_state - set the unmute state of the speaker
|
||||
* @to: zero to unmute, non-zero to ununmute.
|
||||
*/
|
||||
static void spk_unmute_state(int to)
|
||||
{
|
||||
pr_debug("%s: to=%d\n", __func__, to);
|
||||
|
||||
spk_unmute = to;
|
||||
gpio_set_value(pdata->amp_gpio, to);
|
||||
|
||||
/* if we're umuting, also re-set the gain */
|
||||
if (to && pdata->amp_gain[0] > 0)
|
||||
speaker_gain_set(spk_gain);
|
||||
}
|
||||
|
||||
/**
|
||||
* speaker_unmute_get - read the speaker unmute setting.
|
||||
* @kcontrol: The control for the speaker gain.
|
||||
* @ucontrol: The value that needs to be updated.
|
||||
*
|
||||
* Read the value for the AMP gain control.
|
||||
*/
|
||||
static int speaker_unmute_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.integer.value[0] = spk_unmute;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* speaker_unmute_put - set the speaker unmute setting.
|
||||
* @kcontrol: The control for the speaker gain.
|
||||
* @ucontrol: The value that needs to be set.
|
||||
*
|
||||
* Set the value of the speaker gain from the specified
|
||||
* @ucontrol setting.
|
||||
*/
|
||||
static int speaker_unmute_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
spk_unmute_state(ucontrol->value.integer.value[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is added as a manual control as the speaker amps create clicks
|
||||
* when their power state is changed, which are far more noticeable than
|
||||
* anything produced by the CODEC itself.
|
||||
*/
|
||||
static const struct snd_kcontrol_new amp_unmute_controls[] = {
|
||||
SOC_SINGLE_EXT("Speaker Switch", 0, 0, 1, 0,
|
||||
speaker_unmute_get, speaker_unmute_put),
|
||||
};
|
||||
|
||||
void simtec_audio_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_card *card = rtd->card;
|
||||
|
||||
if (pdata->amp_gpio > 0) {
|
||||
pr_debug("%s: adding amp routes\n", __func__);
|
||||
|
||||
snd_soc_add_card_controls(card, amp_unmute_controls,
|
||||
ARRAY_SIZE(amp_unmute_controls));
|
||||
}
|
||||
|
||||
if (pdata->amp_gain[0] > 0) {
|
||||
pr_debug("%s: adding amp controls\n", __func__);
|
||||
snd_soc_add_card_controls(card, amp_gain_controls,
|
||||
ARRAY_SIZE(amp_gain_controls));
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(simtec_audio_init);
|
||||
|
||||
#define CODEC_CLOCK 12000000
|
||||
|
||||
/**
|
||||
* simtec_hw_params - update hardware parameters
|
||||
* @substream: The audio substream instance.
|
||||
* @params: The parameters requested.
|
||||
*
|
||||
* Update the codec data routing and configuration settings
|
||||
* from the supplied data.
|
||||
*/
|
||||
static int simtec_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
|
||||
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, 0,
|
||||
CODEC_CLOCK, SND_SOC_CLOCK_IN);
|
||||
if (ret) {
|
||||
pr_err( "%s: failed setting codec sysclk\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (pdata->use_mpllin) {
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_MPLL,
|
||||
0, SND_SOC_CLOCK_OUT);
|
||||
|
||||
if (ret) {
|
||||
pr_err("%s: failed to set MPLLin as clksrc\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata->output_cdclk) {
|
||||
int cdclk_scale;
|
||||
|
||||
cdclk_scale = clk_get_rate(xtal_clk) / CODEC_CLOCK;
|
||||
cdclk_scale--;
|
||||
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
|
||||
cdclk_scale);
|
||||
if (ret) {
|
||||
pr_err("%s: failed to set clock div\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int simtec_call_startup(struct s3c24xx_audio_simtec_pdata *pd)
|
||||
{
|
||||
/* call any board supplied startup code, this currently only
|
||||
* covers the bast/vr1000 which have a CPLD in the way of the
|
||||
* LRCLK */
|
||||
if (pd->startup)
|
||||
pd->startup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops simtec_snd_ops = {
|
||||
.hw_params = simtec_hw_params,
|
||||
};
|
||||
|
||||
/**
|
||||
* attach_gpio_amp - get and configure the necessary gpios
|
||||
* @dev: The device we're probing.
|
||||
* @pd: The platform data supplied by the board.
|
||||
*
|
||||
* If there is a GPIO based amplifier attached to the board, claim
|
||||
* the necessary GPIO lines for it, and set default values.
|
||||
*/
|
||||
static int attach_gpio_amp(struct device *dev,
|
||||
struct s3c24xx_audio_simtec_pdata *pd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* attach gpio amp gain (if any) */
|
||||
if (pdata->amp_gain[0] > 0) {
|
||||
ret = gpio_request(pd->amp_gain[0], "gpio-amp-gain0");
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot get amp gpio gain0\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpio_request(pd->amp_gain[1], "gpio-amp-gain1");
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot get amp gpio gain1\n");
|
||||
gpio_free(pdata->amp_gain[0]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpio_direction_output(pd->amp_gain[0], 0);
|
||||
gpio_direction_output(pd->amp_gain[1], 0);
|
||||
}
|
||||
|
||||
/* note, currently we assume GPA0 isn't valid amp */
|
||||
if (pdata->amp_gpio > 0) {
|
||||
ret = gpio_request(pd->amp_gpio, "gpio-amp");
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot get amp gpio %d (%d)\n",
|
||||
pd->amp_gpio, ret);
|
||||
goto err_amp;
|
||||
}
|
||||
|
||||
/* set the amp off at startup */
|
||||
spk_unmute_state(0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_amp:
|
||||
if (pd->amp_gain[0] > 0) {
|
||||
gpio_free(pd->amp_gain[0]);
|
||||
gpio_free(pd->amp_gain[1]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void detach_gpio_amp(struct s3c24xx_audio_simtec_pdata *pd)
|
||||
{
|
||||
if (pd->amp_gain[0] > 0) {
|
||||
gpio_free(pd->amp_gain[0]);
|
||||
gpio_free(pd->amp_gain[1]);
|
||||
}
|
||||
|
||||
if (pd->amp_gpio > 0)
|
||||
gpio_free(pd->amp_gpio);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int simtec_audio_resume(struct device *dev)
|
||||
{
|
||||
simtec_call_startup(pdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct dev_pm_ops simtec_audio_pmops = {
|
||||
.resume = simtec_audio_resume,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(simtec_audio_pmops);
|
||||
#endif
|
||||
|
||||
int simtec_audio_core_probe(struct platform_device *pdev,
|
||||
struct snd_soc_card *card)
|
||||
{
|
||||
struct platform_device *snd_dev;
|
||||
int ret;
|
||||
|
||||
card->dai_link->ops = &simtec_snd_ops;
|
||||
card->dai_link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBM_CFM;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "no platform data supplied\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
simtec_call_startup(pdata);
|
||||
|
||||
xtal_clk = clk_get(&pdev->dev, "xtal");
|
||||
if (IS_ERR(xtal_clk)) {
|
||||
dev_err(&pdev->dev, "could not get clkout0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "xtal rate is %ld\n", clk_get_rate(xtal_clk));
|
||||
|
||||
ret = attach_gpio_amp(&pdev->dev, pdata);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
snd_dev = platform_device_alloc("soc-audio", -1);
|
||||
if (!snd_dev) {
|
||||
dev_err(&pdev->dev, "failed to alloc soc-audio device\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_gpio;
|
||||
}
|
||||
|
||||
platform_set_drvdata(snd_dev, card);
|
||||
|
||||
ret = platform_device_add(snd_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add soc-audio dev\n");
|
||||
goto err_pdev;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, snd_dev);
|
||||
return 0;
|
||||
|
||||
err_pdev:
|
||||
platform_device_put(snd_dev);
|
||||
|
||||
err_gpio:
|
||||
detach_gpio_amp(pdata);
|
||||
|
||||
err_clk:
|
||||
clk_put(xtal_clk);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(simtec_audio_core_probe);
|
||||
|
||||
int simtec_audio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct platform_device *snd_dev = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_unregister(snd_dev);
|
||||
|
||||
detach_gpio_amp(pdata);
|
||||
clk_put(xtal_clk);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(simtec_audio_remove);
|
||||
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
||||
MODULE_DESCRIPTION("ALSA SoC Simtec Audio common support");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,18 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2009 Simtec Electronics
|
||||
*/
|
||||
|
||||
extern void simtec_audio_init(struct snd_soc_pcm_runtime *rtd);
|
||||
|
||||
extern int simtec_audio_core_probe(struct platform_device *pdev,
|
||||
struct snd_soc_card *card);
|
||||
|
||||
extern int simtec_audio_remove(struct platform_device *pdev);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
extern const struct dev_pm_ops simtec_audio_pmops;
|
||||
#define simtec_audio_pm &simtec_audio_pmops
|
||||
#else
|
||||
#define simtec_audio_pm NULL
|
||||
#endif
|
@ -1,112 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Copyright 2009 Simtec Electronics
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "s3c24xx_simtec.h"
|
||||
|
||||
static const struct snd_soc_dapm_widget dapm_widgets[] = {
|
||||
SND_SOC_DAPM_LINE("GSM Out", NULL),
|
||||
SND_SOC_DAPM_LINE("GSM In", NULL),
|
||||
SND_SOC_DAPM_LINE("Line In", NULL),
|
||||
SND_SOC_DAPM_LINE("Line Out", NULL),
|
||||
SND_SOC_DAPM_LINE("ZV", NULL),
|
||||
SND_SOC_DAPM_MIC("Mic Jack", NULL),
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route base_map[] = {
|
||||
/* Headphone connected to HP{L,R}OUT and HP{L,R}COM */
|
||||
|
||||
{ "Headphone Jack", NULL, "HPLOUT" },
|
||||
{ "Headphone Jack", NULL, "HPLCOM" },
|
||||
{ "Headphone Jack", NULL, "HPROUT" },
|
||||
{ "Headphone Jack", NULL, "HPRCOM" },
|
||||
|
||||
/* ZV connected to Line1 */
|
||||
|
||||
{ "LINE1L", NULL, "ZV" },
|
||||
{ "LINE1R", NULL, "ZV" },
|
||||
|
||||
/* Line In connected to Line2 */
|
||||
|
||||
{ "LINE2L", NULL, "Line In" },
|
||||
{ "LINE2R", NULL, "Line In" },
|
||||
|
||||
/* Microphone connected to MIC3R and MIC_BIAS */
|
||||
|
||||
{ "MIC3L", NULL, "Mic Jack" },
|
||||
|
||||
/* GSM connected to MONO_LOUT and MIC3L (in) */
|
||||
|
||||
{ "GSM Out", NULL, "MONO_LOUT" },
|
||||
{ "MIC3L", NULL, "GSM In" },
|
||||
|
||||
/* Speaker is connected to LINEOUT{LN,LP,RN,RP}, however we are
|
||||
* not using the DAPM to power it up and down as there it makes
|
||||
* a click when powering up. */
|
||||
};
|
||||
|
||||
/**
|
||||
* simtec_hermes_init - initialise and add controls
|
||||
* @codec; The codec instance to attach to.
|
||||
*
|
||||
* Attach our controls and configure the necessary codec
|
||||
* mappings for our sound card instance.
|
||||
*/
|
||||
static int simtec_hermes_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
simtec_audio_init(rtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SND_SOC_DAILINK_DEFS(tlv320aic33,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("s3c24xx-iis")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("tlv320aic3x-codec.0-001a",
|
||||
"tlv320aic3x-hifi")),
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("s3c24xx-iis")));
|
||||
|
||||
static struct snd_soc_dai_link simtec_dai_aic33 = {
|
||||
.name = "tlv320aic33",
|
||||
.stream_name = "TLV320AIC33",
|
||||
.init = simtec_hermes_init,
|
||||
SND_SOC_DAILINK_REG(tlv320aic33),
|
||||
};
|
||||
|
||||
/* simtec audio machine driver */
|
||||
static struct snd_soc_card snd_soc_machine_simtec_aic33 = {
|
||||
.name = "Simtec-Hermes",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = &simtec_dai_aic33,
|
||||
.num_links = 1,
|
||||
|
||||
.dapm_widgets = dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(dapm_widgets),
|
||||
.dapm_routes = base_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(base_map),
|
||||
};
|
||||
|
||||
static int simtec_audio_hermes_probe(struct platform_device *pd)
|
||||
{
|
||||
dev_info(&pd->dev, "probing....\n");
|
||||
return simtec_audio_core_probe(pd, &snd_soc_machine_simtec_aic33);
|
||||
}
|
||||
|
||||
static struct platform_driver simtec_audio_hermes_platdrv = {
|
||||
.driver = {
|
||||
.name = "s3c24xx-simtec-hermes-snd",
|
||||
.pm = simtec_audio_pm,
|
||||
},
|
||||
.probe = simtec_audio_hermes_probe,
|
||||
.remove = simtec_audio_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(simtec_audio_hermes_platdrv);
|
||||
|
||||
MODULE_ALIAS("platform:s3c24xx-simtec-hermes-snd");
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
||||
MODULE_DESCRIPTION("ALSA SoC Simtec Audio support");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,100 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Copyright 2009 Simtec Electronics
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "s3c24xx_simtec.h"
|
||||
|
||||
/* supported machines:
|
||||
*
|
||||
* Machine Connections AMP
|
||||
* ------- ----------- ---
|
||||
* BAST MIC, HPOUT, LOUT, LIN TPA2001D1 (HPOUTL,R) (gain hardwired)
|
||||
* VR1000 HPOUT, LIN None
|
||||
* VR2000 LIN, LOUT, MIC, HP LM4871 (HPOUTL,R)
|
||||
* DePicture LIN, LOUT, MIC, HP LM4871 (HPOUTL,R)
|
||||
* Anubis LIN, LOUT, MIC, HP TPA2001D1 (HPOUTL,R)
|
||||
*/
|
||||
|
||||
static const struct snd_soc_dapm_widget dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_LINE("Line In", NULL),
|
||||
SND_SOC_DAPM_LINE("Line Out", NULL),
|
||||
SND_SOC_DAPM_MIC("Mic Jack", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route base_map[] = {
|
||||
{ "Headphone Jack", NULL, "LHPOUT"},
|
||||
{ "Headphone Jack", NULL, "RHPOUT"},
|
||||
|
||||
{ "Line Out", NULL, "LOUT" },
|
||||
{ "Line Out", NULL, "ROUT" },
|
||||
|
||||
{ "LLINEIN", NULL, "Line In"},
|
||||
{ "RLINEIN", NULL, "Line In"},
|
||||
|
||||
{ "MICIN", NULL, "Mic Jack"},
|
||||
};
|
||||
|
||||
/**
|
||||
* simtec_tlv320aic23_init - initialise and add controls
|
||||
* @codec; The codec instance to attach to.
|
||||
*
|
||||
* Attach our controls and configure the necessary codec
|
||||
* mappings for our sound card instance.
|
||||
*/
|
||||
static int simtec_tlv320aic23_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
simtec_audio_init(rtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SND_SOC_DAILINK_DEFS(tlv320aic23,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("s3c24xx-iis")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("tlv320aic3x-codec.0-001a",
|
||||
"tlv320aic3x-hifi")),
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("s3c24xx-iis")));
|
||||
|
||||
static struct snd_soc_dai_link simtec_dai_aic23 = {
|
||||
.name = "tlv320aic23",
|
||||
.stream_name = "TLV320AIC23",
|
||||
.init = simtec_tlv320aic23_init,
|
||||
SND_SOC_DAILINK_REG(tlv320aic23),
|
||||
};
|
||||
|
||||
/* simtec audio machine driver */
|
||||
static struct snd_soc_card snd_soc_machine_simtec_aic23 = {
|
||||
.name = "Simtec",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = &simtec_dai_aic23,
|
||||
.num_links = 1,
|
||||
|
||||
.dapm_widgets = dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(dapm_widgets),
|
||||
.dapm_routes = base_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(base_map),
|
||||
};
|
||||
|
||||
static int simtec_audio_tlv320aic23_probe(struct platform_device *pd)
|
||||
{
|
||||
return simtec_audio_core_probe(pd, &snd_soc_machine_simtec_aic23);
|
||||
}
|
||||
|
||||
static struct platform_driver simtec_audio_tlv320aic23_driver = {
|
||||
.driver = {
|
||||
.name = "s3c24xx-simtec-tlv320aic23",
|
||||
.pm = simtec_audio_pm,
|
||||
},
|
||||
.probe = simtec_audio_tlv320aic23_probe,
|
||||
.remove = simtec_audio_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(simtec_audio_tlv320aic23_driver);
|
||||
|
||||
MODULE_ALIAS("platform:s3c24xx-simtec-tlv320aic23");
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
||||
MODULE_DESCRIPTION("ALSA SoC Simtec Audio support");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,257 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Modifications by Christian Pellegrin <chripell@evolware.org>
|
||||
//
|
||||
// s3c24xx_uda134x.c - S3C24XX_UDA134X ALSA SoC Audio board driver
|
||||
//
|
||||
// Copyright 2007 Dension Audio Systems Ltd.
|
||||
// Author: Zoltan Devai
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
#include <sound/s3c24xx_uda134x.h>
|
||||
|
||||
#include "regs-iis.h"
|
||||
#include "s3c24xx-i2s.h"
|
||||
|
||||
struct s3c24xx_uda134x {
|
||||
struct clk *xtal;
|
||||
struct clk *pclk;
|
||||
struct mutex clk_lock;
|
||||
int clk_users;
|
||||
};
|
||||
|
||||
/* #define ENFORCE_RATES 1 */
|
||||
/*
|
||||
Unfortunately the S3C24XX in master mode has a limited capacity of
|
||||
generating the clock for the codec. If you define this only rates
|
||||
that are really available will be enforced. But be careful, most
|
||||
user level application just want the usual sampling frequencies (8,
|
||||
11.025, 22.050, 44.1 kHz) and anyway resampling is a costly
|
||||
operation for embedded systems. So if you aren't very lucky or your
|
||||
hardware engineer wasn't very forward-looking it's better to leave
|
||||
this undefined. If you do so an approximate value for the requested
|
||||
sampling rate in the range -/+ 5% will be chosen. If this in not
|
||||
possible an error will be returned.
|
||||
*/
|
||||
|
||||
static unsigned int rates[33 * 2];
|
||||
#ifdef ENFORCE_RATES
|
||||
static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
|
||||
.count = ARRAY_SIZE(rates),
|
||||
.list = rates,
|
||||
.mask = 0,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct s3c24xx_uda134x *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&priv->clk_lock);
|
||||
|
||||
if (priv->clk_users == 0) {
|
||||
priv->xtal = clk_get(rtd->dev, "xtal");
|
||||
if (IS_ERR(priv->xtal)) {
|
||||
dev_err(rtd->dev, "%s cannot get xtal\n", __func__);
|
||||
ret = PTR_ERR(priv->xtal);
|
||||
} else {
|
||||
priv->pclk = clk_get(cpu_dai->dev, "iis");
|
||||
if (IS_ERR(priv->pclk)) {
|
||||
dev_err(rtd->dev, "%s cannot get pclk\n",
|
||||
__func__);
|
||||
clk_put(priv->xtal);
|
||||
ret = PTR_ERR(priv->pclk);
|
||||
}
|
||||
}
|
||||
if (!ret) {
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
int fs = i ? 256 : 384;
|
||||
|
||||
rates[i*33] = clk_get_rate(priv->xtal) / fs;
|
||||
for (j = 1; j < 33; j++)
|
||||
rates[i*33 + j] = clk_get_rate(priv->pclk) /
|
||||
(j * fs);
|
||||
}
|
||||
}
|
||||
}
|
||||
priv->clk_users += 1;
|
||||
mutex_unlock(&priv->clk_lock);
|
||||
|
||||
if (!ret) {
|
||||
#ifdef ENFORCE_RATES
|
||||
ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&hw_constraints_rates);
|
||||
if (ret < 0)
|
||||
dev_err(rtd->dev, "%s cannot set constraints\n",
|
||||
__func__);
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct s3c24xx_uda134x *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
|
||||
mutex_lock(&priv->clk_lock);
|
||||
priv->clk_users -= 1;
|
||||
if (priv->clk_users == 0) {
|
||||
clk_put(priv->xtal);
|
||||
priv->xtal = NULL;
|
||||
clk_put(priv->pclk);
|
||||
priv->pclk = NULL;
|
||||
}
|
||||
mutex_unlock(&priv->clk_lock);
|
||||
}
|
||||
|
||||
static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
|
||||
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
|
||||
unsigned int clk = 0;
|
||||
int ret = 0;
|
||||
int clk_source, fs_mode;
|
||||
unsigned long rate = params_rate(params);
|
||||
long err, cerr;
|
||||
unsigned int div;
|
||||
int i, bi;
|
||||
|
||||
err = 999999;
|
||||
bi = 0;
|
||||
for (i = 0; i < 2*33; i++) {
|
||||
cerr = rates[i] - rate;
|
||||
if (cerr < 0)
|
||||
cerr = -cerr;
|
||||
if (cerr < err) {
|
||||
err = cerr;
|
||||
bi = i;
|
||||
}
|
||||
}
|
||||
if (bi / 33 == 1)
|
||||
fs_mode = S3C2410_IISMOD_256FS;
|
||||
else
|
||||
fs_mode = S3C2410_IISMOD_384FS;
|
||||
if (bi % 33 == 0) {
|
||||
clk_source = S3C24XX_CLKSRC_MPLL;
|
||||
div = 1;
|
||||
} else {
|
||||
clk_source = S3C24XX_CLKSRC_PCLK;
|
||||
div = bi % 33;
|
||||
}
|
||||
|
||||
dev_dbg(rtd->dev, "%s desired rate %lu, %d\n", __func__, rate, bi);
|
||||
|
||||
clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
|
||||
|
||||
dev_dbg(rtd->dev, "%s will use: %s %s %d sysclk %d err %ld\n", __func__,
|
||||
fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
|
||||
clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
|
||||
div, clk, err);
|
||||
|
||||
if ((err * 100 / rate) > 5) {
|
||||
dev_err(rtd->dev, "effective frequency too different "
|
||||
"from desired (%ld%%)\n", err * 100 / rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
|
||||
S3C2410_IISMOD_32FS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
|
||||
S3C24XX_PRESCALE(div, div));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set the codec system clock for DAC and ADC */
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
|
||||
SND_SOC_CLOCK_OUT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops s3c24xx_uda134x_ops = {
|
||||
.startup = s3c24xx_uda134x_startup,
|
||||
.shutdown = s3c24xx_uda134x_shutdown,
|
||||
.hw_params = s3c24xx_uda134x_hw_params,
|
||||
};
|
||||
|
||||
SND_SOC_DAILINK_DEFS(uda134x,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("s3c24xx-iis")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("uda134x-codec", "uda134x-hifi")),
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("s3c24xx-iis")));
|
||||
|
||||
static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
|
||||
.name = "UDA134X",
|
||||
.stream_name = "UDA134X",
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.ops = &s3c24xx_uda134x_ops,
|
||||
SND_SOC_DAILINK_REG(uda134x),
|
||||
};
|
||||
|
||||
static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
|
||||
.name = "S3C24XX_UDA134X",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = &s3c24xx_uda134x_dai_link,
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
static int s3c24xx_uda134x_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = &snd_soc_s3c24xx_uda134x;
|
||||
struct s3c24xx_uda134x *priv;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&priv->clk_lock);
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
snd_soc_card_set_drvdata(card, priv);
|
||||
|
||||
ret = devm_snd_soc_register_card(&pdev->dev, card);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "failed to register card: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver s3c24xx_uda134x_driver = {
|
||||
.probe = s3c24xx_uda134x_probe,
|
||||
.driver = {
|
||||
.name = "s3c24xx_uda134x",
|
||||
},
|
||||
};
|
||||
module_platform_driver(s3c24xx_uda134x_driver);
|
||||
|
||||
MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
|
||||
MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,224 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// Copyright 2010 Maurus Cuelenaere <mcuelenaere@gmail.com>
|
||||
//
|
||||
// Based on smdk6410_wm8987.c
|
||||
// Copyright 2007 Wolfson Microelectronics PLC. - linux@wolfsonmicro.com
|
||||
// Graeme Gregory - graeme.gregory@wolfsonmicro.com
|
||||
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
|
||||
#include "i2s.h"
|
||||
#include "../codecs/wm8750.h"
|
||||
|
||||
/*
|
||||
* WM8987 is register compatible with WM8750, so using that as base driver.
|
||||
*/
|
||||
|
||||
static struct snd_soc_card snd_soc_smartq;
|
||||
|
||||
static int smartq_hifi_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
|
||||
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
|
||||
unsigned int clk = 0;
|
||||
int ret;
|
||||
|
||||
switch (params_rate(params)) {
|
||||
case 8000:
|
||||
case 16000:
|
||||
case 32000:
|
||||
case 48000:
|
||||
case 96000:
|
||||
clk = 12288000;
|
||||
break;
|
||||
case 11025:
|
||||
case 22050:
|
||||
case 44100:
|
||||
case 88200:
|
||||
clk = 11289600;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Use PCLK for I2S signal generation */
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
|
||||
0, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Gate the RCLK output on PAD */
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
|
||||
0, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set the codec system clock for DAC and ADC */
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* SmartQ WM8987 HiFi DAI operations.
|
||||
*/
|
||||
static const struct snd_soc_ops smartq_hifi_ops = {
|
||||
.hw_params = smartq_hifi_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_jack smartq_jack;
|
||||
|
||||
static struct snd_soc_jack_pin smartq_jack_pins[] = {
|
||||
/* Disable speaker when headphone is plugged in */
|
||||
{
|
||||
.pin = "Internal Speaker",
|
||||
.mask = SND_JACK_HEADPHONE,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_jack_gpio smartq_jack_gpios[] = {
|
||||
{
|
||||
.gpio = -1,
|
||||
.name = "headphone detect",
|
||||
.report = SND_JACK_HEADPHONE,
|
||||
.debounce_time = 200,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new wm8987_smartq_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Internal Speaker"),
|
||||
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
|
||||
SOC_DAPM_PIN_SWITCH("Internal Mic"),
|
||||
};
|
||||
|
||||
static int smartq_speaker_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k,
|
||||
int event)
|
||||
{
|
||||
struct gpio_desc *gpio = snd_soc_card_get_drvdata(&snd_soc_smartq);
|
||||
|
||||
gpiod_set_value(gpio, SND_SOC_DAPM_EVENT_OFF(event));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget wm8987_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SPK("Internal Speaker", smartq_speaker_event),
|
||||
SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
SND_SOC_DAPM_MIC("Internal Mic", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route audio_map[] = {
|
||||
{"Headphone Jack", NULL, "LOUT2"},
|
||||
{"Headphone Jack", NULL, "ROUT2"},
|
||||
|
||||
{"Internal Speaker", NULL, "LOUT2"},
|
||||
{"Internal Speaker", NULL, "ROUT2"},
|
||||
|
||||
{"Mic Bias", NULL, "Internal Mic"},
|
||||
{"LINPUT2", NULL, "Mic Bias"},
|
||||
};
|
||||
|
||||
static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
|
||||
int err = 0;
|
||||
|
||||
/* set endpoints to not connected */
|
||||
snd_soc_dapm_nc_pin(dapm, "LINPUT1");
|
||||
snd_soc_dapm_nc_pin(dapm, "RINPUT1");
|
||||
snd_soc_dapm_nc_pin(dapm, "OUT3");
|
||||
snd_soc_dapm_nc_pin(dapm, "ROUT1");
|
||||
|
||||
/* Headphone jack detection */
|
||||
err = snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
|
||||
SND_JACK_HEADPHONE, &smartq_jack,
|
||||
smartq_jack_pins,
|
||||
ARRAY_SIZE(smartq_jack_pins));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = snd_soc_jack_add_gpios(&smartq_jack,
|
||||
ARRAY_SIZE(smartq_jack_gpios),
|
||||
smartq_jack_gpios);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SND_SOC_DAILINK_DEFS(wm8987,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("wm8750.0-0x1a", "wm8750-hifi")),
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
|
||||
|
||||
static struct snd_soc_dai_link smartq_dai[] = {
|
||||
{
|
||||
.name = "wm8987",
|
||||
.stream_name = "SmartQ Hi-Fi",
|
||||
.init = smartq_wm8987_init,
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
.ops = &smartq_hifi_ops,
|
||||
SND_SOC_DAILINK_REG(wm8987),
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card snd_soc_smartq = {
|
||||
.name = "SmartQ",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = smartq_dai,
|
||||
.num_links = ARRAY_SIZE(smartq_dai),
|
||||
|
||||
.dapm_widgets = wm8987_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(wm8987_dapm_widgets),
|
||||
.dapm_routes = audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(audio_map),
|
||||
.controls = wm8987_smartq_controls,
|
||||
.num_controls = ARRAY_SIZE(wm8987_smartq_controls),
|
||||
};
|
||||
|
||||
static int smartq_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_desc *gpio;
|
||||
int ret;
|
||||
|
||||
platform_set_drvdata(pdev, &snd_soc_smartq);
|
||||
|
||||
/* Initialise GPIOs used by amplifiers */
|
||||
gpio = devm_gpiod_get(&pdev->dev, "amplifiers shutdown",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(&pdev->dev, "Failed to register GPK12\n");
|
||||
ret = PTR_ERR(gpio);
|
||||
goto out;
|
||||
}
|
||||
snd_soc_card_set_drvdata(&snd_soc_smartq, gpio);
|
||||
|
||||
ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_smartq);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "Failed to register card\n");
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver smartq_driver = {
|
||||
.driver = {
|
||||
.name = "smartq-audio",
|
||||
},
|
||||
.probe = smartq_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(smartq_driver);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Maurus Cuelenaere <mcuelenaere@gmail.com>");
|
||||
MODULE_DESCRIPTION("ALSA SoC SmartQ WM8987");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,211 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// Copyright (c) 2009 Samsung Electronics Co. Ltd
|
||||
// Author: Jaswinder Singh <jassisinghbrar@gmail.com>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "../codecs/wm8580.h"
|
||||
#include "i2s.h"
|
||||
|
||||
/*
|
||||
* Default CFG switch settings to use this driver:
|
||||
*
|
||||
* SMDK6410: Set CFG1 1-3 Off, CFG2 1-4 On
|
||||
*/
|
||||
|
||||
/* SMDK has a 12MHZ crystal attached to WM8580 */
|
||||
#define SMDK_WM8580_FREQ 12000000
|
||||
|
||||
static int smdk_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
|
||||
unsigned int pll_out;
|
||||
int rfs, ret;
|
||||
|
||||
switch (params_width(params)) {
|
||||
case 8:
|
||||
case 16:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The Fvco for WM8580 PLLs must fall within [90,100]MHz.
|
||||
* This criterion can't be met if we request PLL output
|
||||
* as {8000x256, 64000x256, 11025x256}Hz.
|
||||
* As a wayout, we rather change rfs to a minimum value that
|
||||
* results in (params_rate(params) * rfs), and itself, acceptable
|
||||
* to both - the CODEC and the CPU.
|
||||
*/
|
||||
switch (params_rate(params)) {
|
||||
case 16000:
|
||||
case 22050:
|
||||
case 32000:
|
||||
case 44100:
|
||||
case 48000:
|
||||
case 88200:
|
||||
case 96000:
|
||||
rfs = 256;
|
||||
break;
|
||||
case 64000:
|
||||
rfs = 384;
|
||||
break;
|
||||
case 8000:
|
||||
case 11025:
|
||||
rfs = 512;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
pll_out = params_rate(params) * rfs;
|
||||
|
||||
/* Set WM8580 to drive MCLK from its PLLA */
|
||||
ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
|
||||
WM8580_CLKSRC_PLLA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0,
|
||||
SMDK_WM8580_FREQ, pll_out);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA,
|
||||
pll_out, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* SMDK WM8580 DAI operations.
|
||||
*/
|
||||
static const struct snd_soc_ops smdk_ops = {
|
||||
.hw_params = smdk_hw_params,
|
||||
};
|
||||
|
||||
/* SMDK Playback widgets */
|
||||
static const struct snd_soc_dapm_widget smdk_wm8580_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Front", NULL),
|
||||
SND_SOC_DAPM_HP("Center+Sub", NULL),
|
||||
SND_SOC_DAPM_HP("Rear", NULL),
|
||||
|
||||
SND_SOC_DAPM_MIC("MicIn", NULL),
|
||||
SND_SOC_DAPM_LINE("LineIn", NULL),
|
||||
};
|
||||
|
||||
/* SMDK-PAIFTX connections */
|
||||
static const struct snd_soc_dapm_route smdk_wm8580_audio_map[] = {
|
||||
/* MicIn feeds AINL */
|
||||
{"AINL", NULL, "MicIn"},
|
||||
|
||||
/* LineIn feeds AINL/R */
|
||||
{"AINL", NULL, "LineIn"},
|
||||
{"AINR", NULL, "LineIn"},
|
||||
|
||||
/* Front Left/Right are fed VOUT1L/R */
|
||||
{"Front", NULL, "VOUT1L"},
|
||||
{"Front", NULL, "VOUT1R"},
|
||||
|
||||
/* Center/Sub are fed VOUT2L/R */
|
||||
{"Center+Sub", NULL, "VOUT2L"},
|
||||
{"Center+Sub", NULL, "VOUT2R"},
|
||||
|
||||
/* Rear Left/Right are fed VOUT3L/R */
|
||||
{"Rear", NULL, "VOUT3L"},
|
||||
{"Rear", NULL, "VOUT3R"},
|
||||
};
|
||||
|
||||
static int smdk_wm8580_init_paiftx(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
/* Enabling the microphone requires the fitting of a 0R
|
||||
* resistor to connect the line from the microphone jack.
|
||||
*/
|
||||
snd_soc_dapm_disable_pin(&rtd->card->dapm, "MicIn");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
PRI_PLAYBACK = 0,
|
||||
PRI_CAPTURE,
|
||||
};
|
||||
|
||||
#define SMDK_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \
|
||||
SND_SOC_DAIFMT_CBM_CFM)
|
||||
|
||||
SND_SOC_DAILINK_DEFS(paif_rx,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.2")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("wm8580.0-001b", "wm8580-hifi-playback")),
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
|
||||
|
||||
SND_SOC_DAILINK_DEFS(paif_tx,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.2")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC("wm8580.0-001b", "wm8580-hifi-capture")),
|
||||
DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
|
||||
|
||||
static struct snd_soc_dai_link smdk_dai[] = {
|
||||
[PRI_PLAYBACK] = { /* Primary Playback i/f */
|
||||
.name = "WM8580 PAIF RX",
|
||||
.stream_name = "Playback",
|
||||
.dai_fmt = SMDK_DAI_FMT,
|
||||
.ops = &smdk_ops,
|
||||
SND_SOC_DAILINK_REG(paif_rx),
|
||||
},
|
||||
[PRI_CAPTURE] = { /* Primary Capture i/f */
|
||||
.name = "WM8580 PAIF TX",
|
||||
.stream_name = "Capture",
|
||||
.dai_fmt = SMDK_DAI_FMT,
|
||||
.init = smdk_wm8580_init_paiftx,
|
||||
.ops = &smdk_ops,
|
||||
SND_SOC_DAILINK_REG(paif_tx),
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card smdk = {
|
||||
.name = "SMDK-I2S",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = smdk_dai,
|
||||
.num_links = ARRAY_SIZE(smdk_dai),
|
||||
|
||||
.dapm_widgets = smdk_wm8580_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(smdk_wm8580_dapm_widgets),
|
||||
.dapm_routes = smdk_wm8580_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(smdk_wm8580_audio_map),
|
||||
};
|
||||
|
||||
static struct platform_device *smdk_snd_device;
|
||||
|
||||
static int __init smdk_audio_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
smdk_snd_device = platform_device_alloc("soc-audio", -1);
|
||||
if (!smdk_snd_device)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(smdk_snd_device, &smdk);
|
||||
ret = platform_device_add(smdk_snd_device);
|
||||
|
||||
if (ret)
|
||||
platform_device_put(smdk_snd_device);
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(smdk_audio_init);
|
||||
|
||||
static void __exit smdk_audio_exit(void)
|
||||
{
|
||||
platform_device_unregister(smdk_snd_device);
|
||||
}
|
||||
module_exit(smdk_audio_exit);
|
||||
|
||||
MODULE_AUTHOR("Jaswinder Singh, jassisinghbrar@gmail.com");
|
||||
MODULE_DESCRIPTION("ALSA SoC SMDK WM8580");
|
||||
MODULE_LICENSE("GPL");
|
Loading…
Reference in New Issue
Block a user