Commit 503278c1 authored by Arnd Bergmann's avatar Arnd Bergmann

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: default avatarKrzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parent 2e3ee090
/* 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);
};
/* 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
......
// 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");
// 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");
// 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");
/* 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 */
/* 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__ */
// 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");
// 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");
/* 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 */
// 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");
/* 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 */
// 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");
/* 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_*/
// 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");
/* 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
// 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");
// 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");
// 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");
// 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");
// 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");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment