Merge branch 'for-3.1' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6 into topic/asoc
This commit is contained in:
commit
f187700c2d
@ -534,6 +534,7 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
W: http://wiki.analog.com/
|
||||
S: Supported
|
||||
F: sound/soc/codecs/adau*
|
||||
F: sound/soc/codecs/adav*
|
||||
F: sound/soc/codecs/ad1*
|
||||
F: sound/soc/codecs/ssm*
|
||||
|
||||
|
@ -312,6 +312,10 @@ int snd_soc_default_readable_register(struct snd_soc_codec *codec,
|
||||
unsigned int reg);
|
||||
int snd_soc_default_writable_register(struct snd_soc_codec *codec,
|
||||
unsigned int reg);
|
||||
int snd_soc_platform_read(struct snd_soc_platform *platform,
|
||||
unsigned int reg);
|
||||
int snd_soc_platform_write(struct snd_soc_platform *platform,
|
||||
unsigned int reg, unsigned int val);
|
||||
|
||||
/* Utility functions to get clock rates from various things */
|
||||
int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
|
||||
@ -658,6 +662,10 @@ struct snd_soc_platform_driver {
|
||||
/* probe ordering - for components with runtime dependencies */
|
||||
int probe_order;
|
||||
int remove_order;
|
||||
|
||||
/* platform IO - used for platform DAPM */
|
||||
unsigned int (*read)(struct snd_soc_platform *, unsigned int);
|
||||
int (*write)(struct snd_soc_platform *, unsigned int, unsigned int);
|
||||
};
|
||||
|
||||
struct snd_soc_platform {
|
||||
|
@ -27,6 +27,19 @@ config SND_SOC_BFIN_EVAL_ADAU1701
|
||||
board connected to one of the Blackfin evaluation boards like the
|
||||
BF5XX-STAMP or BF5XX-EZKIT.
|
||||
|
||||
config SND_SOC_BFIN_EVAL_ADAV80X
|
||||
tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards"
|
||||
depends on SND_BF5XX_I2S && (SPI_MASTER || I2C)
|
||||
select SND_BF5XX_SOC_I2S
|
||||
select SND_SOC_ADAV80X
|
||||
help
|
||||
Say Y if you want to add support for the Analog Devices EVAL-ADAV801 or
|
||||
EVAL-ADAV803 board connected to one of the Blackfin evaluation boards
|
||||
like the BF5XX-STAMP or BF5XX-EZKIT.
|
||||
|
||||
Note: This driver assumes that the ADAV80X digital record and playback
|
||||
interfaces are connected to the first SPORT port on the BF5XX board.
|
||||
|
||||
config SND_BF5XX_SOC_AD73311
|
||||
tristate "SoC AD73311 Audio support for Blackfin"
|
||||
depends on SND_BF5XX_I2S
|
||||
|
@ -22,6 +22,7 @@ snd-ssm2602-objs := bf5xx-ssm2602.o
|
||||
snd-ad73311-objs := bf5xx-ad73311.o
|
||||
snd-ad193x-objs := bf5xx-ad193x.o
|
||||
snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o
|
||||
snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.o
|
||||
|
||||
obj-$(CONFIG_SND_BF5XX_SOC_AD1836) += snd-ad1836.o
|
||||
obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o
|
||||
@ -29,3 +30,4 @@ obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
|
||||
obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
|
||||
obj-$(CONFIG_SND_BF5XX_SOC_AD193X) += snd-ad193x.o
|
||||
obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) += snd-soc-bfin-eval-adau1701.o
|
||||
obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAV80X) += snd-soc-bfin-eval-adav80x.o
|
||||
|
@ -138,11 +138,20 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
pr_debug("%s enter\n", __func__);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
diff = sport_curr_offset_tx(sport);
|
||||
frames = bytes_to_frames(substream->runtime, diff);
|
||||
} else {
|
||||
diff = sport_curr_offset_rx(sport);
|
||||
frames = bytes_to_frames(substream->runtime, diff);
|
||||
}
|
||||
|
||||
/*
|
||||
* TX at least can report one frame beyond the end of the
|
||||
* buffer if we hit the wraparound case - clamp to within the
|
||||
* buffer as the ALSA APIs require.
|
||||
*/
|
||||
if (diff == snd_pcm_lib_buffer_bytes(substream))
|
||||
diff = 0;
|
||||
|
||||
frames = bytes_to_frames(substream->runtime, diff);
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
|
173
sound/soc/blackfin/bfin-eval-adav80x.c
Normal file
173
sound/soc/blackfin/bfin-eval-adav80x.c
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Machine driver for EVAL-ADAV801 and EVAL-ADAV803 on Analog Devices bfin
|
||||
* evaluation boards.
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "../codecs/adav80x.h"
|
||||
|
||||
static const struct snd_soc_dapm_widget bfin_eval_adav80x_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_LINE("Line Out", NULL),
|
||||
SND_SOC_DAPM_LINE("Line In", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route bfin_eval_adav80x_dapm_routes[] = {
|
||||
{ "Line Out", NULL, "VOUTL" },
|
||||
{ "Line Out", NULL, "VOUTR" },
|
||||
|
||||
{ "VINL", NULL, "Line In" },
|
||||
{ "VINR", NULL, "Line In" },
|
||||
};
|
||||
|
||||
static int bfin_eval_adav80x_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 *cpu_dai = rtd->cpu_dai;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dai_set_pll(codec_dai, ADAV80X_PLL1, ADAV80X_PLL_SRC_XTAL,
|
||||
27000000, params_rate(params) * 256);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_PLL1,
|
||||
params_rate(params) * 256, SND_SOC_CLOCK_IN);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bfin_eval_adav80x_codec_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
|
||||
snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK1, 0,
|
||||
SND_SOC_CLOCK_OUT);
|
||||
snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK2, 0,
|
||||
SND_SOC_CLOCK_OUT);
|
||||
snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK3, 0,
|
||||
SND_SOC_CLOCK_OUT);
|
||||
|
||||
snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_XTAL, 2700000, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops bfin_eval_adav80x_ops = {
|
||||
.hw_params = bfin_eval_adav80x_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link bfin_eval_adav80x_dais[] = {
|
||||
{
|
||||
.name = "adav80x",
|
||||
.stream_name = "ADAV80x HiFi",
|
||||
.cpu_dai_name = "bfin-i2s.0",
|
||||
.codec_dai_name = "adav80x-hifi",
|
||||
.platform_name = "bfin-i2s-pcm-audio",
|
||||
.init = bfin_eval_adav80x_codec_init,
|
||||
.ops = &bfin_eval_adav80x_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card bfin_eval_adav80x = {
|
||||
.name = "bfin-eval-adav80x",
|
||||
.dai_link = bfin_eval_adav80x_dais,
|
||||
.num_links = ARRAY_SIZE(bfin_eval_adav80x_dais),
|
||||
|
||||
.dapm_widgets = bfin_eval_adav80x_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(bfin_eval_adav80x_dapm_widgets),
|
||||
.dapm_routes = bfin_eval_adav80x_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(bfin_eval_adav80x_dapm_routes),
|
||||
};
|
||||
|
||||
enum bfin_eval_adav80x_type {
|
||||
BFIN_EVAL_ADAV801,
|
||||
BFIN_EVAL_ADAV803,
|
||||
};
|
||||
|
||||
static int bfin_eval_adav80x_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = &bfin_eval_adav80x;
|
||||
const char *codec_name;
|
||||
|
||||
switch (platform_get_device_id(pdev)->driver_data) {
|
||||
case BFIN_EVAL_ADAV801:
|
||||
codec_name = "spi0.1";
|
||||
break;
|
||||
case BFIN_EVAL_ADAV803:
|
||||
codec_name = "adav803.0-0034";
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bfin_eval_adav80x_dais[0].codec_name = codec_name;
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
|
||||
return snd_soc_register_card(&bfin_eval_adav80x);
|
||||
}
|
||||
|
||||
static int __devexit bfin_eval_adav80x_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
|
||||
snd_soc_unregister_card(card);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id bfin_eval_adav80x_ids[] = {
|
||||
{ "bfin-eval-adav801", BFIN_EVAL_ADAV801 },
|
||||
{ "bfin-eval-adav803", BFIN_EVAL_ADAV803 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, bfin_eval_adav80x_ids);
|
||||
|
||||
static struct platform_driver bfin_eval_adav80x_driver = {
|
||||
.driver = {
|
||||
.name = "bfin-eval-adav80x",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
.probe = bfin_eval_adav80x_probe,
|
||||
.remove = __devexit_p(bfin_eval_adav80x_remove),
|
||||
.id_table = bfin_eval_adav80x_ids,
|
||||
};
|
||||
|
||||
static int __init bfin_eval_adav80x_init(void)
|
||||
{
|
||||
return platform_driver_register(&bfin_eval_adav80x_driver);
|
||||
}
|
||||
module_init(bfin_eval_adav80x_init);
|
||||
|
||||
static void __exit bfin_eval_adav80x_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&bfin_eval_adav80x_driver);
|
||||
}
|
||||
module_exit(bfin_eval_adav80x_exit);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("ALSA SoC bfin adav80x driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -17,6 +17,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
|
||||
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
|
||||
select SND_SOC_AD73311
|
||||
select SND_SOC_ADAV80X
|
||||
select SND_SOC_ADS117X
|
||||
select SND_SOC_AK4104 if SPI_MASTER
|
||||
select SND_SOC_AK4535 if I2C
|
||||
@ -137,6 +138,9 @@ config SND_SOC_ADAU1701
|
||||
select SIGMA
|
||||
tristate
|
||||
|
||||
config SND_SOC_ADAV80X
|
||||
tristate
|
||||
|
||||
config SND_SOC_ADS117X
|
||||
tristate
|
||||
|
||||
|
@ -5,6 +5,7 @@ snd-soc-ad193x-objs := ad193x.o
|
||||
snd-soc-ad1980-objs := ad1980.o
|
||||
snd-soc-ad73311-objs := ad73311.o
|
||||
snd-soc-adau1701-objs := adau1701.o
|
||||
snd-soc-adav80x-objs := adav80x.o
|
||||
snd-soc-ads117x-objs := ads117x.o
|
||||
snd-soc-ak4104-objs := ak4104.o
|
||||
snd-soc-ak4535-objs := ak4535.o
|
||||
@ -99,6 +100,7 @@ obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o
|
||||
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
|
||||
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
|
||||
obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o
|
||||
obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o
|
||||
obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
|
||||
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
|
||||
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
|
||||
|
951
sound/soc/codecs/adav80x.c
Normal file
951
sound/soc/codecs/adav80x.c
Normal file
@ -0,0 +1,951 @@
|
||||
/*
|
||||
* ADAV80X Audio Codec driver supporting ADAV801, ADAV803
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
* Author: Yi Li <yi.li@analog.com>
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "adav80x.h"
|
||||
|
||||
#define ADAV80X_PLAYBACK_CTRL 0x04
|
||||
#define ADAV80X_AUX_IN_CTRL 0x05
|
||||
#define ADAV80X_REC_CTRL 0x06
|
||||
#define ADAV80X_AUX_OUT_CTRL 0x07
|
||||
#define ADAV80X_DPATH_CTRL1 0x62
|
||||
#define ADAV80X_DPATH_CTRL2 0x63
|
||||
#define ADAV80X_DAC_CTRL1 0x64
|
||||
#define ADAV80X_DAC_CTRL2 0x65
|
||||
#define ADAV80X_DAC_CTRL3 0x66
|
||||
#define ADAV80X_DAC_L_VOL 0x68
|
||||
#define ADAV80X_DAC_R_VOL 0x69
|
||||
#define ADAV80X_PGA_L_VOL 0x6c
|
||||
#define ADAV80X_PGA_R_VOL 0x6d
|
||||
#define ADAV80X_ADC_CTRL1 0x6e
|
||||
#define ADAV80X_ADC_CTRL2 0x6f
|
||||
#define ADAV80X_ADC_L_VOL 0x70
|
||||
#define ADAV80X_ADC_R_VOL 0x71
|
||||
#define ADAV80X_PLL_CTRL1 0x74
|
||||
#define ADAV80X_PLL_CTRL2 0x75
|
||||
#define ADAV80X_ICLK_CTRL1 0x76
|
||||
#define ADAV80X_ICLK_CTRL2 0x77
|
||||
#define ADAV80X_PLL_CLK_SRC 0x78
|
||||
#define ADAV80X_PLL_OUTE 0x7a
|
||||
|
||||
#define ADAV80X_PLL_CLK_SRC_PLL_XIN(pll) 0x00
|
||||
#define ADAV80X_PLL_CLK_SRC_PLL_MCLKI(pll) (0x40 << (pll))
|
||||
#define ADAV80X_PLL_CLK_SRC_PLL_MASK(pll) (0x40 << (pll))
|
||||
|
||||
#define ADAV80X_ICLK_CTRL1_DAC_SRC(src) ((src) << 5)
|
||||
#define ADAV80X_ICLK_CTRL1_ADC_SRC(src) ((src) << 2)
|
||||
#define ADAV80X_ICLK_CTRL1_ICLK2_SRC(src) (src)
|
||||
#define ADAV80X_ICLK_CTRL2_ICLK1_SRC(src) ((src) << 3)
|
||||
|
||||
#define ADAV80X_PLL_CTRL1_PLLDIV 0x10
|
||||
#define ADAV80X_PLL_CTRL1_PLLPD(pll) (0x04 << (pll))
|
||||
#define ADAV80X_PLL_CTRL1_XTLPD 0x02
|
||||
|
||||
#define ADAV80X_PLL_CTRL2_FIELD(pll, x) ((x) << ((pll) * 4))
|
||||
|
||||
#define ADAV80X_PLL_CTRL2_FS_48(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x00)
|
||||
#define ADAV80X_PLL_CTRL2_FS_32(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x08)
|
||||
#define ADAV80X_PLL_CTRL2_FS_44(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x0c)
|
||||
|
||||
#define ADAV80X_PLL_CTRL2_SEL(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x02)
|
||||
#define ADAV80X_PLL_CTRL2_DOUB(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x01)
|
||||
#define ADAV80X_PLL_CTRL2_PLL_MASK(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x0f)
|
||||
|
||||
#define ADAV80X_ADC_CTRL1_MODULATOR_MASK 0x80
|
||||
#define ADAV80X_ADC_CTRL1_MODULATOR_128FS 0x00
|
||||
#define ADAV80X_ADC_CTRL1_MODULATOR_64FS 0x80
|
||||
|
||||
#define ADAV80X_DAC_CTRL1_PD 0x80
|
||||
|
||||
#define ADAV80X_DAC_CTRL2_DIV1 0x00
|
||||
#define ADAV80X_DAC_CTRL2_DIV1_5 0x10
|
||||
#define ADAV80X_DAC_CTRL2_DIV2 0x20
|
||||
#define ADAV80X_DAC_CTRL2_DIV3 0x30
|
||||
#define ADAV80X_DAC_CTRL2_DIV_MASK 0x30
|
||||
|
||||
#define ADAV80X_DAC_CTRL2_INTERPOL_256FS 0x00
|
||||
#define ADAV80X_DAC_CTRL2_INTERPOL_128FS 0x40
|
||||
#define ADAV80X_DAC_CTRL2_INTERPOL_64FS 0x80
|
||||
#define ADAV80X_DAC_CTRL2_INTERPOL_MASK 0xc0
|
||||
|
||||
#define ADAV80X_DAC_CTRL2_DEEMPH_NONE 0x00
|
||||
#define ADAV80X_DAC_CTRL2_DEEMPH_44 0x01
|
||||
#define ADAV80X_DAC_CTRL2_DEEMPH_32 0x02
|
||||
#define ADAV80X_DAC_CTRL2_DEEMPH_48 0x03
|
||||
#define ADAV80X_DAC_CTRL2_DEEMPH_MASK 0x01
|
||||
|
||||
#define ADAV80X_CAPTURE_MODE_MASTER 0x20
|
||||
#define ADAV80X_CAPTURE_WORD_LEN24 0x00
|
||||
#define ADAV80X_CAPTURE_WORD_LEN20 0x04
|
||||
#define ADAV80X_CAPTRUE_WORD_LEN18 0x08
|
||||
#define ADAV80X_CAPTURE_WORD_LEN16 0x0c
|
||||
#define ADAV80X_CAPTURE_WORD_LEN_MASK 0x0c
|
||||
|
||||
#define ADAV80X_CAPTURE_MODE_LEFT_J 0x00
|
||||
#define ADAV80X_CAPTURE_MODE_I2S 0x01
|
||||
#define ADAV80X_CAPTURE_MODE_RIGHT_J 0x03
|
||||
#define ADAV80X_CAPTURE_MODE_MASK 0x03
|
||||
|
||||
#define ADAV80X_PLAYBACK_MODE_MASTER 0x10
|
||||
#define ADAV80X_PLAYBACK_MODE_LEFT_J 0x00
|
||||
#define ADAV80X_PLAYBACK_MODE_I2S 0x01
|
||||
#define ADAV80X_PLAYBACK_MODE_RIGHT_J_24 0x04
|
||||
#define ADAV80X_PLAYBACK_MODE_RIGHT_J_20 0x05
|
||||
#define ADAV80X_PLAYBACK_MODE_RIGHT_J_18 0x06
|
||||
#define ADAV80X_PLAYBACK_MODE_RIGHT_J_16 0x07
|
||||
#define ADAV80X_PLAYBACK_MODE_MASK 0x07
|
||||
|
||||
#define ADAV80X_PLL_OUTE_SYSCLKPD(x) BIT(2 - (x))
|
||||
|
||||
static u8 adav80x_default_regs[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x01, 0x80, 0x26, 0x00, 0x00,
|
||||
0x02, 0x40, 0x20, 0x00, 0x09, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0x92, 0xb1, 0x37,
|
||||
0x48, 0xd2, 0xfb, 0xca, 0xd2, 0x15, 0xe8, 0x29, 0xb9, 0x6a, 0xda, 0x2b,
|
||||
0xb7, 0xc0, 0x11, 0x65, 0x5c, 0xf6, 0xff, 0x8d, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00,
|
||||
0x00, 0xe8, 0x46, 0xe1, 0x5b, 0xd3, 0x43, 0x77, 0x93, 0xa7, 0x44, 0xee,
|
||||
0x32, 0x12, 0xc0, 0x11, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3f, 0x3f,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x52, 0x00,
|
||||
};
|
||||
|
||||
struct adav80x {
|
||||
enum snd_soc_control_type control_type;
|
||||
|
||||
enum adav80x_clk_src clk_src;
|
||||
unsigned int sysclk;
|
||||
enum adav80x_pll_src pll_src;
|
||||
|
||||
unsigned int dai_fmt[2];
|
||||
unsigned int rate;
|
||||
bool deemph;
|
||||
bool sysclk_pd[3];
|
||||
};
|
||||
|
||||
static const char *adav80x_mux_text[] = {
|
||||
"ADC",
|
||||
"Playback",
|
||||
"Aux Playback",
|
||||
};
|
||||
|
||||
static const unsigned int adav80x_mux_values[] = {
|
||||
0, 2, 3,
|
||||
};
|
||||
|
||||
#define ADAV80X_MUX_ENUM_DECL(name, reg, shift) \
|
||||
SOC_VALUE_ENUM_DOUBLE_DECL(name, reg, shift, 7, \
|
||||
ARRAY_SIZE(adav80x_mux_text), adav80x_mux_text, \
|
||||
adav80x_mux_values)
|
||||
|
||||
static ADAV80X_MUX_ENUM_DECL(adav80x_aux_capture_enum, ADAV80X_DPATH_CTRL1, 0);
|
||||
static ADAV80X_MUX_ENUM_DECL(adav80x_capture_enum, ADAV80X_DPATH_CTRL1, 3);
|
||||
static ADAV80X_MUX_ENUM_DECL(adav80x_dac_enum, ADAV80X_DPATH_CTRL2, 3);
|
||||
|
||||
static const struct snd_kcontrol_new adav80x_aux_capture_mux_ctrl =
|
||||
SOC_DAPM_VALUE_ENUM("Route", adav80x_aux_capture_enum);
|
||||
static const struct snd_kcontrol_new adav80x_capture_mux_ctrl =
|
||||
SOC_DAPM_VALUE_ENUM("Route", adav80x_capture_enum);
|
||||
static const struct snd_kcontrol_new adav80x_dac_mux_ctrl =
|
||||
SOC_DAPM_VALUE_ENUM("Route", adav80x_dac_enum);
|
||||
|
||||
#define ADAV80X_MUX(name, ctrl) \
|
||||
SND_SOC_DAPM_VALUE_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
|
||||
|
||||
static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_DAC("DAC", NULL, ADAV80X_DAC_CTRL1, 7, 1),
|
||||
SND_SOC_DAPM_ADC("ADC", NULL, ADAV80X_ADC_CTRL1, 5, 1),
|
||||
|
||||
SND_SOC_DAPM_PGA("Right PGA", ADAV80X_ADC_CTRL1, 0, 1, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("Left PGA", ADAV80X_ADC_CTRL1, 1, 1, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_AIF_OUT("AIFOUT", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_AIF_IN("AIFIN", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
|
||||
|
||||
SND_SOC_DAPM_AIF_OUT("AIFAUXOUT", "Aux Capture", 0, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_AIF_IN("AIFAUXIN", "Aux Playback", 0, SND_SOC_NOPM, 0, 0),
|
||||
|
||||
ADAV80X_MUX("Aux Capture Select", &adav80x_aux_capture_mux_ctrl),
|
||||
ADAV80X_MUX("Capture Select", &adav80x_capture_mux_ctrl),
|
||||
ADAV80X_MUX("DAC Select", &adav80x_dac_mux_ctrl),
|
||||
|
||||
SND_SOC_DAPM_INPUT("VINR"),
|
||||
SND_SOC_DAPM_INPUT("VINL"),
|
||||
SND_SOC_DAPM_OUTPUT("VOUTR"),
|
||||
SND_SOC_DAPM_OUTPUT("VOUTL"),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("SYSCLK", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("PLL1", ADAV80X_PLL_CTRL1, 2, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("PLL2", ADAV80X_PLL_CTRL1, 3, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("OSC", ADAV80X_PLL_CTRL1, 1, 1, NULL, 0),
|
||||
};
|
||||
|
||||
static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
|
||||
struct snd_soc_dapm_widget *sink)
|
||||
{
|
||||
struct snd_soc_codec *codec = source->codec;
|
||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||
const char *clk;
|
||||
|
||||
switch (adav80x->clk_src) {
|
||||
case ADAV80X_CLK_PLL1:
|
||||
clk = "PLL1";
|
||||
break;
|
||||
case ADAV80X_CLK_PLL2:
|
||||
clk = "PLL2";
|
||||
break;
|
||||
case ADAV80X_CLK_XTAL:
|
||||
clk = "OSC";
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strcmp(source->name, clk) == 0;
|
||||
}
|
||||
|
||||
static int adav80x_dapm_pll_check(struct snd_soc_dapm_widget *source,
|
||||
struct snd_soc_dapm_widget *sink)
|
||||
{
|
||||
struct snd_soc_codec *codec = source->codec;
|
||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
return adav80x->pll_src == ADAV80X_PLL_SRC_XTAL;
|
||||
}
|
||||
|
||||
|
||||
static const struct snd_soc_dapm_route adav80x_dapm_routes[] = {
|
||||
{ "DAC Select", "ADC", "ADC" },
|
||||
{ "DAC Select", "Playback", "AIFIN" },
|
||||
{ "DAC Select", "Aux Playback", "AIFAUXIN" },
|
||||
{ "DAC", NULL, "DAC Select" },
|
||||
|
||||
{ "Capture Select", "ADC", "ADC" },
|
||||
{ "Capture Select", "Playback", "AIFIN" },
|
||||
{ "Capture Select", "Aux Playback", "AIFAUXIN" },
|
||||
{ "AIFOUT", NULL, "Capture Select" },
|
||||
|
||||
{ "Aux Capture Select", "ADC", "ADC" },
|
||||
{ "Aux Capture Select", "Playback", "AIFIN" },
|
||||
{ "Aux Capture Select", "Aux Playback", "AIFAUXIN" },
|
||||
{ "AIFAUXOUT", NULL, "Aux Capture Select" },
|
||||
|
||||
{ "VOUTR", NULL, "DAC" },
|
||||
{ "VOUTL", NULL, "DAC" },
|
||||
|
||||
{ "Left PGA", NULL, "VINL" },
|
||||
{ "Right PGA", NULL, "VINR" },
|
||||
{ "ADC", NULL, "Left PGA" },
|
||||
{ "ADC", NULL, "Right PGA" },
|
||||
|
||||
{ "SYSCLK", NULL, "PLL1", adav80x_dapm_sysclk_check },
|
||||
{ "SYSCLK", NULL, "PLL2", adav80x_dapm_sysclk_check },
|
||||
{ "SYSCLK", NULL, "OSC", adav80x_dapm_sysclk_check },
|
||||
{ "PLL1", NULL, "OSC", adav80x_dapm_pll_check },
|
||||
{ "PLL2", NULL, "OSC", adav80x_dapm_pll_check },
|
||||
|
||||
{ "ADC", NULL, "SYSCLK" },
|
||||
{ "DAC", NULL, "SYSCLK" },
|
||||
{ "AIFOUT", NULL, "SYSCLK" },
|
||||
{ "AIFAUXOUT", NULL, "SYSCLK" },
|
||||
{ "AIFIN", NULL, "SYSCLK" },
|
||||
{ "AIFAUXIN", NULL, "SYSCLK" },
|
||||
};
|
||||
|
||||
static int adav80x_set_deemph(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int val;
|
||||
|
||||
if (adav80x->deemph) {
|
||||
switch (adav80x->rate) {
|
||||
case 32000:
|
||||
val = ADAV80X_DAC_CTRL2_DEEMPH_32;
|
||||
break;
|
||||
case 44100:
|
||||
val = ADAV80X_DAC_CTRL2_DEEMPH_44;
|
||||
break;
|
||||
case 48000:
|
||||
case 64000:
|
||||
case 88200:
|
||||
case 96000:
|
||||
val = ADAV80X_DAC_CTRL2_DEEMPH_48;
|
||||
break;
|
||||
default:
|
||||
val = ADAV80X_DAC_CTRL2_DEEMPH_NONE;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
val = ADAV80X_DAC_CTRL2_DEEMPH_NONE;
|
||||
}
|
||||
|
||||
return snd_soc_update_bits(codec, ADAV80X_DAC_CTRL2,
|
||||
ADAV80X_DAC_CTRL2_DEEMPH_MASK, val);
|
||||
}
|
||||
|
||||
static int adav80x_put_deemph(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int deemph = ucontrol->value.enumerated.item[0];
|
||||
|
||||
if (deemph > 1)
|
||||
return -EINVAL;
|
||||
|
||||
adav80x->deemph = deemph;
|
||||
|
||||
return adav80x_set_deemph(codec);
|
||||
}
|
||||
|
||||
static int adav80x_get_deemph(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
ucontrol->value.enumerated.item[0] = adav80x->deemph;
|
||||
return 0;
|
||||
};
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(adav80x_inpga_tlv, 0, 50, 0);
|
||||
static const DECLARE_TLV_DB_MINMAX(adav80x_digital_tlv, -9563, 0);
|
||||
|
||||
static const struct snd_kcontrol_new adav80x_controls[] = {
|
||||
SOC_DOUBLE_R_TLV("Master Playback Volume", ADAV80X_DAC_L_VOL,
|
||||
ADAV80X_DAC_R_VOL, 0, 0xff, 0, adav80x_digital_tlv),
|
||||
SOC_DOUBLE_R_TLV("Master Capture Volume", ADAV80X_ADC_L_VOL,
|
||||
ADAV80X_ADC_R_VOL, 0, 0xff, 0, adav80x_digital_tlv),
|
||||
|
||||
SOC_DOUBLE_R_TLV("PGA Capture Volume", ADAV80X_PGA_L_VOL,
|
||||
ADAV80X_PGA_R_VOL, 0, 0x30, 0, adav80x_inpga_tlv),
|
||||
|
||||
SOC_DOUBLE("Master Playback Switch", ADAV80X_DAC_CTRL1, 0, 1, 1, 0),
|
||||
SOC_DOUBLE("Master Capture Switch", ADAV80X_ADC_CTRL1, 2, 3, 1, 1),
|
||||
|
||||
SOC_SINGLE("ADC High Pass Filter Switch", ADAV80X_ADC_CTRL1, 6, 1, 0),
|
||||
|
||||
SOC_SINGLE_BOOL_EXT("Playback De-emphasis Switch", 0,
|
||||
adav80x_get_deemph, adav80x_put_deemph),
|
||||
};
|
||||
|
||||
static unsigned int adav80x_port_ctrl_regs[2][2] = {
|
||||
{ ADAV80X_REC_CTRL, ADAV80X_PLAYBACK_CTRL, },
|
||||
{ ADAV80X_AUX_OUT_CTRL, ADAV80X_AUX_IN_CTRL },
|
||||
};
|
||||
|
||||
static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int capture = 0x00;
|
||||
unsigned int playback = 0x00;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
capture |= ADAV80X_CAPTURE_MODE_MASTER;
|
||||
playback |= ADAV80X_PLAYBACK_MODE_MASTER;
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
capture |= ADAV80X_CAPTURE_MODE_I2S;
|
||||
playback |= ADAV80X_PLAYBACK_MODE_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
capture |= ADAV80X_CAPTURE_MODE_LEFT_J;
|
||||
playback |= ADAV80X_PLAYBACK_MODE_LEFT_J;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
capture |= ADAV80X_CAPTURE_MODE_RIGHT_J;
|
||||
playback |= ADAV80X_PLAYBACK_MODE_RIGHT_J_24;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, adav80x_port_ctrl_regs[dai->id][0],
|
||||
ADAV80X_CAPTURE_MODE_MASK | ADAV80X_CAPTURE_MODE_MASTER,
|
||||
capture);
|
||||
snd_soc_write(codec, adav80x_port_ctrl_regs[dai->id][1], playback);
|
||||
|
||||
adav80x->dai_fmt[dai->id] = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adav80x_set_adc_clock(struct snd_soc_codec *codec,
|
||||
unsigned int sample_rate)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
if (sample_rate <= 48000)
|
||||
val = ADAV80X_ADC_CTRL1_MODULATOR_128FS;
|
||||
else
|
||||
val = ADAV80X_ADC_CTRL1_MODULATOR_64FS;
|
||||
|
||||
snd_soc_update_bits(codec, ADAV80X_ADC_CTRL1,
|
||||
ADAV80X_ADC_CTRL1_MODULATOR_MASK, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adav80x_set_dac_clock(struct snd_soc_codec *codec,
|
||||
unsigned int sample_rate)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
if (sample_rate <= 48000)
|
||||
val = ADAV80X_DAC_CTRL2_DIV1 | ADAV80X_DAC_CTRL2_INTERPOL_256FS;
|
||||
else
|
||||
val = ADAV80X_DAC_CTRL2_DIV2 | ADAV80X_DAC_CTRL2_INTERPOL_128FS;
|
||||
|
||||
snd_soc_update_bits(codec, ADAV80X_DAC_CTRL2,
|
||||
ADAV80X_DAC_CTRL2_DIV_MASK | ADAV80X_DAC_CTRL2_INTERPOL_MASK,
|
||||
val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec,
|
||||
struct snd_soc_dai *dai, snd_pcm_format_t format)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
switch (format) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
val = ADAV80X_CAPTURE_WORD_LEN16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S18_3LE:
|
||||
val = ADAV80X_CAPTRUE_WORD_LEN18;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
val = ADAV80X_CAPTURE_WORD_LEN20;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
val = ADAV80X_CAPTURE_WORD_LEN24;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, adav80x_port_ctrl_regs[dai->id][0],
|
||||
ADAV80X_CAPTURE_WORD_LEN_MASK, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec,
|
||||
struct snd_soc_dai *dai, snd_pcm_format_t format)
|
||||
{
|
||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int val;
|
||||
|
||||
if (adav80x->dai_fmt[dai->id] != SND_SOC_DAIFMT_RIGHT_J)
|
||||
return 0;
|
||||
|
||||
switch (format) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_16;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S18_3LE:
|
||||
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_18;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_20;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_24;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, adav80x_port_ctrl_regs[dai->id][1],
|
||||
ADAV80X_PLAYBACK_MODE_MASK, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adav80x_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int rate = params_rate(params);
|
||||
|
||||
if (rate * 256 != adav80x->sysclk)
|
||||
return -EINVAL;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
adav80x_set_playback_pcm_format(codec, dai,
|
||||
params_format(params));
|
||||
adav80x_set_dac_clock(codec, rate);
|
||||
} else {
|
||||
adav80x_set_capture_pcm_format(codec, dai,
|
||||
params_format(params));
|
||||
adav80x_set_adc_clock(codec, rate);
|
||||
}
|
||||
adav80x->rate = rate;
|
||||
adav80x_set_deemph(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adav80x_set_sysclk(struct snd_soc_codec *codec,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (dir == SND_SOC_CLOCK_IN) {
|
||||
switch (clk_id) {
|
||||
case ADAV80X_CLK_XIN:
|
||||
case ADAV80X_CLK_XTAL:
|
||||
case ADAV80X_CLK_MCLKI:
|
||||
case ADAV80X_CLK_PLL1:
|
||||
case ADAV80X_CLK_PLL2:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
adav80x->sysclk = freq;
|
||||
|
||||
if (adav80x->clk_src != clk_id) {
|
||||
unsigned int iclk_ctrl1, iclk_ctrl2;
|
||||
|
||||
adav80x->clk_src = clk_id;
|
||||
if (clk_id == ADAV80X_CLK_XTAL)
|
||||
clk_id = ADAV80X_CLK_XIN;
|
||||
|
||||
iclk_ctrl1 = ADAV80X_ICLK_CTRL1_DAC_SRC(clk_id) |
|
||||
ADAV80X_ICLK_CTRL1_ADC_SRC(clk_id) |
|
||||
ADAV80X_ICLK_CTRL1_ICLK2_SRC(clk_id);
|
||||
iclk_ctrl2 = ADAV80X_ICLK_CTRL2_ICLK1_SRC(clk_id);
|
||||
|
||||
snd_soc_write(codec, ADAV80X_ICLK_CTRL1, iclk_ctrl1);
|
||||
snd_soc_write(codec, ADAV80X_ICLK_CTRL2, iclk_ctrl2);
|
||||
|
||||
snd_soc_dapm_sync(&codec->dapm);
|
||||
}
|
||||
} else {
|
||||
unsigned int mask;
|
||||
|
||||
switch (clk_id) {
|
||||
case ADAV80X_CLK_SYSCLK1:
|
||||
case ADAV80X_CLK_SYSCLK2:
|
||||
case ADAV80X_CLK_SYSCLK3:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
clk_id -= ADAV80X_CLK_SYSCLK1;
|
||||
mask = ADAV80X_PLL_OUTE_SYSCLKPD(clk_id);
|
||||
|
||||
if (freq == 0) {
|
||||
snd_soc_update_bits(codec, ADAV80X_PLL_OUTE, mask, mask);
|
||||
adav80x->sysclk_pd[clk_id] = true;
|
||||
} else {
|
||||
snd_soc_update_bits(codec, ADAV80X_PLL_OUTE, mask, 0);
|
||||
adav80x->sysclk_pd[clk_id] = false;
|
||||
}
|
||||
|
||||
if (adav80x->sysclk_pd[0])
|
||||
snd_soc_dapm_disable_pin(&codec->dapm, "PLL1");
|
||||
else
|
||||
snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1");
|
||||
|
||||
if (adav80x->sysclk_pd[1] || adav80x->sysclk_pd[2])
|
||||
snd_soc_dapm_disable_pin(&codec->dapm, "PLL2");
|
||||
else
|
||||
snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL2");
|
||||
|
||||
snd_soc_dapm_sync(&codec->dapm);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
|
||||
int source, unsigned int freq_in, unsigned int freq_out)
|
||||
{
|
||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int pll_ctrl1 = 0;
|
||||
unsigned int pll_ctrl2 = 0;
|
||||
unsigned int pll_src;
|
||||
|
||||
switch (source) {
|
||||
case ADAV80X_PLL_SRC_XTAL:
|
||||
case ADAV80X_PLL_SRC_XIN:
|
||||
case ADAV80X_PLL_SRC_MCLKI:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!freq_out)
|
||||
return 0;
|
||||
|
||||
switch (freq_in) {
|
||||
case 27000000:
|
||||
break;
|
||||
case 54000000:
|
||||
if (source == ADAV80X_PLL_SRC_XIN) {
|
||||
pll_ctrl1 |= ADAV80X_PLL_CTRL1_PLLDIV;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (freq_out > 12288000) {
|
||||
pll_ctrl2 |= ADAV80X_PLL_CTRL2_DOUB(pll_id);
|
||||
freq_out /= 2;
|
||||
}
|
||||
|
||||
/* freq_out = sample_rate * 256 */
|
||||
switch (freq_out) {
|
||||
case 8192000:
|
||||
pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_32(pll_id);
|
||||
break;
|
||||
case 11289600:
|
||||
pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_44(pll_id);
|
||||
break;
|
||||
case 12288000:
|
||||
pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_48(pll_id);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, ADAV80X_PLL_CTRL1, ADAV80X_PLL_CTRL1_PLLDIV,
|
||||
pll_ctrl1);
|
||||
snd_soc_update_bits(codec, ADAV80X_PLL_CTRL2,
|
||||
ADAV80X_PLL_CTRL2_PLL_MASK(pll_id), pll_ctrl2);
|
||||
|
||||
if (source != adav80x->pll_src) {
|
||||
if (source == ADAV80X_PLL_SRC_MCLKI)
|
||||
pll_src = ADAV80X_PLL_CLK_SRC_PLL_MCLKI(pll_id);
|
||||
else
|
||||
pll_src = ADAV80X_PLL_CLK_SRC_PLL_XIN(pll_id);
|
||||
|
||||
snd_soc_update_bits(codec, ADAV80X_PLL_CLK_SRC,
|
||||
ADAV80X_PLL_CLK_SRC_PLL_MASK(pll_id), pll_src);
|
||||
|
||||
adav80x->pll_src = source;
|
||||
|
||||
snd_soc_dapm_sync(&codec->dapm);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adav80x_set_bias_level(struct snd_soc_codec *codec,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
unsigned int mask = ADAV80X_DAC_CTRL1_PD;
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
break;
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
break;
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
snd_soc_update_bits(codec, ADAV80X_DAC_CTRL1, mask, 0x00);
|
||||
break;
|
||||
case SND_SOC_BIAS_OFF:
|
||||
snd_soc_update_bits(codec, ADAV80X_DAC_CTRL1, mask, mask);
|
||||
break;
|
||||
}
|
||||
|
||||
codec->dapm.bias_level = level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Enforce the same sample rate on all audio interfaces */
|
||||
static int adav80x_dai_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (!codec->active || !adav80x->rate)
|
||||
return 0;
|
||||
|
||||
return snd_pcm_hw_constraint_minmax(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_RATE, adav80x->rate, adav80x->rate);
|
||||
}
|
||||
|
||||
static void adav80x_dai_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (!codec->active)
|
||||
adav80x->rate = 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops adav80x_dai_ops = {
|
||||
.set_fmt = adav80x_set_dai_fmt,
|
||||
.hw_params = adav80x_hw_params,
|
||||
.startup = adav80x_dai_startup,
|
||||
.shutdown = adav80x_dai_shutdown,
|
||||
};
|
||||
|
||||
#define ADAV80X_PLAYBACK_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
|
||||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | \
|
||||
SNDRV_PCM_RATE_96000)
|
||||
|
||||
#define ADAV80X_CAPTURE_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
|
||||
|
||||
#define ADAV80X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
|
||||
|
||||
static struct snd_soc_dai_driver adav80x_dais[] = {
|
||||
{
|
||||
.name = "adav80x-hifi",
|
||||
.id = 0,
|
||||
.playback = {
|
||||
.stream_name = "HiFi Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = ADAV80X_PLAYBACK_RATES,
|
||||
.formats = ADAV80X_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "HiFi Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = ADAV80X_CAPTURE_RATES,
|
||||
.formats = ADAV80X_FORMATS,
|
||||
},
|
||||
.ops = &adav80x_dai_ops,
|
||||
},
|
||||
{
|
||||
.name = "adav80x-aux",
|
||||
.id = 1,
|
||||
.playback = {
|
||||
.stream_name = "Aux Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = ADAV80X_PLAYBACK_RATES,
|
||||
.formats = ADAV80X_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Aux Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = ADAV80X_CAPTURE_RATES,
|
||||
.formats = ADAV80X_FORMATS,
|
||||
},
|
||||
.ops = &adav80x_dai_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int adav80x_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
int ret;
|
||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
ret = snd_soc_codec_set_cache_io(codec, 7, 9, adav80x->control_type);
|
||||
if (ret) {
|
||||
dev_err(codec->dev, "failed to set cache I/O: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Force PLLs on for SYSCLK output */
|
||||
snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1");
|
||||
snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL2");
|
||||
|
||||
/* Power down S/PDIF receiver, since it is currently not supported */
|
||||
snd_soc_write(codec, ADAV80X_PLL_OUTE, 0x20);
|
||||
/* Disable DAC zero flag */
|
||||
snd_soc_write(codec, ADAV80X_DAC_CTRL3, 0x6);
|
||||
|
||||
return adav80x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
}
|
||||
|
||||
static int adav80x_suspend(struct snd_soc_codec *codec, pm_message_t state)
|
||||
{
|
||||
return adav80x_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
}
|
||||
|
||||
static int adav80x_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
adav80x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
codec->cache_sync = 1;
|
||||
snd_soc_cache_sync(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adav80x_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
return adav80x_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver adav80x_codec_driver = {
|
||||
.probe = adav80x_probe,
|
||||
.remove = adav80x_remove,
|
||||
.suspend = adav80x_suspend,
|
||||
.resume = adav80x_resume,
|
||||
.set_bias_level = adav80x_set_bias_level,
|
||||
|
||||
.set_pll = adav80x_set_pll,
|
||||
.set_sysclk = adav80x_set_sysclk,
|
||||
|
||||
.reg_word_size = sizeof(u8),
|
||||
.reg_cache_size = ARRAY_SIZE(adav80x_default_regs),
|
||||
.reg_cache_default = adav80x_default_regs,
|
||||
|
||||
.controls = adav80x_controls,
|
||||
.num_controls = ARRAY_SIZE(adav80x_controls),
|
||||
.dapm_widgets = adav80x_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(adav80x_dapm_widgets),
|
||||
.dapm_routes = adav80x_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(adav80x_dapm_routes),
|
||||
};
|
||||
|
||||
static int __devinit adav80x_bus_probe(struct device *dev,
|
||||
enum snd_soc_control_type control_type)
|
||||
{
|
||||
struct adav80x *adav80x;
|
||||
int ret;
|
||||
|
||||
adav80x = kzalloc(sizeof(*adav80x), GFP_KERNEL);
|
||||
if (!adav80x)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, adav80x);
|
||||
adav80x->control_type = control_type;
|
||||
|
||||
ret = snd_soc_register_codec(dev, &adav80x_codec_driver,
|
||||
adav80x_dais, ARRAY_SIZE(adav80x_dais));
|
||||
if (ret)
|
||||
kfree(adav80x);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit adav80x_bus_remove(struct device *dev)
|
||||
{
|
||||
snd_soc_unregister_codec(dev);
|
||||
kfree(dev_get_drvdata(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
static int __devinit adav80x_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
return adav80x_bus_probe(&spi->dev, SND_SOC_SPI);
|
||||
}
|
||||
|
||||
static int __devexit adav80x_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
return adav80x_bus_remove(&spi->dev);
|
||||
}
|
||||
|
||||
static struct spi_driver adav80x_spi_driver = {
|
||||
.driver = {
|
||||
.name = "adav801",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = adav80x_spi_probe,
|
||||
.remove = __devexit_p(adav80x_spi_remove),
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
static const struct i2c_device_id adav80x_id[] = {
|
||||
{ "adav803", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adav80x_id);
|
||||
|
||||
static int __devinit adav80x_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
return adav80x_bus_probe(&client->dev, SND_SOC_I2C);
|
||||
}
|
||||
|
||||
static int __devexit adav80x_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
return adav80x_bus_remove(&client->dev);
|
||||
}
|
||||
|
||||
static struct i2c_driver adav80x_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "adav803",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = adav80x_i2c_probe,
|
||||
.remove = __devexit_p(adav80x_i2c_remove),
|
||||
.id_table = adav80x_id,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int __init adav80x_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
ret = i2c_add_driver(&adav80x_i2c_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
ret = spi_register_driver(&adav80x_spi_driver);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(adav80x_init);
|
||||
|
||||
static void __exit adav80x_exit(void)
|
||||
{
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
i2c_del_driver(&adav80x_i2c_driver);
|
||||
#endif
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
spi_unregister_driver(&adav80x_spi_driver);
|
||||
#endif
|
||||
}
|
||||
module_exit(adav80x_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC ADAV80x driver");
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_AUTHOR("Yi Li <yi.li@analog.com>>");
|
||||
MODULE_LICENSE("GPL");
|
35
sound/soc/codecs/adav80x.h
Normal file
35
sound/soc/codecs/adav80x.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* header file for ADAV80X parts
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#ifndef _ADAV80X_H
|
||||
#define _ADAV80X_H
|
||||
|
||||
enum adav80x_pll_src {
|
||||
ADAV80X_PLL_SRC_XIN,
|
||||
ADAV80X_PLL_SRC_XTAL,
|
||||
ADAV80X_PLL_SRC_MCLKI,
|
||||
};
|
||||
|
||||
enum adav80x_pll {
|
||||
ADAV80X_PLL1 = 0,
|
||||
ADAV80X_PLL2 = 1,
|
||||
};
|
||||
|
||||
enum adav80x_clk_src {
|
||||
ADAV80X_CLK_XIN = 0,
|
||||
ADAV80X_CLK_MCLKI = 1,
|
||||
ADAV80X_CLK_PLL1 = 2,
|
||||
ADAV80X_CLK_PLL2 = 3,
|
||||
ADAV80X_CLK_XTAL = 6,
|
||||
|
||||
ADAV80X_CLK_SYSCLK1 = 6,
|
||||
ADAV80X_CLK_SYSCLK2 = 7,
|
||||
ADAV80X_CLK_SYSCLK3 = 8,
|
||||
};
|
||||
|
||||
#endif
|
@ -1713,6 +1713,8 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
|
||||
snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_1 + reg_offset,
|
||||
WM8994_FLL1_ENA | WM8994_FLL1_FRAC,
|
||||
reg);
|
||||
|
||||
msleep(5);
|
||||
}
|
||||
|
||||
wm8994->fll[id].in = freq_in;
|
||||
|
@ -727,7 +727,7 @@ SND_SOC_DAPM_MIXER_NAMED_CTL("Mixer", SND_SOC_NOPM, 0, 0,
|
||||
SND_SOC_DAPM_PGA("LINEOUT PGA", WM9081_POWER_MANAGEMENT, 4, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_PGA("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("Speaker", WM9081_POWER_MANAGEMENT, 1, 0, NULL, 0),
|
||||
SND_SOC_DAPM_OUT_DRV("Speaker", WM9081_POWER_MANAGEMENT, 1, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("LINEOUT"),
|
||||
SND_SOC_DAPM_OUTPUT("SPKN"),
|
||||
|
@ -133,9 +133,9 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
|
||||
break;
|
||||
case 1:
|
||||
reg = snd_soc_read(codec, WM8993_DC_SERVO_3);
|
||||
reg_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
|
||||
reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
|
||||
>> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
|
||||
reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
|
||||
reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
|
||||
break;
|
||||
default:
|
||||
WARN(1, "Unknown DCS readback method\n");
|
||||
@ -149,13 +149,13 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
|
||||
dev_dbg(codec->dev, "Applying %d code DC servo correction\n",
|
||||
hubs->dcs_codes);
|
||||
|
||||
/* HPOUT1L */
|
||||
offset = reg_l;
|
||||
/* HPOUT1R */
|
||||
offset = reg_r;
|
||||
offset += hubs->dcs_codes;
|
||||
dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
|
||||
|
||||
/* HPOUT1R */
|
||||
offset = reg_r;
|
||||
/* HPOUT1L */
|
||||
offset = reg_l;
|
||||
offset += hubs->dcs_codes;
|
||||
dcs_cfg |= (u8)offset;
|
||||
|
||||
@ -167,8 +167,8 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
|
||||
WM8993_DCS_TRIG_DAC_WR_0 |
|
||||
WM8993_DCS_TRIG_DAC_WR_1);
|
||||
} else {
|
||||
dcs_cfg = reg_l << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
|
||||
dcs_cfg |= reg_r;
|
||||
dcs_cfg = reg_r << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
|
||||
dcs_cfg |= reg_l;
|
||||
}
|
||||
|
||||
/* Save the callibrated offset if we're in class W mode and
|
||||
|
@ -171,6 +171,14 @@ config SND_SOC_SMDK_WM8580_PCM
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on the SMDK.
|
||||
|
||||
config SND_SOC_SMDK_WM8994_PCM
|
||||
tristate "SoC PCM Audio support for WM8994 on SMDK"
|
||||
depends on SND_SOC_SAMSUNG && (MACH_SMDKC210 || MACH_SMDKV310)
|
||||
select SND_SOC_WM8994
|
||||
select SND_SAMSUNG_PCM
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on the SMDK
|
||||
|
||||
config SND_SOC_SPEYSIDE
|
||||
tristate "Audio support for Wolfson Speyside"
|
||||
depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
|
||||
|
@ -35,6 +35,7 @@ snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o
|
||||
snd-soc-goni-wm8994-objs := goni_wm8994.o
|
||||
snd-soc-smdk-spdif-objs := smdk_spdif.o
|
||||
snd-soc-smdk-wm8580pcm-objs := smdk_wm8580pcm.o
|
||||
snd-soc-smdk-wm8994pcm-objs := smdk_wm8994pcm.o
|
||||
snd-soc-speyside-objs := speyside.o
|
||||
snd-soc-speyside-wm8962-objs := speyside_wm8962.o
|
||||
|
||||
@ -55,5 +56,6 @@ 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_GONI_AQUILA_WM8994) += snd-soc-goni-wm8994.o
|
||||
obj-$(CONFIG_SND_SOC_SMDK_WM8580_PCM) += snd-soc-smdk-wm8580pcm.o
|
||||
obj-$(CONFIG_SND_SOC_SMDK_WM8994_PCM) += snd-soc-smdk-wm8994pcm.o
|
||||
obj-$(CONFIG_SND_SOC_SPEYSIDE) += snd-soc-speyside.o
|
||||
obj-$(CONFIG_SND_SOC_SPEYSIDE_WM8962) += snd-soc-speyside-wm8962.o
|
||||
|
143
sound/soc/samsung/i2s-regs.h
Normal file
143
sound/soc/samsung/i2s-regs.h
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* linux/sound/soc/samsung/i2s-regs.h
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* Samsung I2S driver's register header
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __SND_SOC_SAMSUNG_I2S_REGS_H
|
||||
#define __SND_SOC_SAMSUNG_I2S_REGS_H
|
||||
|
||||
#define I2SCON 0x0
|
||||
#define I2SMOD 0x4
|
||||
#define I2SFIC 0x8
|
||||
#define I2SPSR 0xc
|
||||
#define I2STXD 0x10
|
||||
#define I2SRXD 0x14
|
||||
#define I2SFICS 0x18
|
||||
#define I2STXDS 0x1c
|
||||
#define I2SAHB 0x20
|
||||
#define I2SSTR0 0x24
|
||||
#define I2SSIZE 0x28
|
||||
#define I2STRNCNT 0x2c
|
||||
#define I2SLVL0ADDR 0x30
|
||||
#define I2SLVL1ADDR 0x34
|
||||
#define I2SLVL2ADDR 0x38
|
||||
#define I2SLVL3ADDR 0x3c
|
||||
|
||||
#define CON_RSTCLR (1 << 31)
|
||||
#define CON_FRXOFSTATUS (1 << 26)
|
||||
#define CON_FRXORINTEN (1 << 25)
|
||||
#define CON_FTXSURSTAT (1 << 24)
|
||||
#define CON_FTXSURINTEN (1 << 23)
|
||||
#define CON_TXSDMA_PAUSE (1 << 20)
|
||||
#define CON_TXSDMA_ACTIVE (1 << 18)
|
||||
|
||||
#define CON_FTXURSTATUS (1 << 17)
|
||||
#define CON_FTXURINTEN (1 << 16)
|
||||
#define CON_TXFIFO2_EMPTY (1 << 15)
|
||||
#define CON_TXFIFO1_EMPTY (1 << 14)
|
||||
#define CON_TXFIFO2_FULL (1 << 13)
|
||||
#define CON_TXFIFO1_FULL (1 << 12)
|
||||
|
||||
#define CON_LRINDEX (1 << 11)
|
||||
#define CON_TXFIFO_EMPTY (1 << 10)
|
||||
#define CON_RXFIFO_EMPTY (1 << 9)
|
||||
#define CON_TXFIFO_FULL (1 << 8)
|
||||
#define CON_RXFIFO_FULL (1 << 7)
|
||||
#define CON_TXDMA_PAUSE (1 << 6)
|
||||
#define CON_RXDMA_PAUSE (1 << 5)
|
||||
#define CON_TXCH_PAUSE (1 << 4)
|
||||
#define CON_RXCH_PAUSE (1 << 3)
|
||||
#define CON_TXDMA_ACTIVE (1 << 2)
|
||||
#define CON_RXDMA_ACTIVE (1 << 1)
|
||||
#define CON_ACTIVE (1 << 0)
|
||||
|
||||
#define MOD_OPCLK_CDCLK_OUT (0 << 30)
|
||||
#define MOD_OPCLK_CDCLK_IN (1 << 30)
|
||||
#define MOD_OPCLK_BCLK_OUT (2 << 30)
|
||||
#define MOD_OPCLK_PCLK (3 << 30)
|
||||
#define MOD_OPCLK_MASK (3 << 30)
|
||||
#define MOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */
|
||||
|
||||
#define MOD_BLCS_SHIFT 26
|
||||
#define MOD_BLCS_16BIT (0 << MOD_BLCS_SHIFT)
|
||||
#define MOD_BLCS_8BIT (1 << MOD_BLCS_SHIFT)
|
||||
#define MOD_BLCS_24BIT (2 << MOD_BLCS_SHIFT)
|
||||
#define MOD_BLCS_MASK (3 << MOD_BLCS_SHIFT)
|
||||
#define MOD_BLCP_SHIFT 24
|
||||
#define MOD_BLCP_16BIT (0 << MOD_BLCP_SHIFT)
|
||||
#define MOD_BLCP_8BIT (1 << MOD_BLCP_SHIFT)
|
||||
#define MOD_BLCP_24BIT (2 << MOD_BLCP_SHIFT)
|
||||
#define MOD_BLCP_MASK (3 << MOD_BLCP_SHIFT)
|
||||
|
||||
#define MOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */
|
||||
#define MOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */
|
||||
#define MOD_C1DD_HHALF (1 << 19)
|
||||
#define MOD_C1DD_LHALF (1 << 18)
|
||||
#define MOD_DC2_EN (1 << 17)
|
||||
#define MOD_DC1_EN (1 << 16)
|
||||
#define MOD_BLC_16BIT (0 << 13)
|
||||
#define MOD_BLC_8BIT (1 << 13)
|
||||
#define MOD_BLC_24BIT (2 << 13)
|
||||
#define MOD_BLC_MASK (3 << 13)
|
||||
|
||||
#define MOD_IMS_SYSMUX (1 << 10)
|
||||
#define MOD_SLAVE (1 << 11)
|
||||
#define MOD_TXONLY (0 << 8)
|
||||
#define MOD_RXONLY (1 << 8)
|
||||
#define MOD_TXRX (2 << 8)
|
||||
#define MOD_MASK (3 << 8)
|
||||
#define MOD_LR_LLOW (0 << 7)
|
||||
#define MOD_LR_RLOW (1 << 7)
|
||||
#define MOD_SDF_IIS (0 << 5)
|
||||
#define MOD_SDF_MSB (1 << 5)
|
||||
#define MOD_SDF_LSB (2 << 5)
|
||||
#define MOD_SDF_MASK (3 << 5)
|
||||
#define MOD_RCLK_256FS (0 << 3)
|
||||
#define MOD_RCLK_512FS (1 << 3)
|
||||
#define MOD_RCLK_384FS (2 << 3)
|
||||
#define MOD_RCLK_768FS (3 << 3)
|
||||
#define MOD_RCLK_MASK (3 << 3)
|
||||
#define MOD_BCLK_32FS (0 << 1)
|
||||
#define MOD_BCLK_48FS (1 << 1)
|
||||
#define MOD_BCLK_16FS (2 << 1)
|
||||
#define MOD_BCLK_24FS (3 << 1)
|
||||
#define MOD_BCLK_MASK (3 << 1)
|
||||
#define MOD_8BIT (1 << 0)
|
||||
|
||||
#define MOD_CDCLKCON (1 << 12)
|
||||
|
||||
#define PSR_PSREN (1 << 15)
|
||||
|
||||
#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf)
|
||||
#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf)
|
||||
|
||||
#define FIC_TXFLUSH (1 << 15)
|
||||
#define FIC_RXFLUSH (1 << 7)
|
||||
|
||||
#define FIC_TXCOUNT(x) (((x) >> 8) & 0xf)
|
||||
#define FIC_RXCOUNT(x) (((x) >> 0) & 0xf)
|
||||
#define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f)
|
||||
|
||||
#define AHB_INTENLVL0 (1 << 24)
|
||||
#define AHB_LVL0INT (1 << 20)
|
||||
#define AHB_CLRLVL0INT (1 << 16)
|
||||
#define AHB_DMARLD (1 << 5)
|
||||
#define AHB_INTMASK (1 << 3)
|
||||
#define AHB_DMAEN (1 << 0)
|
||||
#define AHB_LVLINTMASK (0xf << 20)
|
||||
|
||||
#define I2SSIZE_TRNMSK (0xffff)
|
||||
#define I2SSIZE_SHIFT (16)
|
||||
|
||||
#endif /* __SND_SOC_SAMSUNG_I2S_REGS_H */
|
||||
|
||||
|
@ -22,109 +22,7 @@
|
||||
|
||||
#include "dma.h"
|
||||
#include "i2s.h"
|
||||
|
||||
#define I2SCON 0x0
|
||||
#define I2SMOD 0x4
|
||||
#define I2SFIC 0x8
|
||||
#define I2SPSR 0xc
|
||||
#define I2STXD 0x10
|
||||
#define I2SRXD 0x14
|
||||
#define I2SFICS 0x18
|
||||
#define I2STXDS 0x1c
|
||||
|
||||
#define CON_RSTCLR (1 << 31)
|
||||
#define CON_FRXOFSTATUS (1 << 26)
|
||||
#define CON_FRXORINTEN (1 << 25)
|
||||
#define CON_FTXSURSTAT (1 << 24)
|
||||
#define CON_FTXSURINTEN (1 << 23)
|
||||
#define CON_TXSDMA_PAUSE (1 << 20)
|
||||
#define CON_TXSDMA_ACTIVE (1 << 18)
|
||||
|
||||
#define CON_FTXURSTATUS (1 << 17)
|
||||
#define CON_FTXURINTEN (1 << 16)
|
||||
#define CON_TXFIFO2_EMPTY (1 << 15)
|
||||
#define CON_TXFIFO1_EMPTY (1 << 14)
|
||||
#define CON_TXFIFO2_FULL (1 << 13)
|
||||
#define CON_TXFIFO1_FULL (1 << 12)
|
||||
|
||||
#define CON_LRINDEX (1 << 11)
|
||||
#define CON_TXFIFO_EMPTY (1 << 10)
|
||||
#define CON_RXFIFO_EMPTY (1 << 9)
|
||||
#define CON_TXFIFO_FULL (1 << 8)
|
||||
#define CON_RXFIFO_FULL (1 << 7)
|
||||
#define CON_TXDMA_PAUSE (1 << 6)
|
||||
#define CON_RXDMA_PAUSE (1 << 5)
|
||||
#define CON_TXCH_PAUSE (1 << 4)
|
||||
#define CON_RXCH_PAUSE (1 << 3)
|
||||
#define CON_TXDMA_ACTIVE (1 << 2)
|
||||
#define CON_RXDMA_ACTIVE (1 << 1)
|
||||
#define CON_ACTIVE (1 << 0)
|
||||
|
||||
#define MOD_OPCLK_CDCLK_OUT (0 << 30)
|
||||
#define MOD_OPCLK_CDCLK_IN (1 << 30)
|
||||
#define MOD_OPCLK_BCLK_OUT (2 << 30)
|
||||
#define MOD_OPCLK_PCLK (3 << 30)
|
||||
#define MOD_OPCLK_MASK (3 << 30)
|
||||
#define MOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */
|
||||
|
||||
#define MOD_BLCS_SHIFT 26
|
||||
#define MOD_BLCS_16BIT (0 << MOD_BLCS_SHIFT)
|
||||
#define MOD_BLCS_8BIT (1 << MOD_BLCS_SHIFT)
|
||||
#define MOD_BLCS_24BIT (2 << MOD_BLCS_SHIFT)
|
||||
#define MOD_BLCS_MASK (3 << MOD_BLCS_SHIFT)
|
||||
#define MOD_BLCP_SHIFT 24
|
||||
#define MOD_BLCP_16BIT (0 << MOD_BLCP_SHIFT)
|
||||
#define MOD_BLCP_8BIT (1 << MOD_BLCP_SHIFT)
|
||||
#define MOD_BLCP_24BIT (2 << MOD_BLCP_SHIFT)
|
||||
#define MOD_BLCP_MASK (3 << MOD_BLCP_SHIFT)
|
||||
|
||||
#define MOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */
|
||||
#define MOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */
|
||||
#define MOD_C1DD_HHALF (1 << 19)
|
||||
#define MOD_C1DD_LHALF (1 << 18)
|
||||
#define MOD_DC2_EN (1 << 17)
|
||||
#define MOD_DC1_EN (1 << 16)
|
||||
#define MOD_BLC_16BIT (0 << 13)
|
||||
#define MOD_BLC_8BIT (1 << 13)
|
||||
#define MOD_BLC_24BIT (2 << 13)
|
||||
#define MOD_BLC_MASK (3 << 13)
|
||||
|
||||
#define MOD_IMS_SYSMUX (1 << 10)
|
||||
#define MOD_SLAVE (1 << 11)
|
||||
#define MOD_TXONLY (0 << 8)
|
||||
#define MOD_RXONLY (1 << 8)
|
||||
#define MOD_TXRX (2 << 8)
|
||||
#define MOD_MASK (3 << 8)
|
||||
#define MOD_LR_LLOW (0 << 7)
|
||||
#define MOD_LR_RLOW (1 << 7)
|
||||
#define MOD_SDF_IIS (0 << 5)
|
||||
#define MOD_SDF_MSB (1 << 5)
|
||||
#define MOD_SDF_LSB (2 << 5)
|
||||
#define MOD_SDF_MASK (3 << 5)
|
||||
#define MOD_RCLK_256FS (0 << 3)
|
||||
#define MOD_RCLK_512FS (1 << 3)
|
||||
#define MOD_RCLK_384FS (2 << 3)
|
||||
#define MOD_RCLK_768FS (3 << 3)
|
||||
#define MOD_RCLK_MASK (3 << 3)
|
||||
#define MOD_BCLK_32FS (0 << 1)
|
||||
#define MOD_BCLK_48FS (1 << 1)
|
||||
#define MOD_BCLK_16FS (2 << 1)
|
||||
#define MOD_BCLK_24FS (3 << 1)
|
||||
#define MOD_BCLK_MASK (3 << 1)
|
||||
#define MOD_8BIT (1 << 0)
|
||||
|
||||
#define MOD_CDCLKCON (1 << 12)
|
||||
|
||||
#define PSR_PSREN (1 << 15)
|
||||
|
||||
#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf)
|
||||
#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf)
|
||||
|
||||
#define FIC_TXFLUSH (1 << 15)
|
||||
#define FIC_RXFLUSH (1 << 7)
|
||||
#define FIC_TXCOUNT(x) (((x) >> 8) & 0xf)
|
||||
#define FIC_RXCOUNT(x) (((x) >> 0) & 0xf)
|
||||
#define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f)
|
||||
#include "i2s-regs.h"
|
||||
|
||||
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
|
||||
|
||||
|
176
sound/soc/samsung/smdk_wm8994pcm.c
Normal file
176
sound/soc/samsung/smdk_wm8994pcm.c
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* sound/soc/samsung/smdk_wm8994pcm.c
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* 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 <sound/soc.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "../codecs/wm8994.h"
|
||||
#include "dma.h"
|
||||
#include "pcm.h"
|
||||
|
||||
/*
|
||||
* Board Settings:
|
||||
* o '1' means 'ON'
|
||||
* o '0' means 'OFF'
|
||||
* o 'X' means 'Don't care'
|
||||
*
|
||||
* SMDKC210, SMDKV310: CFG3- 1001, CFG5-1000, CFG7-111111
|
||||
*/
|
||||
|
||||
/*
|
||||
* Configure audio route as :-
|
||||
* $ amixer sset 'DAC1' on,on
|
||||
* $ amixer sset 'Right Headphone Mux' 'DAC'
|
||||
* $ amixer sset 'Left Headphone Mux' 'DAC'
|
||||
* $ amixer sset 'DAC1R Mixer AIF1.1' on
|
||||
* $ amixer sset 'DAC1L Mixer AIF1.1' on
|
||||
* $ amixer sset 'IN2L' on
|
||||
* $ amixer sset 'IN2L PGA IN2LN' on
|
||||
* $ amixer sset 'MIXINL IN2L' on
|
||||
* $ amixer sset 'AIF1ADC1L Mixer ADC/DMIC' on
|
||||
* $ amixer sset 'IN2R' on
|
||||
* $ amixer sset 'IN2R PGA IN2RN' on
|
||||
* $ amixer sset 'MIXINR IN2R' on
|
||||
* $ amixer sset 'AIF1ADC1R Mixer ADC/DMIC' on
|
||||
*/
|
||||
|
||||
/* SMDK has a 16.9344MHZ crystal attached to WM8994 */
|
||||
#define SMDK_WM8994_FREQ 16934400
|
||||
|
||||
static int smdk_wm8994_pcm_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 long mclk_freq;
|
||||
int rfs, ret;
|
||||
|
||||
switch(params_rate(params)) {
|
||||
case 8000:
|
||||
rfs = 512;
|
||||
break;
|
||||
default:
|
||||
dev_err(cpu_dai->dev, "%s:%d Sampling Rate %u not supported!\n",
|
||||
__func__, __LINE__, params_rate(params));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mclk_freq = params_rate(params) * rfs;
|
||||
|
||||
/* Set the codec DAI configuration */
|
||||
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B
|
||||
| SND_SOC_DAIFMT_IB_NF
|
||||
| SND_SOC_DAIFMT_CBS_CFS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Set the cpu DAI configuration */
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_B
|
||||
| SND_SOC_DAIFMT_IB_NF
|
||||
| SND_SOC_DAIFMT_CBS_CFS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1,
|
||||
mclk_freq, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1,
|
||||
SMDK_WM8994_FREQ, mclk_freq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Set PCM source clock on CPU */
|
||||
ret = snd_soc_dai_set_sysclk(cpu_dai, S3C_PCM_CLKSRC_MUX,
|
||||
mclk_freq, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Set SCLK_DIV for making bclk */
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_PCM_SCLK_PER_FS, rfs);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops smdk_wm8994_pcm_ops = {
|
||||
.hw_params = smdk_wm8994_pcm_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link smdk_dai[] = {
|
||||
{
|
||||
.name = "WM8994 PAIF PCM",
|
||||
.stream_name = "Primary PCM",
|
||||
.cpu_dai_name = "samsung-pcm.0",
|
||||
.codec_dai_name = "wm8994-aif1",
|
||||
.platform_name = "samsung-audio",
|
||||
.codec_name = "wm8994-codec",
|
||||
.ops = &smdk_wm8994_pcm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card smdk_pcm = {
|
||||
.name = "SMDK-PCM",
|
||||
.dai_link = smdk_dai,
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
static int __devinit snd_smdk_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
smdk_pcm.dev = &pdev->dev;
|
||||
ret = snd_soc_register_card(&smdk_pcm);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit snd_smdk_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_card(&smdk_pcm);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver snd_smdk_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "samsung-smdk-pcm",
|
||||
},
|
||||
.probe = snd_smdk_probe,
|
||||
.remove = __devexit_p(snd_smdk_remove),
|
||||
};
|
||||
|
||||
static int __init smdk_audio_init(void)
|
||||
{
|
||||
return platform_driver_register(&snd_smdk_driver);
|
||||
}
|
||||
|
||||
module_init(smdk_audio_init);
|
||||
|
||||
static void __exit smdk_audio_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&snd_smdk_driver);
|
||||
}
|
||||
|
||||
module_exit(smdk_audio_exit);
|
||||
|
||||
MODULE_AUTHOR("Sangbeom Kim, <sbkim73@samsung.com>");
|
||||
MODULE_DESCRIPTION("ALSA SoC SMDK WM8994 for PCM");
|
||||
MODULE_LICENSE("GPL");
|
@ -30,14 +30,16 @@ static int speyside_wm8962_set_bias_level(struct snd_soc_card *card,
|
||||
WM8962_FLL_MCLK, 32768,
|
||||
44100 * 256);
|
||||
if (ret < 0)
|
||||
pr_err("Failed to start FLL\n");
|
||||
pr_err("Failed to start FLL: %d\n", ret);
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai,
|
||||
WM8962_SYSCLK_FLL,
|
||||
44100 * 256,
|
||||
SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
pr_err("Failed to set SYSCLK: %d\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -59,13 +61,15 @@ static int speyside_wm8962_set_bias_level_post(struct snd_soc_card *card,
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
|
||||
32768, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
pr_err("Failed to switch away from FLL: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
|
||||
0, 0, 0);
|
||||
if (ret < 0) {
|
||||
pr_err("Failed to stop FLL\n");
|
||||
pr_err("Failed to stop FLL: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
@ -986,6 +986,39 @@ err_probe:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int soc_probe_platform(struct snd_soc_card *card,
|
||||
struct snd_soc_platform *platform)
|
||||
{
|
||||
int ret = 0;
|
||||
const struct snd_soc_platform_driver *driver = platform->driver;
|
||||
|
||||
platform->card = card;
|
||||
|
||||
if (!try_module_get(platform->dev->driver->owner))
|
||||
return -ENODEV;
|
||||
|
||||
if (driver->probe) {
|
||||
ret = driver->probe(platform);
|
||||
if (ret < 0) {
|
||||
dev_err(platform->dev,
|
||||
"asoc: failed to probe platform %s: %d\n",
|
||||
platform->name, ret);
|
||||
goto err_probe;
|
||||
}
|
||||
}
|
||||
|
||||
/* mark platform as probed and add to card platform list */
|
||||
platform->probed = 1;
|
||||
list_add(&platform->card_list, &card->platform_dev_list);
|
||||
|
||||
return 0;
|
||||
|
||||
err_probe:
|
||||
module_put(platform->dev->driver->owner);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rtd_release(struct device *dev) {}
|
||||
|
||||
static int soc_post_component_init(struct snd_soc_card *card,
|
||||
@ -1109,21 +1142,9 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
|
||||
/* probe the platform */
|
||||
if (!platform->probed &&
|
||||
platform->driver->probe_order == order) {
|
||||
if (!try_module_get(platform->dev->driver->owner))
|
||||
return -ENODEV;
|
||||
|
||||
if (platform->driver->probe) {
|
||||
ret = platform->driver->probe(platform);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "asoc: failed to probe platform %s\n",
|
||||
platform->name);
|
||||
module_put(platform->dev->driver->owner);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
/* mark platform as probed and add to card platform list */
|
||||
platform->probed = 1;
|
||||
list_add(&platform->card_list, &card->platform_dev_list);
|
||||
ret = soc_probe_platform(card, platform);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* probe the CODEC DAI */
|
||||
@ -1619,6 +1640,36 @@ int snd_soc_codec_writable_register(struct snd_soc_codec *codec,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_codec_writable_register);
|
||||
|
||||
int snd_soc_platform_read(struct snd_soc_platform *platform,
|
||||
unsigned int reg)
|
||||
{
|
||||
unsigned int ret;
|
||||
|
||||
if (!platform->driver->read) {
|
||||
dev_err(platform->dev, "platform has no read back\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = platform->driver->read(platform, reg);
|
||||
dev_dbg(platform->dev, "read %x => %x\n", reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_platform_read);
|
||||
|
||||
int snd_soc_platform_write(struct snd_soc_platform *platform,
|
||||
unsigned int reg, unsigned int val)
|
||||
{
|
||||
if (!platform->driver->write) {
|
||||
dev_err(platform->dev, "platform has no write back\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev_dbg(platform->dev, "write %x = %x\n", reg, val);
|
||||
return platform->driver->write(platform, reg, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_platform_write);
|
||||
|
||||
/**
|
||||
* snd_soc_new_ac97_codec - initailise AC97 device
|
||||
* @codec: audio codec
|
||||
|
@ -222,12 +222,18 @@ static int tegra_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
if (i2sclock % (2 * srate))
|
||||
reg |= TEGRA_I2S_TIMING_NON_SYM_ENABLE;
|
||||
|
||||
if (!i2s->clk_refs)
|
||||
clk_enable(i2s->clk_i2s);
|
||||
|
||||
tegra_i2s_write(i2s, TEGRA_I2S_TIMING, reg);
|
||||
|
||||
tegra_i2s_write(i2s, TEGRA_I2S_FIFO_SCR,
|
||||
TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS |
|
||||
TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS);
|
||||
|
||||
if (!i2s->clk_refs)
|
||||
clk_disable(i2s->clk_i2s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -348,7 +354,6 @@ struct snd_soc_dai_driver tegra_i2s_dai[] = {
|
||||
static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_i2s * i2s;
|
||||
char clk_name[12]; /* tegra-i2s.0 */
|
||||
struct resource *mem, *memregion, *dmareq;
|
||||
int ret;
|
||||
|
||||
@ -383,8 +388,7 @@ static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev)
|
||||
}
|
||||
dev_set_drvdata(&pdev->dev, i2s);
|
||||
|
||||
snprintf(clk_name, sizeof(clk_name), DRV_NAME ".%d", pdev->id);
|
||||
i2s->clk_i2s = clk_get_sys(clk_name, NULL);
|
||||
i2s->clk_i2s = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(i2s->clk_i2s)) {
|
||||
dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
|
||||
ret = PTR_ERR(i2s->clk_i2s);
|
||||
|
Loading…
Reference in New Issue
Block a user