a077ff9034
This patch adds a codec driver for the dfbmcs320 bluetooth module, which is used on the neo1973 boards. The patch also modifies the neo1937_wm8753 sound board driver to use the new driver instead of registering the bluetooth DAI manually. Previously there was a name mismatch between the bluetooth DAI and the bluetooth DAI link and the sound card was not instantiated, with this patch the issue is no longer present and sound support works again. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Acked-by: Liam Girdwood <lrg@ti.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
540 lines
13 KiB
C
540 lines
13 KiB
C
/*
|
|
* 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
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/gpio.h>
|
|
|
|
#include <sound/soc.h>
|
|
|
|
#include <asm/mach-types.h>
|
|
#include <plat/regs-iis.h>
|
|
#include <mach/gta02.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 = substream->private_data;
|
|
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
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 codec DAI configuration */
|
|
ret = snd_soc_dai_set_fmt(codec_dai,
|
|
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
|
SND_SOC_DAIFMT_CBM_CFM);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* set cpu DAI configuration */
|
|
ret = snd_soc_dai_set_fmt(cpu_dai,
|
|
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
|
SND_SOC_DAIFMT_CBM_CFM);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* 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 = substream->private_data;
|
|
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
|
|
|
/* disable the PLL */
|
|
return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
|
|
}
|
|
|
|
/*
|
|
* Neo1973 WM8753 HiFi DAI opserations.
|
|
*/
|
|
static 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 = substream->private_data;
|
|
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
|
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 */
|
|
|
|
/* todo: gg check mode (DSP_B) against CSR datasheet */
|
|
/* set codec DAI configuration */
|
|
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
|
|
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* 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 = substream->private_data;
|
|
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
|
|
|
/* disable the PLL */
|
|
return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
|
|
}
|
|
|
|
static struct snd_soc_ops neo1973_voice_ops = {
|
|
.hw_params = neo1973_voice_hw_params,
|
|
.hw_free = neo1973_voice_hw_free,
|
|
};
|
|
|
|
/* Shared routes and controls */
|
|
|
|
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),
|
|
};
|
|
|
|
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"},
|
|
};
|
|
|
|
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"),
|
|
};
|
|
|
|
/* GTA02 specific routes and controlls */
|
|
|
|
#ifdef CONFIG_MACH_NEO1973_GTA02
|
|
|
|
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];
|
|
|
|
gpio_set_value(GTA02_GPIO_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)
|
|
{
|
|
gpio_set_value(GTA02_GPIO_AMP_SHUT, SND_SOC_DAPM_EVENT_OFF(event));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_soc_dapm_route neo1973_gta02_routes[] = {
|
|
/* 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_gta02_wm8753_controls[] = {
|
|
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 const struct snd_soc_dapm_widget neo1973_gta02_wm8753_dapm_widgets[] = {
|
|
SND_SOC_DAPM_SPK("Handset Spk", NULL),
|
|
SND_SOC_DAPM_SPK("Stereo Out", lm4853_event),
|
|
};
|
|
|
|
static int neo1973_gta02_wm8753_init(struct snd_soc_codec *codec)
|
|
{
|
|
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
|
int ret;
|
|
|
|
ret = snd_soc_dapm_new_controls(dapm, neo1973_gta02_wm8753_dapm_widgets,
|
|
ARRAY_SIZE(neo1973_gta02_wm8753_dapm_widgets));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = snd_soc_dapm_add_routes(dapm, neo1973_gta02_routes,
|
|
ARRAY_SIZE(neo1973_gta02_routes));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = snd_soc_add_controls(codec, neo1973_gta02_wm8753_controls,
|
|
ARRAY_SIZE(neo1973_gta02_wm8753_controls));
|
|
if (ret)
|
|
return ret;
|
|
|
|
snd_soc_dapm_disable_pin(dapm, "Stereo Out");
|
|
snd_soc_dapm_disable_pin(dapm, "Handset Spk");
|
|
snd_soc_dapm_ignore_suspend(dapm, "Stereo Out");
|
|
snd_soc_dapm_ignore_suspend(dapm, "Handset Spk");
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
static int neo1973_gta02_wm8753_init(struct snd_soc_code *codec) { return 0; }
|
|
#endif
|
|
|
|
static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd)
|
|
{
|
|
struct snd_soc_codec *codec = rtd->codec;
|
|
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
|
int ret;
|
|
|
|
/* set up NC codec pins */
|
|
if (machine_is_neo1973_gta01()) {
|
|
snd_soc_dapm_nc_pin(dapm, "LOUT2");
|
|
snd_soc_dapm_nc_pin(dapm, "ROUT2");
|
|
}
|
|
snd_soc_dapm_nc_pin(dapm, "OUT3");
|
|
snd_soc_dapm_nc_pin(dapm, "OUT4");
|
|
snd_soc_dapm_nc_pin(dapm, "LINE1");
|
|
snd_soc_dapm_nc_pin(dapm, "LINE2");
|
|
|
|
/* Add neo1973 specific widgets */
|
|
ret = snd_soc_dapm_new_controls(dapm, neo1973_wm8753_dapm_widgets,
|
|
ARRAY_SIZE(neo1973_wm8753_dapm_widgets));
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* add neo1973 specific controls */
|
|
ret = snd_soc_add_controls(codec, neo1973_wm8753_controls,
|
|
ARRAY_SIZE(neo1973_wm8753_controls));
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* set up neo1973 specific audio routes */
|
|
ret = snd_soc_dapm_add_routes(dapm, neo1973_wm8753_routes,
|
|
ARRAY_SIZE(neo1973_wm8753_routes));
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* set endpoints to default off mode */
|
|
snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
|
|
snd_soc_dapm_disable_pin(dapm, "GSM Line In");
|
|
snd_soc_dapm_disable_pin(dapm, "Headset Mic");
|
|
snd_soc_dapm_disable_pin(dapm, "Handset Mic");
|
|
|
|
/* allow audio paths from the GSM modem to run during suspend */
|
|
snd_soc_dapm_ignore_suspend(dapm, "GSM Line Out");
|
|
snd_soc_dapm_ignore_suspend(dapm, "GSM Line In");
|
|
snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
|
|
snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");
|
|
|
|
if (machine_is_neo1973_gta02()) {
|
|
ret = neo1973_gta02_wm8753_init(codec);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
snd_soc_dapm_sync(dapm);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* GTA01 specific controlls */
|
|
|
|
#ifdef CONFIG_MACH_NEO1973_GTA01
|
|
|
|
static const struct snd_soc_dapm_route neo1973_lm4857_routes[] = {
|
|
{"Amp IN", NULL, "ROUT1"},
|
|
{"Amp IN", NULL, "LOUT1"},
|
|
|
|
{"Handset Spk", NULL, "Amp EP"},
|
|
{"Stereo Out", NULL, "Amp LS"},
|
|
{"Headphone", NULL, "Amp HP"},
|
|
};
|
|
|
|
static const struct snd_soc_dapm_widget neo1973_lm4857_dapm_widgets[] = {
|
|
SND_SOC_DAPM_SPK("Handset Spk", NULL),
|
|
SND_SOC_DAPM_SPK("Stereo Out", NULL),
|
|
SND_SOC_DAPM_HP("Headphone", NULL),
|
|
};
|
|
|
|
static int neo1973_lm4857_init(struct snd_soc_dapm_context *dapm)
|
|
{
|
|
int ret;
|
|
|
|
ret = snd_soc_dapm_new_controls(dapm, neo1973_lm4857_dapm_widgets,
|
|
ARRAY_SIZE(neo1973_lm4857_dapm_widgets));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = snd_soc_dapm_add_routes(dapm, neo1973_lm4857_routes,
|
|
ARRAY_SIZE(neo1973_lm4857_routes));
|
|
if (ret)
|
|
return ret;
|
|
|
|
snd_soc_dapm_ignore_suspend(dapm, "Stereo Out");
|
|
snd_soc_dapm_ignore_suspend(dapm, "Handset Spk");
|
|
snd_soc_dapm_ignore_suspend(dapm, "Headphone");
|
|
|
|
snd_soc_dapm_sync(dapm);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
static int neo1973_lm4857_init(struct snd_soc_dapm_context *dapm) { return 0; };
|
|
#endif
|
|
|
|
static struct snd_soc_dai_link neo1973_dai[] = {
|
|
{ /* Hifi Playback - for similatious use with voice below */
|
|
.name = "WM8753",
|
|
.stream_name = "WM8753 HiFi",
|
|
.platform_name = "samsung-audio",
|
|
.cpu_dai_name = "s3c24xx-iis",
|
|
.codec_dai_name = "wm8753-hifi",
|
|
.codec_name = "wm8753-codec.0-001a",
|
|
.init = neo1973_wm8753_init,
|
|
.ops = &neo1973_hifi_ops,
|
|
},
|
|
{ /* Voice via BT */
|
|
.name = "Bluetooth",
|
|
.stream_name = "Voice",
|
|
.platform_name = "samsung-audio",
|
|
.cpu_dai_name = "dfbmcs320-pcm",
|
|
.codec_dai_name = "wm8753-voice",
|
|
.codec_name = "wm8753-codec.0-001a",
|
|
.ops = &neo1973_voice_ops,
|
|
},
|
|
};
|
|
|
|
static struct snd_soc_aux_dev neo1973_aux_devs[] = {
|
|
{
|
|
.name = "dfbmcs320",
|
|
.codec_name = "dfbmcs320.0",
|
|
},
|
|
{
|
|
.name = "lm4857",
|
|
.codec_name = "lm4857.0-007c",
|
|
.init = neo1973_lm4857_init,
|
|
},
|
|
};
|
|
|
|
static struct snd_soc_codec_conf neo1973_codec_conf[] = {
|
|
{
|
|
.dev_name = "lm4857.0-007c",
|
|
.name_prefix = "Amp",
|
|
},
|
|
};
|
|
|
|
#ifdef CONFIG_MACH_NEO1973_GTA02
|
|
static const struct gpio neo1973_gta02_gpios[] = {
|
|
{ GTA02_GPIO_HP_IN, GPIOF_OUT_INIT_HIGH, "GTA02_HP_IN" },
|
|
{ GTA02_GPIO_AMP_SHUT, GPIOF_OUT_INIT_HIGH, "GTA02_AMP_SHUT" },
|
|
};
|
|
#else
|
|
static const struct gpio neo1973_gta02_gpios[] = {};
|
|
#endif
|
|
|
|
static struct snd_soc_card neo1973 = {
|
|
.name = "neo1973",
|
|
.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),
|
|
};
|
|
|
|
static struct platform_device *neo1973_snd_device;
|
|
|
|
static int __init neo1973_init(void)
|
|
{
|
|
int ret;
|
|
|
|
if (!machine_is_neo1973_gta01() && !machine_is_neo1973_gta02())
|
|
return -ENODEV;
|
|
|
|
if (machine_is_neo1973_gta02()) {
|
|
neo1973.name = "neo1973gta02";
|
|
neo1973.num_aux_devs = 1;
|
|
|
|
ret = gpio_request_array(neo1973_gta02_gpios,
|
|
ARRAY_SIZE(neo1973_gta02_gpios));
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
neo1973_snd_device = platform_device_alloc("soc-audio", -1);
|
|
if (!neo1973_snd_device) {
|
|
ret = -ENOMEM;
|
|
goto err_gpio_free;
|
|
}
|
|
|
|
platform_set_drvdata(neo1973_snd_device, &neo1973);
|
|
ret = platform_device_add(neo1973_snd_device);
|
|
|
|
if (ret)
|
|
goto err_put_device;
|
|
|
|
return 0;
|
|
|
|
err_put_device:
|
|
platform_device_put(neo1973_snd_device);
|
|
err_gpio_free:
|
|
if (machine_is_neo1973_gta02()) {
|
|
gpio_free_array(neo1973_gta02_gpios,
|
|
ARRAY_SIZE(neo1973_gta02_gpios));
|
|
}
|
|
return ret;
|
|
}
|
|
module_init(neo1973_init);
|
|
|
|
static void __exit neo1973_exit(void)
|
|
{
|
|
platform_device_unregister(neo1973_snd_device);
|
|
|
|
if (machine_is_neo1973_gta02()) {
|
|
gpio_free_array(neo1973_gta02_gpios,
|
|
ARRAY_SIZE(neo1973_gta02_gpios));
|
|
}
|
|
}
|
|
module_exit(neo1973_exit);
|
|
|
|
/* Module information */
|
|
MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org, www.openmoko.org");
|
|
MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 and Frerunner");
|
|
MODULE_LICENSE("GPL");
|