Merge branch 'topic/asoc' into for-linus

This commit is contained in:
Takashi Iwai 2011-07-22 08:43:19 +02:00
commit 13b137ef03
100 changed files with 10100 additions and 2086 deletions

View File

@ -533,6 +533,8 @@ L: device-drivers-devel@blackfin.uclinux.org
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*

View File

@ -209,6 +209,10 @@ struct snd_soc_dai_driver {
struct snd_soc_pcm_stream capture;
struct snd_soc_pcm_stream playback;
unsigned int symmetric_rates:1;
/* probe ordering - for components with runtime dependencies */
int probe_order;
int remove_order;
};
/*

View File

@ -348,6 +348,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm);
void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm);
int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num);
int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num);
/* dapm events */
int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd,
@ -429,6 +431,7 @@ struct snd_soc_dapm_path {
/* status */
u32 connect:1; /* source and sink widgets are connected */
u32 walked:1; /* path has been walked */
u32 weak:1; /* path ignored for power management */
int (*connected)(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink);
@ -444,6 +447,7 @@ struct snd_soc_dapm_widget {
char *name; /* widget name */
char *sname; /* stream name */
struct snd_soc_codec *codec;
struct snd_soc_platform *platform;
struct list_head list;
struct snd_soc_dapm_context *dapm;
@ -507,10 +511,11 @@ struct snd_soc_dapm_context {
struct device *dev; /* from parent - for debug */
struct snd_soc_codec *codec; /* parent codec */
struct snd_soc_platform *platform; /* parent platform */
struct snd_soc_card *card; /* parent card */
/* used during DAPM updates */
int dev_power;
enum snd_soc_bias_level target_bias_level;
struct list_head list;
#ifdef CONFIG_DEBUG_FS

View File

@ -202,6 +202,16 @@
#define SOC_VALUE_ENUM_SINGLE_DECL(name, xreg, xshift, xmask, xtexts, xvalues) \
SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xmask, xtexts, xvalues)
/*
* Component probe and remove ordering levels for components with runtime
* dependencies.
*/
#define SND_SOC_COMP_ORDER_FIRST -2
#define SND_SOC_COMP_ORDER_EARLY -1
#define SND_SOC_COMP_ORDER_NORMAL 0
#define SND_SOC_COMP_ORDER_LATE 1
#define SND_SOC_COMP_ORDER_LAST 2
/*
* Bias levels
*
@ -214,10 +224,10 @@
* @OFF: Power Off. No restrictions on transition times.
*/
enum snd_soc_bias_level {
SND_SOC_BIAS_OFF,
SND_SOC_BIAS_STANDBY,
SND_SOC_BIAS_PREPARE,
SND_SOC_BIAS_ON,
SND_SOC_BIAS_OFF = 0,
SND_SOC_BIAS_STANDBY = 1,
SND_SOC_BIAS_PREPARE = 2,
SND_SOC_BIAS_ON = 3,
};
struct snd_jack;
@ -258,6 +268,11 @@ enum snd_soc_compress_type {
SND_SOC_RBTREE_COMPRESSION
};
enum snd_soc_pcm_subclass {
SND_SOC_PCM_CLASS_PCM = 0,
SND_SOC_PCM_CLASS_BE = 1,
};
int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
unsigned int freq, int dir);
int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
@ -297,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);
@ -349,6 +368,8 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
const char *prefix);
int snd_soc_add_controls(struct snd_soc_codec *codec,
const struct snd_kcontrol_new *controls, int num_controls);
int snd_soc_add_platform_controls(struct snd_soc_platform *platform,
const struct snd_kcontrol_new *controls, int num_controls);
int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,
@ -612,6 +633,10 @@ struct snd_soc_codec_driver {
void (*seq_notifier)(struct snd_soc_dapm_context *,
enum snd_soc_dapm_type, int);
/* probe ordering - for components with runtime dependencies */
int probe_order;
int remove_order;
};
/* SoC platform interface */
@ -623,10 +648,17 @@ struct snd_soc_platform_driver {
int (*resume)(struct snd_soc_dai *dai);
/* pcm creation and destruction */
int (*pcm_new)(struct snd_card *, struct snd_soc_dai *,
struct snd_pcm *);
int (*pcm_new)(struct snd_soc_pcm_runtime *);
void (*pcm_free)(struct snd_pcm *);
/* Default control and setup, added after probe() is run */
const struct snd_kcontrol_new *controls;
int num_controls;
const struct snd_soc_dapm_widget *dapm_widgets;
int num_dapm_widgets;
const struct snd_soc_dapm_route *dapm_routes;
int num_dapm_routes;
/*
* For platform caused delay reporting.
* Optional.
@ -636,6 +668,14 @@ struct snd_soc_platform_driver {
/* platform stream ops */
struct snd_pcm_ops *ops;
/* 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 {
@ -650,6 +690,8 @@ struct snd_soc_platform {
struct snd_soc_card *card;
struct list_head list;
struct list_head card_list;
struct snd_soc_dapm_context dapm;
};
struct snd_soc_dai_link {
@ -725,8 +767,10 @@ struct snd_soc_card {
/* callbacks */
int (*set_bias_level)(struct snd_soc_card *,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level);
int (*set_bias_level_post)(struct snd_soc_card *,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level);
long pmdown_time;
@ -789,6 +833,9 @@ struct snd_soc_pcm_runtime {
struct device dev;
struct snd_soc_card *card;
struct snd_soc_dai_link *dai_link;
struct mutex pcm_mutex;
enum snd_soc_pcm_subclass pcm_subclass;
struct snd_pcm_ops ops;
unsigned int complete:1;
unsigned int dev_registered:1;

View File

@ -9,6 +9,7 @@
struct snd_soc_jack;
struct snd_soc_codec;
struct snd_soc_platform;
struct snd_soc_card;
struct snd_soc_dapm_widget;
@ -59,6 +60,50 @@ DEFINE_EVENT(snd_soc_reg, snd_soc_reg_read,
);
DECLARE_EVENT_CLASS(snd_soc_preg,
TP_PROTO(struct snd_soc_platform *platform, unsigned int reg,
unsigned int val),
TP_ARGS(platform, reg, val),
TP_STRUCT__entry(
__string( name, platform->name )
__field( int, id )
__field( unsigned int, reg )
__field( unsigned int, val )
),
TP_fast_assign(
__assign_str(name, platform->name);
__entry->id = platform->id;
__entry->reg = reg;
__entry->val = val;
),
TP_printk("platform=%s.%d reg=%x val=%x", __get_str(name),
(int)__entry->id, (unsigned int)__entry->reg,
(unsigned int)__entry->val)
);
DEFINE_EVENT(snd_soc_preg, snd_soc_preg_write,
TP_PROTO(struct snd_soc_platform *platform, unsigned int reg,
unsigned int val),
TP_ARGS(platform, reg, val)
);
DEFINE_EVENT(snd_soc_preg, snd_soc_preg_read,
TP_PROTO(struct snd_soc_platform *platform, unsigned int reg,
unsigned int val),
TP_ARGS(platform, reg, val)
);
DECLARE_EVENT_CLASS(snd_soc_card,
TP_PROTO(struct snd_soc_card *card, int val),

View File

@ -1,4 +1,5 @@
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
snd-soc-core-objs += soc-pcm.o soc-io.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/

View File

@ -364,9 +364,11 @@ static struct snd_pcm_ops atmel_pcm_ops = {
\*--------------------------------------------------------------------------*/
static u64 atmel_pcm_dmamask = 0xffffffff;
static int atmel_pcm_new(struct snd_card *card,
struct snd_soc_dai *dai, struct snd_pcm *pcm)
static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)
@ -382,7 +384,7 @@ static int atmel_pcm_new(struct snd_card *card,
}
if (dai->driver->capture.channels_min) {
pr_debug("at32-pcm:"
pr_debug("atmel-pcm:"
"Allocating PCM capture DMA buffer\n");
ret = atmel_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);

View File

@ -60,7 +60,7 @@ struct atmel_ssc_mask {
* This structure, shared between the PCM driver and the interface,
* contains all information required by the PCM driver to perform the
* PDC DMA operation. All fields except dma_intr_handler() are initialized
* by the interface. The dms_intr_handler() pointer is set by the PCM
* by the interface. The dma_intr_handler() pointer is set by the PCM
* driver and called by the interface SSC interrupt handler if it is
* non-NULL.
*/

View File

@ -402,7 +402,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S
&& bits > 16) {
printk(KERN_WARNING
"atmel_ssc_dai: sample size %d"
"atmel_ssc_dai: sample size %d "
"is too large for I2S\n", bits);
return -EINVAL;
}
@ -838,10 +838,8 @@ int atmel_ssc_set_audio(int ssc_id)
}
ssc_pdev = platform_device_alloc("atmel-ssc-dai", ssc_id);
if (!ssc_pdev) {
ssc_free(ssc);
if (!ssc_pdev)
return -ENOMEM;
}
/* If we can grab the SSC briefly to parent the DAI device off it */
ssc = ssc_request(ssc_id);

View File

@ -92,6 +92,7 @@ static struct snd_soc_ops at91sam9g20ek_ops = {
};
static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
static int mclk_on;

View File

@ -319,10 +319,11 @@ static void au1xpsc_pcm_free_dma_buffers(struct snd_pcm *pcm)
snd_pcm_lib_preallocate_free_for_all(pcm);
}
static int au1xpsc_pcm_new(struct snd_card *card,
struct snd_soc_dai *dai,
struct snd_pcm *pcm)
static int au1xpsc_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1);

View File

@ -10,13 +10,36 @@ config SND_BF5XX_I2S
config SND_BF5XX_SOC_SSM2602
tristate "SoC SSM2602 Audio support for BF52x ezkit"
depends on SND_BF5XX_I2S
depends on SND_BF5XX_I2S && (SPI_MASTER || I2C)
select SND_BF5XX_SOC_I2S
select SND_SOC_SSM2602
select I2C
help
Say Y if you want to add support for SoC audio on BF527-EZKIT.
config SND_SOC_BFIN_EVAL_ADAU1701
tristate "Support for the EVAL-ADAU1701MINIZ board on Blackfin eval boards"
depends on SND_BF5XX_I2S
select SND_BF5XX_SOC_I2S
select SND_SOC_ADAU1701
select I2C
help
Say Y if you want to add support for the Analog Devices EVAL-ADAU1701MINIZ
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

View File

@ -21,9 +21,13 @@ snd-ad1980-objs := bf5xx-ad1980.o
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
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

View File

@ -418,9 +418,11 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
int bf5xx_pcm_ac97_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
pr_debug("%s enter\n", __func__);

View File

@ -168,7 +168,7 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
ret = snd_pcm_hw_constraint_integer(runtime, \
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0)
goto out;
@ -257,9 +257,11 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
int bf5xx_pcm_i2s_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
pr_debug("%s enter\n", __func__);
@ -304,8 +306,8 @@ static int __devexit bfin_i2s_soc_platform_remove(struct platform_device *pdev)
static struct platform_driver bfin_i2s_pcm_driver = {
.driver = {
.name = "bfin-i2s-pcm-audio",
.owner = THIS_MODULE,
.name = "bfin-i2s-pcm-audio",
.owner = THIS_MODULE,
},
.probe = bfin_i2s_soc_platform_probe,

View File

@ -283,9 +283,11 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
static int bf5xx_pcm_tdm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
static int bf5xx_pcm_tdm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)

View File

@ -0,0 +1,139 @@
/*
* Machine driver for EVAL-ADAU1701MINIZ 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/module.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include "../codecs/adau1701.h"
static const struct snd_soc_dapm_widget bfin_eval_adau1701_dapm_widgets[] = {
SND_SOC_DAPM_SPK("Speaker", NULL),
SND_SOC_DAPM_LINE("Line Out", NULL),
SND_SOC_DAPM_LINE("Line In", NULL),
};
static const struct snd_soc_dapm_route bfin_eval_adau1701_dapm_routes[] = {
{ "Speaker", NULL, "OUT0" },
{ "Speaker", NULL, "OUT1" },
{ "Line Out", NULL, "OUT2" },
{ "Line Out", NULL, "OUT3" },
{ "IN0", NULL, "Line In" },
{ "IN1", NULL, "Line In" },
};
static int bfin_eval_adau1701_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_sysclk(codec_dai, ADAU1701_CLK_SRC_OSC, 12288000,
SND_SOC_CLOCK_IN);
return ret;
}
static struct snd_soc_ops bfin_eval_adau1701_ops = {
.hw_params = bfin_eval_adau1701_hw_params,
};
static struct snd_soc_dai_link bfin_eval_adau1701_dai[] = {
{
.name = "adau1701",
.stream_name = "adau1701",
.cpu_dai_name = "bfin-i2s.0",
.codec_dai_name = "adau1701",
.platform_name = "bfin-i2s-pcm-audio",
.codec_name = "adau1701.0-0034",
.ops = &bfin_eval_adau1701_ops,
},
{
.name = "adau1701",
.stream_name = "adau1701",
.cpu_dai_name = "bfin-i2s.1",
.codec_dai_name = "adau1701",
.platform_name = "bfin-i2s-pcm-audio",
.codec_name = "adau1701.0-0034",
.ops = &bfin_eval_adau1701_ops,
},
};
static struct snd_soc_card bfin_eval_adau1701 = {
.name = "bfin-eval-adau1701",
.dai_link = &bfin_eval_adau1701_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
.dapm_widgets = bfin_eval_adau1701_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1701_dapm_widgets),
.dapm_routes = bfin_eval_adau1701_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1701_dapm_routes),
};
static int bfin_eval_adau1701_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &bfin_eval_adau1701;
card->dev = &pdev->dev;
return snd_soc_register_card(&bfin_eval_adau1701);
}
static int __devexit bfin_eval_adau1701_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
snd_soc_unregister_card(card);
return 0;
}
static struct platform_driver bfin_eval_adau1701_driver = {
.driver = {
.name = "bfin-eval-adau1701",
.owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
},
.probe = bfin_eval_adau1701_probe,
.remove = __devexit_p(bfin_eval_adau1701_remove),
};
static int __init bfin_eval_adau1701_init(void)
{
return platform_driver_register(&bfin_eval_adau1701_driver);
}
module_init(bfin_eval_adau1701_init);
static void __exit bfin_eval_adau1701_exit(void)
{
platform_driver_unregister(&bfin_eval_adau1701_driver);
}
module_exit(bfin_eval_adau1701_exit);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("ALSA SoC bfin ADAU1701 driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:bfin-eval-adau1701");

View 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");

View File

@ -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
@ -42,6 +43,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_SN95031 if INTEL_SCU_IPC
select SND_SOC_SPDIF
select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI
select SND_SOC_STA32X if I2C
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
select SND_SOC_TLV320AIC23 if I2C
select SND_SOC_TLV320AIC26 if SPI_MASTER
@ -71,6 +73,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8770 if SPI_MASTER
select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8782
select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8900 if I2C
select SND_SOC_WM8903 if I2C
@ -84,6 +87,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8971 if I2C
select SND_SOC_WM8974 if I2C
select SND_SOC_WM8978 if I2C
select SND_SOC_WM8983 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8985 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8990 if I2C
@ -130,7 +134,14 @@ config SND_SOC_AD1980
config SND_SOC_AD73311
tristate
config SND_SOC_ADAU1701
select SIGMA
tristate
config SND_SOC_ADAV80X
tristate
config SND_SOC_ADS117X
tristate
@ -216,6 +227,9 @@ config SND_SOC_SPDIF
config SND_SOC_SSM2602
tristate
config SND_SOC_STA32X
tristate
config SND_SOC_STAC9766
tristate
@ -299,6 +313,9 @@ config SND_SOC_WM8770
config SND_SOC_WM8776
tristate
config SND_SOC_WM8782
tristate
config SND_SOC_WM8804
tristate
@ -338,6 +355,9 @@ config SND_SOC_WM8974
config SND_SOC_WM8978
tristate
config SND_SOC_WM8983
tristate
config SND_SOC_WM8985
tristate

View File

@ -4,6 +4,8 @@ snd-soc-ad1836-objs := ad1836.o
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
@ -28,6 +30,7 @@ snd-soc-alc5623-objs := alc5623.o
snd-soc-sn95031-objs := sn95031.o
snd-soc-spdif-objs := spdif_transciever.o
snd-soc-ssm2602-objs := ssm2602.o
snd-soc-sta32x-objs := sta32x.o
snd-soc-stac9766-objs := stac9766.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
@ -55,6 +58,7 @@ snd-soc-wm8750-objs := wm8750.o
snd-soc-wm8753-objs := wm8753.o
snd-soc-wm8770-objs := wm8770.o
snd-soc-wm8776-objs := wm8776.o
snd-soc-wm8782-objs := wm8782.o
snd-soc-wm8804-objs := wm8804.o
snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o
@ -68,6 +72,7 @@ snd-soc-wm8962-objs := wm8962.o
snd-soc-wm8971-objs := wm8971.o
snd-soc-wm8974-objs := wm8974.o
snd-soc-wm8978-objs := wm8978.o
snd-soc-wm8983-objs := wm8983.o
snd-soc-wm8985-objs := wm8985.o
snd-soc-wm8988-objs := wm8988.o
snd-soc-wm8990-objs := wm8990.o
@ -95,6 +100,8 @@ obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
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
@ -120,6 +127,7 @@ obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
@ -147,6 +155,7 @@ obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
obj-$(CONFIG_SND_SOC_WM8770) += snd-soc-wm8770.o
obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o
obj-$(CONFIG_SND_SOC_WM8782) += snd-soc-wm8782.o
obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o
obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
@ -160,6 +169,7 @@ obj-$(CONFIG_SND_SOC_WM8962) += snd-soc-wm8962.o
obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
obj-$(CONFIG_SND_SOC_WM8974) += snd-soc-wm8974.o
obj-$(CONFIG_SND_SOC_WM8978) += snd-soc-wm8978.o
obj-$(CONFIG_SND_SOC_WM8983) += snd-soc-wm8983.o
obj-$(CONFIG_SND_SOC_WM8985) += snd-soc-wm8985.o
obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o
obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o

View File

@ -1,19 +1,10 @@
/*
* File: sound/soc/codecs/ad1836.c
* Author: Barry Song <Barry.Song@analog.com>
/*
* Audio Codec driver supporting:
* AD1835A, AD1836, AD1837A, AD1838A, AD1839A
*
* Created: Aug 04 2009
* Description: Driver for AD1836 sound chip
* Copyright 2009-2011 Analog Devices Inc.
*
* Modified:
* Copyright 2009 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* 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.
* Licensed under the GPL-2 or later.
*/
#include <linux/init.h>
@ -30,10 +21,15 @@
#include <linux/spi/spi.h>
#include "ad1836.h"
enum ad1836_type {
AD1835,
AD1836,
AD1838,
};
/* codec private data */
struct ad1836_priv {
enum snd_soc_control_type control_type;
void *control_data;
enum ad1836_type type;
};
/*
@ -44,29 +40,60 @@ static const char *ad1836_deemp[] = {"None", "44.1kHz", "32kHz", "48kHz"};
static const struct soc_enum ad1836_deemp_enum =
SOC_ENUM_SINGLE(AD1836_DAC_CTRL1, 8, 4, ad1836_deemp);
static const struct snd_kcontrol_new ad1836_snd_controls[] = {
/* DAC volume control */
SOC_DOUBLE_R("DAC1 Volume", AD1836_DAC_L1_VOL,
AD1836_DAC_R1_VOL, 0, 0x3FF, 0),
SOC_DOUBLE_R("DAC2 Volume", AD1836_DAC_L2_VOL,
AD1836_DAC_R2_VOL, 0, 0x3FF, 0),
SOC_DOUBLE_R("DAC3 Volume", AD1836_DAC_L3_VOL,
AD1836_DAC_R3_VOL, 0, 0x3FF, 0),
#define AD1836_DAC_VOLUME(x) \
SOC_DOUBLE_R("DAC" #x " Playback Volume", AD1836_DAC_L_VOL(x), \
AD1836_DAC_R_VOL(x), 0, 0x3FF, 0)
/* ADC switch control */
SOC_DOUBLE("ADC1 Switch", AD1836_ADC_CTRL2, AD1836_ADCL1_MUTE,
AD1836_ADCR1_MUTE, 1, 1),
SOC_DOUBLE("ADC2 Switch", AD1836_ADC_CTRL2, AD1836_ADCL2_MUTE,
AD1836_ADCR2_MUTE, 1, 1),
#define AD1836_DAC_SWITCH(x) \
SOC_DOUBLE("DAC" #x " Playback Switch", AD1836_DAC_CTRL2, \
AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1)
/* DAC switch control */
SOC_DOUBLE("DAC1 Switch", AD1836_DAC_CTRL2, AD1836_DACL1_MUTE,
AD1836_DACR1_MUTE, 1, 1),
SOC_DOUBLE("DAC2 Switch", AD1836_DAC_CTRL2, AD1836_DACL2_MUTE,
AD1836_DACR2_MUTE, 1, 1),
SOC_DOUBLE("DAC3 Switch", AD1836_DAC_CTRL2, AD1836_DACL3_MUTE,
AD1836_DACR3_MUTE, 1, 1),
#define AD1836_ADC_SWITCH(x) \
SOC_DOUBLE("ADC" #x " Capture Switch", AD1836_ADC_CTRL2, \
AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1)
static const struct snd_kcontrol_new ad183x_dac_controls[] = {
AD1836_DAC_VOLUME(1),
AD1836_DAC_SWITCH(1),
AD1836_DAC_VOLUME(2),
AD1836_DAC_SWITCH(2),
AD1836_DAC_VOLUME(3),
AD1836_DAC_SWITCH(3),
AD1836_DAC_VOLUME(4),
AD1836_DAC_SWITCH(4),
};
static const struct snd_soc_dapm_widget ad183x_dac_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("DAC1OUT"),
SND_SOC_DAPM_OUTPUT("DAC2OUT"),
SND_SOC_DAPM_OUTPUT("DAC3OUT"),
SND_SOC_DAPM_OUTPUT("DAC4OUT"),
};
static const struct snd_soc_dapm_route ad183x_dac_routes[] = {
{ "DAC1OUT", NULL, "DAC" },
{ "DAC2OUT", NULL, "DAC" },
{ "DAC3OUT", NULL, "DAC" },
{ "DAC4OUT", NULL, "DAC" },
};
static const struct snd_kcontrol_new ad183x_adc_controls[] = {
AD1836_ADC_SWITCH(1),
AD1836_ADC_SWITCH(2),
AD1836_ADC_SWITCH(3),
};
static const struct snd_soc_dapm_widget ad183x_adc_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("ADC1IN"),
SND_SOC_DAPM_INPUT("ADC2IN"),
};
static const struct snd_soc_dapm_route ad183x_adc_routes[] = {
{ "ADC", NULL, "ADC1IN" },
{ "ADC", NULL, "ADC2IN" },
};
static const struct snd_kcontrol_new ad183x_controls[] = {
/* ADC high-pass filter */
SOC_SINGLE("ADC High Pass Filter Switch", AD1836_ADC_CTRL1,
AD1836_ADC_HIGHPASS_FILTER, 1, 0),
@ -75,27 +102,24 @@ static const struct snd_kcontrol_new ad1836_snd_controls[] = {
SOC_ENUM("Playback Deemphasis", ad1836_deemp_enum),
};
static const struct snd_soc_dapm_widget ad1836_dapm_widgets[] = {
static const struct snd_soc_dapm_widget ad183x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "Playback", AD1836_DAC_CTRL1,
AD1836_DAC_POWERDOWN, 1),
SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1836_ADC_CTRL1,
AD1836_ADC_POWERDOWN, 1, NULL, 0),
SND_SOC_DAPM_OUTPUT("DAC1OUT"),
SND_SOC_DAPM_OUTPUT("DAC2OUT"),
SND_SOC_DAPM_OUTPUT("DAC3OUT"),
SND_SOC_DAPM_INPUT("ADC1IN"),
SND_SOC_DAPM_INPUT("ADC2IN"),
};
static const struct snd_soc_dapm_route audio_paths[] = {
static const struct snd_soc_dapm_route ad183x_dapm_routes[] = {
{ "DAC", NULL, "ADC_PWR" },
{ "ADC", NULL, "ADC_PWR" },
{ "DAC1OUT", "DAC1 Switch", "DAC" },
{ "DAC2OUT", "DAC2 Switch", "DAC" },
{ "DAC3OUT", "DAC3 Switch", "DAC" },
{ "ADC", "ADC1 Switch", "ADC1IN" },
{ "ADC", "ADC2 Switch", "ADC2IN" },
};
static const DECLARE_TLV_DB_SCALE(ad1836_in_tlv, 0, 300, 0);
static const struct snd_kcontrol_new ad1836_controls[] = {
SOC_DOUBLE_TLV("ADC2 Capture Volume", AD1836_ADC_CTRL1, 3, 0, 4, 0,
ad1836_in_tlv),
};
/*
@ -165,64 +189,69 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream,
return 0;
}
#ifdef CONFIG_PM
static int ad1836_soc_suspend(struct snd_soc_codec *codec,
pm_message_t state)
{
/* reset clock control mode */
u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
adc_ctrl2 &= ~AD1836_ADC_SERFMT_MASK;
return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
}
static int ad1836_soc_resume(struct snd_soc_codec *codec)
{
/* restore clock control mode */
u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
adc_ctrl2 |= AD1836_ADC_AUX;
return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
}
#else
#define ad1836_soc_suspend NULL
#define ad1836_soc_resume NULL
#endif
static struct snd_soc_dai_ops ad1836_dai_ops = {
.hw_params = ad1836_hw_params,
.set_fmt = ad1836_set_dai_fmt,
};
/* codec DAI instance */
static struct snd_soc_dai_driver ad1836_dai = {
.name = "ad1836-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 6,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 4,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
},
.ops = &ad1836_dai_ops,
#define AD183X_DAI(_name, num_dacs, num_adcs) \
{ \
.name = _name "-hifi", \
.playback = { \
.stream_name = "Playback", \
.channels_min = 2, \
.channels_max = (num_dacs) * 2, \
.rates = SNDRV_PCM_RATE_48000, \
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \
}, \
.capture = { \
.stream_name = "Capture", \
.channels_min = 2, \
.channels_max = (num_adcs) * 2, \
.rates = SNDRV_PCM_RATE_48000, \
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \
}, \
.ops = &ad1836_dai_ops, \
}
static struct snd_soc_dai_driver ad183x_dais[] = {
[AD1835] = AD183X_DAI("ad1835", 4, 1),
[AD1836] = AD183X_DAI("ad1836", 3, 2),
[AD1838] = AD183X_DAI("ad1838", 3, 1),
};
#ifdef CONFIG_PM
static int ad1836_suspend(struct snd_soc_codec *codec, pm_message_t state)
{
/* reset clock control mode */
return snd_soc_update_bits(codec, AD1836_ADC_CTRL2,
AD1836_ADC_SERFMT_MASK, 0);
}
static int ad1836_resume(struct snd_soc_codec *codec)
{
/* restore clock control mode */
return snd_soc_update_bits(codec, AD1836_ADC_CTRL2,
AD1836_ADC_SERFMT_MASK, AD1836_ADC_AUX);
}
#else
#define ad1836_suspend NULL
#define ad1836_resume NULL
#endif
static int ad1836_probe(struct snd_soc_codec *codec)
{
struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = &codec->dapm;
int num_dacs, num_adcs;
int ret = 0;
int i;
num_dacs = ad183x_dais[ad1836->type].playback.channels_max / 2;
num_adcs = ad183x_dais[ad1836->type].capture.channels_max / 2;
codec->control_data = ad1836->control_data;
ret = snd_soc_codec_set_cache_io(codec, 4, 12, SND_SOC_SPI);
if (ret < 0) {
dev_err(codec->dev, "failed to set cache I/O: %d\n",
@ -239,21 +268,46 @@ static int ad1836_probe(struct snd_soc_codec *codec)
snd_soc_write(codec, AD1836_ADC_CTRL1, 0x100);
/* unmute adc channles, adc aux mode */
snd_soc_write(codec, AD1836_ADC_CTRL2, 0x180);
/* left/right diff:PGA/MUX */
snd_soc_write(codec, AD1836_ADC_CTRL3, 0x3A);
/* volume */
snd_soc_write(codec, AD1836_DAC_L1_VOL, 0x3FF);
snd_soc_write(codec, AD1836_DAC_R1_VOL, 0x3FF);
snd_soc_write(codec, AD1836_DAC_L2_VOL, 0x3FF);
snd_soc_write(codec, AD1836_DAC_R2_VOL, 0x3FF);
snd_soc_write(codec, AD1836_DAC_L3_VOL, 0x3FF);
snd_soc_write(codec, AD1836_DAC_R3_VOL, 0x3FF);
for (i = 1; i <= num_dacs; ++i) {
snd_soc_write(codec, AD1836_DAC_L_VOL(i), 0x3FF);
snd_soc_write(codec, AD1836_DAC_R_VOL(i), 0x3FF);
}
snd_soc_add_controls(codec, ad1836_snd_controls,
ARRAY_SIZE(ad1836_snd_controls));
snd_soc_dapm_new_controls(dapm, ad1836_dapm_widgets,
ARRAY_SIZE(ad1836_dapm_widgets));
snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));
if (ad1836->type == AD1836) {
/* left/right diff:PGA/MUX */
snd_soc_write(codec, AD1836_ADC_CTRL3, 0x3A);
ret = snd_soc_add_controls(codec, ad1836_controls,
ARRAY_SIZE(ad1836_controls));
if (ret)
return ret;
} else {
snd_soc_write(codec, AD1836_ADC_CTRL3, 0x00);
}
ret = snd_soc_add_controls(codec, ad183x_dac_controls, num_dacs * 2);
if (ret)
return ret;
ret = snd_soc_add_controls(codec, ad183x_adc_controls, num_adcs);
if (ret)
return ret;
ret = snd_soc_dapm_new_controls(dapm, ad183x_dac_dapm_widgets, num_dacs);
if (ret)
return ret;
ret = snd_soc_dapm_new_controls(dapm, ad183x_adc_dapm_widgets, num_adcs);
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(dapm, ad183x_dac_routes, num_dacs);
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(dapm, ad183x_adc_routes, num_adcs);
if (ret)
return ret;
return ret;
}
@ -262,19 +316,24 @@ static int ad1836_probe(struct snd_soc_codec *codec)
static int ad1836_remove(struct snd_soc_codec *codec)
{
/* reset clock control mode */
u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
adc_ctrl2 &= ~AD1836_ADC_SERFMT_MASK;
return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
return snd_soc_update_bits(codec, AD1836_ADC_CTRL2,
AD1836_ADC_SERFMT_MASK, 0);
}
static struct snd_soc_codec_driver soc_codec_dev_ad1836 = {
.probe = ad1836_probe,
.remove = ad1836_remove,
.suspend = ad1836_soc_suspend,
.resume = ad1836_soc_resume,
.probe = ad1836_probe,
.remove = ad1836_remove,
.suspend = ad1836_suspend,
.resume = ad1836_resume,
.reg_cache_size = AD1836_NUM_REGS,
.reg_word_size = sizeof(u16),
.controls = ad183x_controls,
.num_controls = ARRAY_SIZE(ad183x_controls),
.dapm_widgets = ad183x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ad183x_dapm_widgets),
.dapm_routes = ad183x_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(ad183x_dapm_routes),
};
static int __devinit ad1836_spi_probe(struct spi_device *spi)
@ -286,12 +345,12 @@ static int __devinit ad1836_spi_probe(struct spi_device *spi)
if (ad1836 == NULL)
return -ENOMEM;
ad1836->type = spi_get_device_id(spi)->driver_data;
spi_set_drvdata(spi, ad1836);
ad1836->control_data = spi;
ad1836->control_type = SND_SOC_SPI;
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_ad1836, &ad1836_dai, 1);
&soc_codec_dev_ad1836, &ad183x_dais[ad1836->type], 1);
if (ret < 0)
kfree(ad1836);
return ret;
@ -303,27 +362,29 @@ static int __devexit ad1836_spi_remove(struct spi_device *spi)
kfree(spi_get_drvdata(spi));
return 0;
}
static const struct spi_device_id ad1836_ids[] = {
{ "ad1835", AD1835 },
{ "ad1836", AD1836 },
{ "ad1837", AD1835 },
{ "ad1838", AD1838 },
{ "ad1839", AD1838 },
{ },
};
MODULE_DEVICE_TABLE(spi, ad1836_ids);
static struct spi_driver ad1836_spi_driver = {
.driver = {
.name = "ad1836-codec",
.name = "ad1836",
.owner = THIS_MODULE,
},
.probe = ad1836_spi_probe,
.remove = __devexit_p(ad1836_spi_remove),
.id_table = ad1836_ids,
};
static int __init ad1836_init(void)
{
int ret;
ret = spi_register_driver(&ad1836_spi_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register ad1836 SPI driver: %d\n",
ret);
}
return ret;
return spi_register_driver(&ad1836_spi_driver);
}
module_init(ad1836_init);

View File

@ -1,19 +1,10 @@
/*
* File: sound/soc/codecs/ad1836.h
* Based on:
* Author: Barry Song <Barry.Song@analog.com>
* Audio Codec driver supporting:
* AD1835A, AD1836, AD1837A, AD1838A, AD1839A
*
* Created: Aug 04, 2009
* Description: definitions for AD1836 registers
* Copyright 2009-2011 Analog Devices Inc.
*
* Modified:
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* 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.
* Licensed under the GPL-2 or later.
*/
#ifndef __AD1836_H__
@ -21,39 +12,30 @@
#define AD1836_DAC_CTRL1 0
#define AD1836_DAC_POWERDOWN 2
#define AD1836_DAC_SERFMT_MASK 0xE0
#define AD1836_DAC_SERFMT_MASK 0xE0
#define AD1836_DAC_SERFMT_PCK256 (0x4 << 5)
#define AD1836_DAC_SERFMT_PCK128 (0x5 << 5)
#define AD1836_DAC_WORD_LEN_MASK 0x18
#define AD1836_DAC_WORD_LEN_OFFSET 3
#define AD1836_DAC_CTRL2 1
#define AD1836_DACL1_MUTE 0
#define AD1836_DACR1_MUTE 1
#define AD1836_DACL2_MUTE 2
#define AD1836_DACR2_MUTE 3
#define AD1836_DACL3_MUTE 4
#define AD1836_DACR3_MUTE 5
#define AD1836_DAC_L1_VOL 2
#define AD1836_DAC_R1_VOL 3
#define AD1836_DAC_L2_VOL 4
#define AD1836_DAC_R2_VOL 5
#define AD1836_DAC_L3_VOL 6
#define AD1836_DAC_R3_VOL 7
/* These macros are one-based. So AD183X_MUTE_LEFT(1) will return the mute bit
* for the first ADC/DAC */
#define AD1836_MUTE_LEFT(x) (((x) * 2) - 2)
#define AD1836_MUTE_RIGHT(x) (((x) * 2) - 1)
#define AD1836_DAC_L_VOL(x) ((x) * 2)
#define AD1836_DAC_R_VOL(x) (1 + ((x) * 2))
#define AD1836_ADC_CTRL1 12
#define AD1836_ADC_POWERDOWN 7
#define AD1836_ADC_HIGHPASS_FILTER 8
#define AD1836_ADC_CTRL2 13
#define AD1836_ADCL1_MUTE 0
#define AD1836_ADCR1_MUTE 1
#define AD1836_ADCL2_MUTE 2
#define AD1836_ADCR2_MUTE 3
#define AD1836_ADC_WORD_LEN_MASK 0x30
#define AD1836_ADC_WORD_OFFSET 5
#define AD1836_ADC_SERFMT_MASK (7 << 6)
#define AD1836_ADC_SERFMT_MASK (7 << 6)
#define AD1836_ADC_SERFMT_PCK256 (0x4 << 6)
#define AD1836_ADC_SERFMT_PCK128 (0x5 << 6)
#define AD1836_ADC_AUX (0x6 << 6)

549
sound/soc/codecs/adau1701.c Normal file
View File

@ -0,0 +1,549 @@
/*
* Driver for ADAU1701 SigmaDSP processor
*
* Copyright 2011 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
* based on an inital version by Cliff Cai <cliff.cai@analog.com>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/sigma.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "adau1701.h"
#define ADAU1701_DSPCTRL 0x1c
#define ADAU1701_SEROCTL 0x1e
#define ADAU1701_SERICTL 0x1f
#define ADAU1701_AUXNPOW 0x22
#define ADAU1701_OSCIPOW 0x26
#define ADAU1701_DACSET 0x27
#define ADAU1701_NUM_REGS 0x28
#define ADAU1701_DSPCTRL_CR (1 << 2)
#define ADAU1701_DSPCTRL_DAM (1 << 3)
#define ADAU1701_DSPCTRL_ADM (1 << 4)
#define ADAU1701_DSPCTRL_SR_48 0x00
#define ADAU1701_DSPCTRL_SR_96 0x01
#define ADAU1701_DSPCTRL_SR_192 0x02
#define ADAU1701_DSPCTRL_SR_MASK 0x03
#define ADAU1701_SEROCTL_INV_LRCLK 0x2000
#define ADAU1701_SEROCTL_INV_BCLK 0x1000
#define ADAU1701_SEROCTL_MASTER 0x0800
#define ADAU1701_SEROCTL_OBF16 0x0000
#define ADAU1701_SEROCTL_OBF8 0x0200
#define ADAU1701_SEROCTL_OBF4 0x0400
#define ADAU1701_SEROCTL_OBF2 0x0600
#define ADAU1701_SEROCTL_OBF_MASK 0x0600
#define ADAU1701_SEROCTL_OLF1024 0x0000
#define ADAU1701_SEROCTL_OLF512 0x0080
#define ADAU1701_SEROCTL_OLF256 0x0100
#define ADAU1701_SEROCTL_OLF_MASK 0x0180
#define ADAU1701_SEROCTL_MSB_DEALY1 0x0000
#define ADAU1701_SEROCTL_MSB_DEALY0 0x0004
#define ADAU1701_SEROCTL_MSB_DEALY8 0x0008
#define ADAU1701_SEROCTL_MSB_DEALY12 0x000c
#define ADAU1701_SEROCTL_MSB_DEALY16 0x0010
#define ADAU1701_SEROCTL_MSB_DEALY_MASK 0x001c
#define ADAU1701_SEROCTL_WORD_LEN_24 0x0000
#define ADAU1701_SEROCTL_WORD_LEN_20 0x0001
#define ADAU1701_SEROCTL_WORD_LEN_16 0x0010
#define ADAU1701_SEROCTL_WORD_LEN_MASK 0x0003
#define ADAU1701_AUXNPOW_VBPD 0x40
#define ADAU1701_AUXNPOW_VRPD 0x20
#define ADAU1701_SERICTL_I2S 0
#define ADAU1701_SERICTL_LEFTJ 1
#define ADAU1701_SERICTL_TDM 2
#define ADAU1701_SERICTL_RIGHTJ_24 3
#define ADAU1701_SERICTL_RIGHTJ_20 4
#define ADAU1701_SERICTL_RIGHTJ_18 5
#define ADAU1701_SERICTL_RIGHTJ_16 6
#define ADAU1701_SERICTL_MODE_MASK 7
#define ADAU1701_SERICTL_INV_BCLK BIT(3)
#define ADAU1701_SERICTL_INV_LRCLK BIT(4)
#define ADAU1701_OSCIPOW_OPD 0x04
#define ADAU1701_DACSET_DACINIT 1
#define ADAU1701_FIRMWARE "adau1701.bin"
struct adau1701 {
unsigned int dai_fmt;
};
static const struct snd_kcontrol_new adau1701_controls[] = {
SOC_SINGLE("Master Capture Switch", ADAU1701_DSPCTRL, 4, 1, 0),
};
static const struct snd_soc_dapm_widget adau1701_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC0", "Playback", ADAU1701_AUXNPOW, 3, 1),
SND_SOC_DAPM_DAC("DAC1", "Playback", ADAU1701_AUXNPOW, 2, 1),
SND_SOC_DAPM_DAC("DAC2", "Playback", ADAU1701_AUXNPOW, 1, 1),
SND_SOC_DAPM_DAC("DAC3", "Playback", ADAU1701_AUXNPOW, 0, 1),
SND_SOC_DAPM_ADC("ADC", "Capture", ADAU1701_AUXNPOW, 7, 1),
SND_SOC_DAPM_OUTPUT("OUT0"),
SND_SOC_DAPM_OUTPUT("OUT1"),
SND_SOC_DAPM_OUTPUT("OUT2"),
SND_SOC_DAPM_OUTPUT("OUT3"),
SND_SOC_DAPM_INPUT("IN0"),
SND_SOC_DAPM_INPUT("IN1"),
};
static const struct snd_soc_dapm_route adau1701_dapm_routes[] = {
{ "OUT0", NULL, "DAC0" },
{ "OUT1", NULL, "DAC1" },
{ "OUT2", NULL, "DAC2" },
{ "OUT3", NULL, "DAC3" },
{ "ADC", NULL, "IN0" },
{ "ADC", NULL, "IN1" },
};
static unsigned int adau1701_register_size(struct snd_soc_codec *codec,
unsigned int reg)
{
switch (reg) {
case ADAU1701_DSPCTRL:
case ADAU1701_SEROCTL:
case ADAU1701_AUXNPOW:
case ADAU1701_OSCIPOW:
case ADAU1701_DACSET:
return 2;
case ADAU1701_SERICTL:
return 1;
}
dev_err(codec->dev, "Unsupported register address: %d\n", reg);
return 0;
}
static int adau1701_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
unsigned int i;
unsigned int size;
uint8_t buf[4];
int ret;
size = adau1701_register_size(codec, reg);
if (size == 0)
return -EINVAL;
snd_soc_cache_write(codec, reg, value);
buf[0] = 0x08;
buf[1] = reg;
for (i = size + 1; i >= 2; --i) {
buf[i] = value;
value >>= 8;
}
ret = i2c_master_send(to_i2c_client(codec->dev), buf, size + 2);
if (ret == size + 2)
return 0;
else if (ret < 0)
return ret;
else
return -EIO;
}
static unsigned int adau1701_read(struct snd_soc_codec *codec, unsigned int reg)
{
unsigned int value;
unsigned int ret;
ret = snd_soc_cache_read(codec, reg, &value);
if (ret)
return ret;
return value;
}
static int adau1701_load_firmware(struct snd_soc_codec *codec)
{
return process_sigma_firmware(codec->control_data, ADAU1701_FIRMWARE);
}
static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
snd_pcm_format_t format)
{
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
unsigned int mask = ADAU1701_SEROCTL_WORD_LEN_MASK;
unsigned int val;
switch (format) {
case SNDRV_PCM_FORMAT_S16_LE:
val = ADAU1701_SEROCTL_WORD_LEN_16;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
val = ADAU1701_SEROCTL_WORD_LEN_20;
break;
case SNDRV_PCM_FORMAT_S24_LE:
val = ADAU1701_SEROCTL_WORD_LEN_24;
break;
default:
return -EINVAL;
}
if (adau1701->dai_fmt == SND_SOC_DAIFMT_RIGHT_J) {
switch (format) {
case SNDRV_PCM_FORMAT_S16_LE:
val |= ADAU1701_SEROCTL_MSB_DEALY16;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
val |= ADAU1701_SEROCTL_MSB_DEALY12;
break;
case SNDRV_PCM_FORMAT_S24_LE:
val |= ADAU1701_SEROCTL_MSB_DEALY8;
break;
}
mask |= ADAU1701_SEROCTL_MSB_DEALY_MASK;
}
snd_soc_update_bits(codec, ADAU1701_SEROCTL, mask, val);
return 0;
}
static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
snd_pcm_format_t format)
{
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
unsigned int val;
if (adau1701->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
return 0;
switch (format) {
case SNDRV_PCM_FORMAT_S16_LE:
val = ADAU1701_SERICTL_RIGHTJ_16;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
val = ADAU1701_SERICTL_RIGHTJ_20;
break;
case SNDRV_PCM_FORMAT_S24_LE:
val = ADAU1701_SERICTL_RIGHTJ_24;
break;
default:
return -EINVAL;
}
snd_soc_update_bits(codec, ADAU1701_SERICTL,
ADAU1701_SERICTL_MODE_MASK, val);
return 0;
}
static int adau1701_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
snd_pcm_format_t format;
unsigned int val;
switch (params_rate(params)) {
case 192000:
val = ADAU1701_DSPCTRL_SR_192;
break;
case 96000:
val = ADAU1701_DSPCTRL_SR_96;
break;
case 48000:
val = ADAU1701_DSPCTRL_SR_48;
break;
default:
return -EINVAL;
}
snd_soc_update_bits(codec, ADAU1701_DSPCTRL,
ADAU1701_DSPCTRL_SR_MASK, val);
format = params_format(params);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return adau1701_set_playback_pcm_format(codec, format);
else
return adau1701_set_capture_pcm_format(codec, format);
}
static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
unsigned int serictl = 0x00, seroctl = 0x00;
bool invert_lrclk;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
/* master, 64-bits per sample, 1 frame per sample */
seroctl |= ADAU1701_SEROCTL_MASTER | ADAU1701_SEROCTL_OBF16
| ADAU1701_SEROCTL_OLF1024;
break;
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
return -EINVAL;
}
/* clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
invert_lrclk = false;
break;
case SND_SOC_DAIFMT_NB_IF:
invert_lrclk = true;
break;
case SND_SOC_DAIFMT_IB_NF:
invert_lrclk = false;
serictl |= ADAU1701_SERICTL_INV_BCLK;
seroctl |= ADAU1701_SEROCTL_INV_BCLK;
break;
case SND_SOC_DAIFMT_IB_IF:
invert_lrclk = true;
serictl |= ADAU1701_SERICTL_INV_BCLK;
seroctl |= ADAU1701_SEROCTL_INV_BCLK;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
break;
case SND_SOC_DAIFMT_LEFT_J:
serictl |= ADAU1701_SERICTL_LEFTJ;
seroctl |= ADAU1701_SEROCTL_MSB_DEALY0;
invert_lrclk = !invert_lrclk;
break;
case SND_SOC_DAIFMT_RIGHT_J:
serictl |= ADAU1701_SERICTL_RIGHTJ_24;
seroctl |= ADAU1701_SEROCTL_MSB_DEALY8;
invert_lrclk = !invert_lrclk;
break;
default:
return -EINVAL;
}
if (invert_lrclk) {
seroctl |= ADAU1701_SEROCTL_INV_LRCLK;
serictl |= ADAU1701_SERICTL_INV_LRCLK;
}
adau1701->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
snd_soc_write(codec, ADAU1701_SERICTL, serictl);
snd_soc_update_bits(codec, ADAU1701_SEROCTL,
~ADAU1701_SEROCTL_WORD_LEN_MASK, seroctl);
return 0;
}
static int adau1701_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
unsigned int mask = ADAU1701_AUXNPOW_VBPD | ADAU1701_AUXNPOW_VRPD;
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* Enable VREF and VREF buffer */
snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, 0x00);
break;
case SND_SOC_BIAS_OFF:
/* Disable VREF and VREF buffer */
snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, mask);
break;
}
codec->dapm.bias_level = level;
return 0;
}
static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
unsigned int mask = ADAU1701_DSPCTRL_DAM;
unsigned int val;
if (mute)
val = 0;
else
val = mask;
snd_soc_update_bits(codec, ADAU1701_DSPCTRL, mask, val);
return 0;
}
static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
unsigned int freq, int dir)
{
unsigned int val;
switch (clk_id) {
case ADAU1701_CLK_SRC_OSC:
val = 0x0;
break;
case ADAU1701_CLK_SRC_MCLK:
val = ADAU1701_OSCIPOW_OPD;
break;
default:
return -EINVAL;
}
snd_soc_update_bits(codec, ADAU1701_OSCIPOW, ADAU1701_OSCIPOW_OPD, val);
return 0;
}
#define ADAU1701_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
SNDRV_PCM_RATE_192000)
#define ADAU1701_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
static const struct snd_soc_dai_ops adau1701_dai_ops = {
.set_fmt = adau1701_set_dai_fmt,
.hw_params = adau1701_hw_params,
.digital_mute = adau1701_digital_mute,
};
static struct snd_soc_dai_driver adau1701_dai = {
.name = "adau1701",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = ADAU1701_RATES,
.formats = ADAU1701_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 8,
.rates = ADAU1701_RATES,
.formats = ADAU1701_FORMATS,
},
.ops = &adau1701_dai_ops,
.symmetric_rates = 1,
};
static int adau1701_probe(struct snd_soc_codec *codec)
{
int ret;
codec->dapm.idle_bias_off = 1;
ret = adau1701_load_firmware(codec);
if (ret)
dev_warn(codec->dev, "Failed to load firmware\n");
snd_soc_write(codec, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
snd_soc_write(codec, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR);
return 0;
}
static struct snd_soc_codec_driver adau1701_codec_drv = {
.probe = adau1701_probe,
.set_bias_level = adau1701_set_bias_level,
.reg_cache_size = ADAU1701_NUM_REGS,
.reg_word_size = sizeof(u16),
.controls = adau1701_controls,
.num_controls = ARRAY_SIZE(adau1701_controls),
.dapm_widgets = adau1701_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(adau1701_dapm_widgets),
.dapm_routes = adau1701_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(adau1701_dapm_routes),
.write = adau1701_write,
.read = adau1701_read,
.set_sysclk = adau1701_set_sysclk,
};
static __devinit int adau1701_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adau1701 *adau1701;
int ret;
adau1701 = kzalloc(sizeof(*adau1701), GFP_KERNEL);
if (!adau1701)
return -ENOMEM;
i2c_set_clientdata(client, adau1701);
ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,
&adau1701_dai, 1);
if (ret < 0)
kfree(adau1701);
return ret;
}
static __devexit int adau1701_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
kfree(i2c_get_clientdata(client));
return 0;
}
static const struct i2c_device_id adau1701_i2c_id[] = {
{ "adau1701", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id);
static struct i2c_driver adau1701_i2c_driver = {
.driver = {
.name = "adau1701",
.owner = THIS_MODULE,
},
.probe = adau1701_i2c_probe,
.remove = __devexit_p(adau1701_i2c_remove),
.id_table = adau1701_i2c_id,
};
static int __init adau1701_init(void)
{
return i2c_add_driver(&adau1701_i2c_driver);
}
module_init(adau1701_init);
static void __exit adau1701_exit(void)
{
i2c_del_driver(&adau1701_i2c_driver);
}
module_exit(adau1701_exit);
MODULE_DESCRIPTION("ASoC ADAU1701 SigmaDSP driver");
MODULE_AUTHOR("Cliff Cai <cliff.cai@analog.com>");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,17 @@
/*
* header file for ADAU1701 SigmaDSP processor
*
* Copyright 2011 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#ifndef _ADAU1701_H
#define _ADAU1701_H
enum adau1701_clk_src {
ADAU1701_CLK_SRC_OSC,
ADAU1701_CLK_SRC_MCLK,
};
#endif

951
sound/soc/codecs/adav80x.c Normal file
View 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:
return -EINVAL;
}
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:
return -EINVAL;
}
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");

View 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

View File

@ -457,7 +457,7 @@ static struct snd_soc_dai_ops ak4641_pcm_dai_ops = {
.set_sysclk = ak4641_set_dai_sysclk,
};
struct snd_soc_dai_driver ak4641_dai[] = {
static struct snd_soc_dai_driver ak4641_dai[] = {
{
.name = "ak4641-hifi",
.id = 1,

View File

@ -636,10 +636,7 @@ static int cs4270_soc_resume(struct snd_soc_codec *codec)
#endif /* CONFIG_PM */
/*
* ASoC codec device structure
*
* Assign this variable to the codec_dev field of the machine driver's
* snd_soc_device structure.
* ASoC codec driver structure
*/
static const struct snd_soc_codec_driver soc_codec_device_cs4270 = {
.probe = cs4270_probe,

View File

@ -1397,8 +1397,6 @@ static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
if (freq == max98088->sysclk)
return 0;
max98088->sysclk = freq; /* remember current sysclk */
/* Setup clocks for slave mode, and using the PLL
* PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
* 0x02 (when master clk is 20MHz to 30MHz)..

View File

@ -1517,8 +1517,6 @@ static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
if (freq == max98095->sysclk)
return 0;
max98095->sysclk = freq; /* remember current sysclk */
/* Setup clocks for slave mode, and using the PLL
* PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
* 0x02 (when master clk is 20MHz to 40MHz)..
@ -2261,11 +2259,11 @@ static int max98095_probe(struct snd_soc_codec *codec)
ret = snd_soc_read(codec, M98095_0FF_REV_ID);
if (ret < 0) {
dev_err(codec->dev, "Failed to read device revision: %d\n",
dev_err(codec->dev, "Failure reading hardware revision: %d\n",
ret);
goto err_access;
}
dev_info(codec->dev, "revision %c\n", ret + 'A');
dev_info(codec->dev, "Hardware revision: %c\n", ret - 0x40 + 'A');
snd_soc_write(codec, M98095_097_PWR_SYS, M98095_PWRSV);
@ -2342,8 +2340,8 @@ static int max98095_i2c_probe(struct i2c_client *i2c,
max98095->control_data = i2c;
max98095->pdata = i2c->dev.platform_data;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_max98095, &max98095_dai[0], 3);
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98095,
max98095_dai, ARRAY_SIZE(max98095_dai));
if (ret < 0)
kfree(max98095);
return ret;

917
sound/soc/codecs/sta32x.c Normal file
View File

@ -0,0 +1,917 @@
/*
* Codec driver for ST STA32x 2.1-channel high-efficiency digital audio system
*
* Copyright: 2011 Raumfeld GmbH
* Author: Johannes Stezenbach <js@sig21.net>
*
* based on code from:
* Wolfson Microelectronics PLC.
* Mark Brown <broonie@opensource.wolfsonmicro.com>
* Freescale Semiconductor, Inc.
* Timur Tabi <timur@freescale.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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "sta32x.h"
#define STA32X_RATES (SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_88200 | \
SNDRV_PCM_RATE_96000 | \
SNDRV_PCM_RATE_176400 | \
SNDRV_PCM_RATE_192000)
#define STA32X_FORMATS \
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
/* Power-up register defaults */
static const u8 sta32x_regs[STA32X_REGISTER_COUNT] = {
0x63, 0x80, 0xc2, 0x40, 0xc2, 0x5c, 0x10, 0xff, 0x60, 0x60,
0x60, 0x80, 0x00, 0x00, 0x00, 0x40, 0x80, 0x77, 0x6a, 0x69,
0x6a, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d,
0xc0, 0xf3, 0x33, 0x00, 0x0c,
};
/* regulator power supply names */
static const char *sta32x_supply_names[] = {
"Vdda", /* analog supply, 3.3VV */
"Vdd3", /* digital supply, 3.3V */
"Vcc" /* power amp spply, 10V - 36V */
};
/* codec private data */
struct sta32x_priv {
struct regulator_bulk_data supplies[ARRAY_SIZE(sta32x_supply_names)];
struct snd_soc_codec *codec;
unsigned int mclk;
unsigned int format;
};
static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12700, 50, 1);
static const DECLARE_TLV_DB_SCALE(chvol_tlv, -7950, 50, 1);
static const DECLARE_TLV_DB_SCALE(tone_tlv, -120, 200, 0);
static const char *sta32x_drc_ac[] = {
"Anti-Clipping", "Dynamic Range Compression" };
static const char *sta32x_auto_eq_mode[] = {
"User", "Preset", "Loudness" };
static const char *sta32x_auto_gc_mode[] = {
"User", "AC no clipping", "AC limited clipping (10%)",
"DRC nighttime listening mode" };
static const char *sta32x_auto_xo_mode[] = {
"User", "80Hz", "100Hz", "120Hz", "140Hz", "160Hz", "180Hz", "200Hz",
"220Hz", "240Hz", "260Hz", "280Hz", "300Hz", "320Hz", "340Hz", "360Hz" };
static const char *sta32x_preset_eq_mode[] = {
"Flat", "Rock", "Soft Rock", "Jazz", "Classical", "Dance", "Pop", "Soft",
"Hard", "Party", "Vocal", "Hip-Hop", "Dialog", "Bass-boost #1",
"Bass-boost #2", "Bass-boost #3", "Loudness 1", "Loudness 2",
"Loudness 3", "Loudness 4", "Loudness 5", "Loudness 6", "Loudness 7",
"Loudness 8", "Loudness 9", "Loudness 10", "Loudness 11", "Loudness 12",
"Loudness 13", "Loudness 14", "Loudness 15", "Loudness 16" };
static const char *sta32x_limiter_select[] = {
"Limiter Disabled", "Limiter #1", "Limiter #2" };
static const char *sta32x_limiter_attack_rate[] = {
"3.1584", "2.7072", "2.2560", "1.8048", "1.3536", "0.9024",
"0.4512", "0.2256", "0.1504", "0.1123", "0.0902", "0.0752",
"0.0645", "0.0564", "0.0501", "0.0451" };
static const char *sta32x_limiter_release_rate[] = {
"0.5116", "0.1370", "0.0744", "0.0499", "0.0360", "0.0299",
"0.0264", "0.0208", "0.0198", "0.0172", "0.0147", "0.0137",
"0.0134", "0.0117", "0.0110", "0.0104" };
static const unsigned int sta32x_limiter_ac_attack_tlv[] = {
TLV_DB_RANGE_HEAD(2),
0, 7, TLV_DB_SCALE_ITEM(-1200, 200, 0),
8, 16, TLV_DB_SCALE_ITEM(300, 100, 0),
};
static const unsigned int sta32x_limiter_ac_release_tlv[] = {
TLV_DB_RANGE_HEAD(5),
0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(-2900, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(-2000, 0, 0),
3, 8, TLV_DB_SCALE_ITEM(-1400, 200, 0),
8, 16, TLV_DB_SCALE_ITEM(-700, 100, 0),
};
static const unsigned int sta32x_limiter_drc_attack_tlv[] = {
TLV_DB_RANGE_HEAD(3),
0, 7, TLV_DB_SCALE_ITEM(-3100, 200, 0),
8, 13, TLV_DB_SCALE_ITEM(-1600, 100, 0),
14, 16, TLV_DB_SCALE_ITEM(-1000, 300, 0),
};
static const unsigned int sta32x_limiter_drc_release_tlv[] = {
TLV_DB_RANGE_HEAD(5),
0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
1, 2, TLV_DB_SCALE_ITEM(-3800, 200, 0),
3, 4, TLV_DB_SCALE_ITEM(-3300, 200, 0),
5, 12, TLV_DB_SCALE_ITEM(-3000, 200, 0),
13, 16, TLV_DB_SCALE_ITEM(-1500, 300, 0),
};
static const struct soc_enum sta32x_drc_ac_enum =
SOC_ENUM_SINGLE(STA32X_CONFD, STA32X_CONFD_DRC_SHIFT,
2, sta32x_drc_ac);
static const struct soc_enum sta32x_auto_eq_enum =
SOC_ENUM_SINGLE(STA32X_AUTO1, STA32X_AUTO1_AMEQ_SHIFT,
3, sta32x_auto_eq_mode);
static const struct soc_enum sta32x_auto_gc_enum =
SOC_ENUM_SINGLE(STA32X_AUTO1, STA32X_AUTO1_AMGC_SHIFT,
4, sta32x_auto_gc_mode);
static const struct soc_enum sta32x_auto_xo_enum =
SOC_ENUM_SINGLE(STA32X_AUTO2, STA32X_AUTO2_XO_SHIFT,
16, sta32x_auto_xo_mode);
static const struct soc_enum sta32x_preset_eq_enum =
SOC_ENUM_SINGLE(STA32X_AUTO3, STA32X_AUTO3_PEQ_SHIFT,
32, sta32x_preset_eq_mode);
static const struct soc_enum sta32x_limiter_ch1_enum =
SOC_ENUM_SINGLE(STA32X_C1CFG, STA32X_CxCFG_LS_SHIFT,
3, sta32x_limiter_select);
static const struct soc_enum sta32x_limiter_ch2_enum =
SOC_ENUM_SINGLE(STA32X_C2CFG, STA32X_CxCFG_LS_SHIFT,
3, sta32x_limiter_select);
static const struct soc_enum sta32x_limiter_ch3_enum =
SOC_ENUM_SINGLE(STA32X_C3CFG, STA32X_CxCFG_LS_SHIFT,
3, sta32x_limiter_select);
static const struct soc_enum sta32x_limiter1_attack_rate_enum =
SOC_ENUM_SINGLE(STA32X_L1AR, STA32X_LxA_SHIFT,
16, sta32x_limiter_attack_rate);
static const struct soc_enum sta32x_limiter2_attack_rate_enum =
SOC_ENUM_SINGLE(STA32X_L2AR, STA32X_LxA_SHIFT,
16, sta32x_limiter_attack_rate);
static const struct soc_enum sta32x_limiter1_release_rate_enum =
SOC_ENUM_SINGLE(STA32X_L1AR, STA32X_LxR_SHIFT,
16, sta32x_limiter_release_rate);
static const struct soc_enum sta32x_limiter2_release_rate_enum =
SOC_ENUM_SINGLE(STA32X_L2AR, STA32X_LxR_SHIFT,
16, sta32x_limiter_release_rate);
/* byte array controls for setting biquad, mixer, scaling coefficients;
* for biquads all five coefficients need to be set in one go,
* mixer and pre/postscale coefs can be set individually;
* each coef is 24bit, the bytes are ordered in the same way
* as given in the STA32x data sheet (big endian; b1, b2, a1, a2, b0)
*/
static int sta32x_coefficient_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
int numcoef = kcontrol->private_value >> 16;
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
uinfo->count = 3 * numcoef;
return 0;
}
static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
int numcoef = kcontrol->private_value >> 16;
int index = kcontrol->private_value & 0xffff;
unsigned int cfud;
int i;
/* preserve reserved bits in STA32X_CFUD */
cfud = snd_soc_read(codec, STA32X_CFUD) & 0xf0;
/* chip documentation does not say if the bits are self clearing,
* so do it explicitly */
snd_soc_write(codec, STA32X_CFUD, cfud);
snd_soc_write(codec, STA32X_CFADDR2, index);
if (numcoef == 1)
snd_soc_write(codec, STA32X_CFUD, cfud | 0x04);
else if (numcoef == 5)
snd_soc_write(codec, STA32X_CFUD, cfud | 0x08);
else
return -EINVAL;
for (i = 0; i < 3 * numcoef; i++)
ucontrol->value.bytes.data[i] =
snd_soc_read(codec, STA32X_B1CF1 + i);
return 0;
}
static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
int numcoef = kcontrol->private_value >> 16;
int index = kcontrol->private_value & 0xffff;
unsigned int cfud;
int i;
/* preserve reserved bits in STA32X_CFUD */
cfud = snd_soc_read(codec, STA32X_CFUD) & 0xf0;
/* chip documentation does not say if the bits are self clearing,
* so do it explicitly */
snd_soc_write(codec, STA32X_CFUD, cfud);
snd_soc_write(codec, STA32X_CFADDR2, index);
for (i = 0; i < 3 * numcoef; i++)
snd_soc_write(codec, STA32X_B1CF1 + i,
ucontrol->value.bytes.data[i]);
if (numcoef == 1)
snd_soc_write(codec, STA32X_CFUD, cfud | 0x01);
else if (numcoef == 5)
snd_soc_write(codec, STA32X_CFUD, cfud | 0x02);
else
return -EINVAL;
return 0;
}
#define SINGLE_COEF(xname, index) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = sta32x_coefficient_info, \
.get = sta32x_coefficient_get,\
.put = sta32x_coefficient_put, \
.private_value = index | (1 << 16) }
#define BIQUAD_COEFS(xname, index) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = sta32x_coefficient_info, \
.get = sta32x_coefficient_get,\
.put = sta32x_coefficient_put, \
.private_value = index | (5 << 16) }
static const struct snd_kcontrol_new sta32x_snd_controls[] = {
SOC_SINGLE_TLV("Master Volume", STA32X_MVOL, 0, 0xff, 1, mvol_tlv),
SOC_SINGLE("Master Switch", STA32X_MMUTE, 0, 1, 1),
SOC_SINGLE("Ch1 Switch", STA32X_MMUTE, 1, 1, 1),
SOC_SINGLE("Ch2 Switch", STA32X_MMUTE, 2, 1, 1),
SOC_SINGLE("Ch3 Switch", STA32X_MMUTE, 3, 1, 1),
SOC_SINGLE_TLV("Ch1 Volume", STA32X_C1VOL, 0, 0xff, 1, chvol_tlv),
SOC_SINGLE_TLV("Ch2 Volume", STA32X_C2VOL, 0, 0xff, 1, chvol_tlv),
SOC_SINGLE_TLV("Ch3 Volume", STA32X_C3VOL, 0, 0xff, 1, chvol_tlv),
SOC_SINGLE("De-emphasis Filter Switch", STA32X_CONFD, STA32X_CONFD_DEMP_SHIFT, 1, 0),
SOC_ENUM("Compressor/Limiter Switch", sta32x_drc_ac_enum),
SOC_SINGLE("Miami Mode Switch", STA32X_CONFD, STA32X_CONFD_MME_SHIFT, 1, 0),
SOC_SINGLE("Zero Cross Switch", STA32X_CONFE, STA32X_CONFE_ZCE_SHIFT, 1, 0),
SOC_SINGLE("Soft Ramp Switch", STA32X_CONFE, STA32X_CONFE_SVE_SHIFT, 1, 0),
SOC_SINGLE("Auto-Mute Switch", STA32X_CONFF, STA32X_CONFF_IDE_SHIFT, 1, 0),
SOC_ENUM("Automode EQ", sta32x_auto_eq_enum),
SOC_ENUM("Automode GC", sta32x_auto_gc_enum),
SOC_ENUM("Automode XO", sta32x_auto_xo_enum),
SOC_ENUM("Preset EQ", sta32x_preset_eq_enum),
SOC_SINGLE("Ch1 Tone Control Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_TCB_SHIFT, 1, 0),
SOC_SINGLE("Ch2 Tone Control Bypass Switch", STA32X_C2CFG, STA32X_CxCFG_TCB_SHIFT, 1, 0),
SOC_SINGLE("Ch1 EQ Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_EQBP_SHIFT, 1, 0),
SOC_SINGLE("Ch2 EQ Bypass Switch", STA32X_C2CFG, STA32X_CxCFG_EQBP_SHIFT, 1, 0),
SOC_SINGLE("Ch1 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0),
SOC_SINGLE("Ch2 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0),
SOC_SINGLE("Ch3 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0),
SOC_ENUM("Ch1 Limiter Select", sta32x_limiter_ch1_enum),
SOC_ENUM("Ch2 Limiter Select", sta32x_limiter_ch2_enum),
SOC_ENUM("Ch3 Limiter Select", sta32x_limiter_ch3_enum),
SOC_SINGLE_TLV("Bass Tone Control", STA32X_TONE, STA32X_TONE_BTC_SHIFT, 15, 0, tone_tlv),
SOC_SINGLE_TLV("Treble Tone Control", STA32X_TONE, STA32X_TONE_TTC_SHIFT, 15, 0, tone_tlv),
SOC_ENUM("Limiter1 Attack Rate (dB/ms)", sta32x_limiter1_attack_rate_enum),
SOC_ENUM("Limiter2 Attack Rate (dB/ms)", sta32x_limiter2_attack_rate_enum),
SOC_ENUM("Limiter1 Release Rate (dB/ms)", sta32x_limiter1_release_rate_enum),
SOC_ENUM("Limiter2 Release Rate (dB/ms)", sta32x_limiter1_release_rate_enum),
/* depending on mode, the attack/release thresholds have
* two different enum definitions; provide both
*/
SOC_SINGLE_TLV("Limiter1 Attack Threshold (AC Mode)", STA32X_L1ATRT, STA32X_LxA_SHIFT,
16, 0, sta32x_limiter_ac_attack_tlv),
SOC_SINGLE_TLV("Limiter2 Attack Threshold (AC Mode)", STA32X_L2ATRT, STA32X_LxA_SHIFT,
16, 0, sta32x_limiter_ac_attack_tlv),
SOC_SINGLE_TLV("Limiter1 Release Threshold (AC Mode)", STA32X_L1ATRT, STA32X_LxR_SHIFT,
16, 0, sta32x_limiter_ac_release_tlv),
SOC_SINGLE_TLV("Limiter2 Release Threshold (AC Mode)", STA32X_L2ATRT, STA32X_LxR_SHIFT,
16, 0, sta32x_limiter_ac_release_tlv),
SOC_SINGLE_TLV("Limiter1 Attack Threshold (DRC Mode)", STA32X_L1ATRT, STA32X_LxA_SHIFT,
16, 0, sta32x_limiter_drc_attack_tlv),
SOC_SINGLE_TLV("Limiter2 Attack Threshold (DRC Mode)", STA32X_L2ATRT, STA32X_LxA_SHIFT,
16, 0, sta32x_limiter_drc_attack_tlv),
SOC_SINGLE_TLV("Limiter1 Release Threshold (DRC Mode)", STA32X_L1ATRT, STA32X_LxR_SHIFT,
16, 0, sta32x_limiter_drc_release_tlv),
SOC_SINGLE_TLV("Limiter2 Release Threshold (DRC Mode)", STA32X_L2ATRT, STA32X_LxR_SHIFT,
16, 0, sta32x_limiter_drc_release_tlv),
BIQUAD_COEFS("Ch1 - Biquad 1", 0),
BIQUAD_COEFS("Ch1 - Biquad 2", 5),
BIQUAD_COEFS("Ch1 - Biquad 3", 10),
BIQUAD_COEFS("Ch1 - Biquad 4", 15),
BIQUAD_COEFS("Ch2 - Biquad 1", 20),
BIQUAD_COEFS("Ch2 - Biquad 2", 25),
BIQUAD_COEFS("Ch2 - Biquad 3", 30),
BIQUAD_COEFS("Ch2 - Biquad 4", 35),
BIQUAD_COEFS("High-pass", 40),
BIQUAD_COEFS("Low-pass", 45),
SINGLE_COEF("Ch1 - Prescale", 50),
SINGLE_COEF("Ch2 - Prescale", 51),
SINGLE_COEF("Ch1 - Postscale", 52),
SINGLE_COEF("Ch2 - Postscale", 53),
SINGLE_COEF("Ch3 - Postscale", 54),
SINGLE_COEF("Thermal warning - Postscale", 55),
SINGLE_COEF("Ch1 - Mix 1", 56),
SINGLE_COEF("Ch1 - Mix 2", 57),
SINGLE_COEF("Ch2 - Mix 1", 58),
SINGLE_COEF("Ch2 - Mix 2", 59),
SINGLE_COEF("Ch3 - Mix 1", 60),
SINGLE_COEF("Ch3 - Mix 2", 61),
};
static const struct snd_soc_dapm_widget sta32x_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_OUTPUT("LEFT"),
SND_SOC_DAPM_OUTPUT("RIGHT"),
SND_SOC_DAPM_OUTPUT("SUB"),
};
static const struct snd_soc_dapm_route sta32x_dapm_routes[] = {
{ "LEFT", NULL, "DAC" },
{ "RIGHT", NULL, "DAC" },
{ "SUB", NULL, "DAC" },
};
/* MCLK interpolation ratio per fs */
static struct {
int fs;
int ir;
} interpolation_ratios[] = {
{ 32000, 0 },
{ 44100, 0 },
{ 48000, 0 },
{ 88200, 1 },
{ 96000, 1 },
{ 176400, 2 },
{ 192000, 2 },
};
/* MCLK to fs clock ratios */
static struct {
int ratio;
int mcs;
} mclk_ratios[3][7] = {
{ { 768, 0 }, { 512, 1 }, { 384, 2 }, { 256, 3 },
{ 128, 4 }, { 576, 5 }, { 0, 0 } },
{ { 384, 2 }, { 256, 3 }, { 192, 4 }, { 128, 5 }, {64, 0 }, { 0, 0 } },
{ { 384, 2 }, { 256, 3 }, { 192, 4 }, { 128, 5 }, {64, 0 }, { 0, 0 } },
};
/**
* sta32x_set_dai_sysclk - configure MCLK
* @codec_dai: the codec DAI
* @clk_id: the clock ID (ignored)
* @freq: the MCLK input frequency
* @dir: the clock direction (ignored)
*
* The value of MCLK is used to determine which sample rates are supported
* by the STA32X, based on the mclk_ratios table.
*
* This function must be called by the machine driver's 'startup' function,
* otherwise the list of supported sample rates will not be available in
* time for ALSA.
*
* For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause
* theoretically possible sample rates to be enabled. Call it again with a
* proper value set one the external clock is set (most probably you would do
* that from a machine's driver 'hw_param' hook.
*/
static int sta32x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
int i, j, ir, fs;
unsigned int rates = 0;
unsigned int rate_min = -1;
unsigned int rate_max = 0;
pr_debug("mclk=%u\n", freq);
sta32x->mclk = freq;
if (sta32x->mclk) {
for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) {
ir = interpolation_ratios[i].ir;
fs = interpolation_ratios[i].fs;
for (j = 0; mclk_ratios[ir][j].ratio; j++) {
if (mclk_ratios[ir][j].ratio * fs == freq) {
rates |= snd_pcm_rate_to_rate_bit(fs);
if (fs < rate_min)
rate_min = fs;
if (fs > rate_max)
rate_max = fs;
}
}
}
/* FIXME: soc should support a rate list */
rates &= ~SNDRV_PCM_RATE_KNOT;
if (!rates) {
dev_err(codec->dev, "could not find a valid sample rate\n");
return -EINVAL;
}
} else {
/* enable all possible rates */
rates = STA32X_RATES;
rate_min = 32000;
rate_max = 192000;
}
codec_dai->driver->playback.rates = rates;
codec_dai->driver->playback.rate_min = rate_min;
codec_dai->driver->playback.rate_max = rate_max;
return 0;
}
/**
* sta32x_set_dai_fmt - configure the codec for the selected audio format
* @codec_dai: the codec DAI
* @fmt: a SND_SOC_DAIFMT_x value indicating the data format
*
* This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the
* codec accordingly.
*/
static int sta32x_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
u8 confb = snd_soc_read(codec, STA32X_CONFB);
pr_debug("\n");
confb &= ~(STA32X_CONFB_C1IM | STA32X_CONFB_C2IM);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
case SND_SOC_DAIFMT_RIGHT_J:
case SND_SOC_DAIFMT_LEFT_J:
sta32x->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
confb |= STA32X_CONFB_C2IM;
break;
case SND_SOC_DAIFMT_NB_IF:
confb |= STA32X_CONFB_C1IM;
break;
default:
return -EINVAL;
}
snd_soc_write(codec, STA32X_CONFB, confb);
return 0;
}
/**
* sta32x_hw_params - program the STA32X with the given hardware parameters.
* @substream: the audio stream
* @params: the hardware parameters to set
* @dai: the SOC DAI (ignored)
*
* This function programs the hardware with the values provided.
* Specifically, the sample rate and the data format.
*/
static int sta32x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
unsigned int rate;
int i, mcs = -1, ir = -1;
u8 confa, confb;
rate = params_rate(params);
pr_debug("rate: %u\n", rate);
for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++)
if (interpolation_ratios[i].fs == rate)
ir = interpolation_ratios[i].ir;
if (ir < 0)
return -EINVAL;
for (i = 0; mclk_ratios[ir][i].ratio; i++)
if (mclk_ratios[ir][i].ratio * rate == sta32x->mclk)
mcs = mclk_ratios[ir][i].mcs;
if (mcs < 0)
return -EINVAL;
confa = snd_soc_read(codec, STA32X_CONFA);
confa &= ~(STA32X_CONFA_MCS_MASK | STA32X_CONFA_IR_MASK);
confa |= (ir << STA32X_CONFA_IR_SHIFT) | (mcs << STA32X_CONFA_MCS_SHIFT);
confb = snd_soc_read(codec, STA32X_CONFB);
confb &= ~(STA32X_CONFB_SAI_MASK | STA32X_CONFB_SAIFB);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S24_LE:
case SNDRV_PCM_FORMAT_S24_BE:
case SNDRV_PCM_FORMAT_S24_3LE:
case SNDRV_PCM_FORMAT_S24_3BE:
pr_debug("24bit\n");
/* fall through */
case SNDRV_PCM_FORMAT_S32_LE:
case SNDRV_PCM_FORMAT_S32_BE:
pr_debug("24bit or 32bit\n");
switch (sta32x->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x0;
break;
case SND_SOC_DAIFMT_LEFT_J:
confb |= 0x1;
break;
case SND_SOC_DAIFMT_RIGHT_J:
confb |= 0x2;
break;
}
break;
case SNDRV_PCM_FORMAT_S20_3LE:
case SNDRV_PCM_FORMAT_S20_3BE:
pr_debug("20bit\n");
switch (sta32x->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x4;
break;
case SND_SOC_DAIFMT_LEFT_J:
confb |= 0x5;
break;
case SND_SOC_DAIFMT_RIGHT_J:
confb |= 0x6;
break;
}
break;
case SNDRV_PCM_FORMAT_S18_3LE:
case SNDRV_PCM_FORMAT_S18_3BE:
pr_debug("18bit\n");
switch (sta32x->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x8;
break;
case SND_SOC_DAIFMT_LEFT_J:
confb |= 0x9;
break;
case SND_SOC_DAIFMT_RIGHT_J:
confb |= 0xa;
break;
}
break;
case SNDRV_PCM_FORMAT_S16_LE:
case SNDRV_PCM_FORMAT_S16_BE:
pr_debug("16bit\n");
switch (sta32x->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x0;
break;
case SND_SOC_DAIFMT_LEFT_J:
confb |= 0xd;
break;
case SND_SOC_DAIFMT_RIGHT_J:
confb |= 0xe;
break;
}
break;
default:
return -EINVAL;
}
snd_soc_write(codec, STA32X_CONFA, confa);
snd_soc_write(codec, STA32X_CONFB, confb);
return 0;
}
/**
* sta32x_set_bias_level - DAPM callback
* @codec: the codec device
* @level: DAPM power level
*
* This is called by ALSA to put the codec into low power mode
* or to wake it up. If the codec is powered off completely
* all registers must be restored after power on.
*/
static int sta32x_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
int ret;
struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
pr_debug("level = %d\n", level);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
/* Full power on */
snd_soc_update_bits(codec, STA32X_CONFF,
STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
STA32X_CONFF_PWDN | STA32X_CONFF_EAPD);
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies),
sta32x->supplies);
if (ret != 0) {
dev_err(codec->dev,
"Failed to enable supplies: %d\n", ret);
return ret;
}
snd_soc_cache_sync(codec);
}
/* Power up to mute */
/* FIXME */
snd_soc_update_bits(codec, STA32X_CONFF,
STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
STA32X_CONFF_PWDN | STA32X_CONFF_EAPD);
break;
case SND_SOC_BIAS_OFF:
/* The chip runs through the power down sequence for us. */
snd_soc_update_bits(codec, STA32X_CONFF,
STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
STA32X_CONFF_PWDN);
msleep(300);
regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies),
sta32x->supplies);
break;
}
codec->dapm.bias_level = level;
return 0;
}
static struct snd_soc_dai_ops sta32x_dai_ops = {
.hw_params = sta32x_hw_params,
.set_sysclk = sta32x_set_dai_sysclk,
.set_fmt = sta32x_set_dai_fmt,
};
static struct snd_soc_dai_driver sta32x_dai = {
.name = "STA32X",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = STA32X_RATES,
.formats = STA32X_FORMATS,
},
.ops = &sta32x_dai_ops,
};
#ifdef CONFIG_PM
static int sta32x_suspend(struct snd_soc_codec *codec, pm_message_t state)
{
sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int sta32x_resume(struct snd_soc_codec *codec)
{
sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
#else
#define sta32x_suspend NULL
#define sta32x_resume NULL
#endif
static int sta32x_probe(struct snd_soc_codec *codec)
{
struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
int i, ret = 0;
sta32x->codec = codec;
/* regulators */
for (i = 0; i < ARRAY_SIZE(sta32x->supplies); i++)
sta32x->supplies[i].supply = sta32x_supply_names[i];
ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sta32x->supplies),
sta32x->supplies);
if (ret != 0) {
dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
goto err;
}
ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies),
sta32x->supplies);
if (ret != 0) {
dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
goto err_get;
}
/* Tell ASoC what kind of I/O to use to read the registers. ASoC will
* then do the I2C transactions itself.
*/
ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
if (ret < 0) {
dev_err(codec->dev, "failed to set cache I/O (ret=%i)\n", ret);
return ret;
}
/* read reg reset values into cache */
for (i = 0; i < STA32X_REGISTER_COUNT; i++)
snd_soc_cache_write(codec, i, sta32x_regs[i]);
/* preserve reset values of reserved register bits */
snd_soc_cache_write(codec, STA32X_CONFC,
codec->hw_read(codec, STA32X_CONFC));
snd_soc_cache_write(codec, STA32X_CONFE,
codec->hw_read(codec, STA32X_CONFE));
snd_soc_cache_write(codec, STA32X_CONFF,
codec->hw_read(codec, STA32X_CONFF));
snd_soc_cache_write(codec, STA32X_MMUTE,
codec->hw_read(codec, STA32X_MMUTE));
snd_soc_cache_write(codec, STA32X_AUTO1,
codec->hw_read(codec, STA32X_AUTO1));
snd_soc_cache_write(codec, STA32X_AUTO3,
codec->hw_read(codec, STA32X_AUTO3));
snd_soc_cache_write(codec, STA32X_C3CFG,
codec->hw_read(codec, STA32X_C3CFG));
/* FIXME enable thermal warning adjustment and recovery */
snd_soc_update_bits(codec, STA32X_CONFA,
STA32X_CONFA_TWAB | STA32X_CONFA_TWRB, 0);
/* FIXME select 2.1 mode */
snd_soc_update_bits(codec, STA32X_CONFF,
STA32X_CONFF_OCFG_MASK,
1 << STA32X_CONFF_OCFG_SHIFT);
/* FIXME channel to output mapping */
snd_soc_update_bits(codec, STA32X_C1CFG,
STA32X_CxCFG_OM_MASK,
0 << STA32X_CxCFG_OM_SHIFT);
snd_soc_update_bits(codec, STA32X_C2CFG,
STA32X_CxCFG_OM_MASK,
1 << STA32X_CxCFG_OM_SHIFT);
snd_soc_update_bits(codec, STA32X_C3CFG,
STA32X_CxCFG_OM_MASK,
2 << STA32X_CxCFG_OM_SHIFT);
sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Bias level configuration will have done an extra enable */
regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
return 0;
err_get:
regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
err:
return ret;
}
static int sta32x_remove(struct snd_soc_codec *codec)
{
struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
return 0;
}
static int sta32x_reg_is_volatile(struct snd_soc_codec *codec,
unsigned int reg)
{
switch (reg) {
case STA32X_CONFA ... STA32X_L2ATRT:
case STA32X_MPCC1 ... STA32X_FDRC2:
return 0;
}
return 1;
}
static const struct snd_soc_codec_driver sta32x_codec = {
.probe = sta32x_probe,
.remove = sta32x_remove,
.suspend = sta32x_suspend,
.resume = sta32x_resume,
.reg_cache_size = STA32X_REGISTER_COUNT,
.reg_word_size = sizeof(u8),
.volatile_register = sta32x_reg_is_volatile,
.set_bias_level = sta32x_set_bias_level,
.controls = sta32x_snd_controls,
.num_controls = ARRAY_SIZE(sta32x_snd_controls),
.dapm_widgets = sta32x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(sta32x_dapm_widgets),
.dapm_routes = sta32x_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(sta32x_dapm_routes),
};
static __devinit int sta32x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct sta32x_priv *sta32x;
int ret;
sta32x = kzalloc(sizeof(struct sta32x_priv), GFP_KERNEL);
if (!sta32x)
return -ENOMEM;
i2c_set_clientdata(i2c, sta32x);
ret = snd_soc_register_codec(&i2c->dev, &sta32x_codec, &sta32x_dai, 1);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to register codec (%d)\n", ret);
return ret;
}
return 0;
}
static __devexit int sta32x_i2c_remove(struct i2c_client *client)
{
struct sta32x_priv *sta32x = i2c_get_clientdata(client);
struct snd_soc_codec *codec = sta32x->codec;
if (codec)
sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF);
regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
if (codec) {
snd_soc_unregister_codec(&client->dev);
snd_soc_codec_set_drvdata(codec, NULL);
}
kfree(sta32x);
return 0;
}
static const struct i2c_device_id sta32x_i2c_id[] = {
{ "sta326", 0 },
{ "sta328", 0 },
{ "sta329", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, sta32x_i2c_id);
static struct i2c_driver sta32x_i2c_driver = {
.driver = {
.name = "sta32x",
.owner = THIS_MODULE,
},
.probe = sta32x_i2c_probe,
.remove = __devexit_p(sta32x_i2c_remove),
.id_table = sta32x_i2c_id,
};
static int __init sta32x_init(void)
{
return i2c_add_driver(&sta32x_i2c_driver);
}
module_init(sta32x_init);
static void __exit sta32x_exit(void)
{
i2c_del_driver(&sta32x_i2c_driver);
}
module_exit(sta32x_exit);
MODULE_DESCRIPTION("ASoC STA32X driver");
MODULE_AUTHOR("Johannes Stezenbach <js@sig21.net>");
MODULE_LICENSE("GPL");

210
sound/soc/codecs/sta32x.h Normal file
View File

@ -0,0 +1,210 @@
/*
* Codec driver for ST STA32x 2.1-channel high-efficiency digital audio system
*
* Copyright: 2011 Raumfeld GmbH
* Author: Johannes Stezenbach <js@sig21.net>
*
* based on code from:
* Wolfson Microelectronics PLC.
* Mark Brown <broonie@opensource.wolfsonmicro.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.
*/
#ifndef _ASOC_STA_32X_H
#define _ASOC_STA_32X_H
/* STA326 register addresses */
#define STA32X_REGISTER_COUNT 0x2d
#define STA32X_CONFA 0x00
#define STA32X_CONFB 0x01
#define STA32X_CONFC 0x02
#define STA32X_CONFD 0x03
#define STA32X_CONFE 0x04
#define STA32X_CONFF 0x05
#define STA32X_MMUTE 0x06
#define STA32X_MVOL 0x07
#define STA32X_C1VOL 0x08
#define STA32X_C2VOL 0x09
#define STA32X_C3VOL 0x0a
#define STA32X_AUTO1 0x0b
#define STA32X_AUTO2 0x0c
#define STA32X_AUTO3 0x0d
#define STA32X_C1CFG 0x0e
#define STA32X_C2CFG 0x0f
#define STA32X_C3CFG 0x10
#define STA32X_TONE 0x11
#define STA32X_L1AR 0x12
#define STA32X_L1ATRT 0x13
#define STA32X_L2AR 0x14
#define STA32X_L2ATRT 0x15
#define STA32X_CFADDR2 0x16
#define STA32X_B1CF1 0x17
#define STA32X_B1CF2 0x18
#define STA32X_B1CF3 0x19
#define STA32X_B2CF1 0x1a
#define STA32X_B2CF2 0x1b
#define STA32X_B2CF3 0x1c
#define STA32X_A1CF1 0x1d
#define STA32X_A1CF2 0x1e
#define STA32X_A1CF3 0x1f
#define STA32X_A2CF1 0x20
#define STA32X_A2CF2 0x21
#define STA32X_A2CF3 0x22
#define STA32X_B0CF1 0x23
#define STA32X_B0CF2 0x24
#define STA32X_B0CF3 0x25
#define STA32X_CFUD 0x26
#define STA32X_MPCC1 0x27
#define STA32X_MPCC2 0x28
/* Reserved 0x29 */
/* Reserved 0x2a */
#define STA32X_Reserved 0x2a
#define STA32X_FDRC1 0x2b
#define STA32X_FDRC2 0x2c
/* Reserved 0x2d */
/* STA326 register field definitions */
/* 0x00 CONFA */
#define STA32X_CONFA_MCS_MASK 0x03
#define STA32X_CONFA_MCS_SHIFT 0
#define STA32X_CONFA_IR_MASK 0x18
#define STA32X_CONFA_IR_SHIFT 3
#define STA32X_CONFA_TWRB 0x20
#define STA32X_CONFA_TWAB 0x40
#define STA32X_CONFA_FDRB 0x80
/* 0x01 CONFB */
#define STA32X_CONFB_SAI_MASK 0x0f
#define STA32X_CONFB_SAI_SHIFT 0
#define STA32X_CONFB_SAIFB 0x10
#define STA32X_CONFB_DSCKE 0x20
#define STA32X_CONFB_C1IM 0x40
#define STA32X_CONFB_C2IM 0x80
/* 0x02 CONFC */
#define STA32X_CONFC_OM_MASK 0x03
#define STA32X_CONFC_OM_SHIFT 0
#define STA32X_CONFC_CSZ_MASK 0x7c
#define STA32X_CONFC_CSZ_SHIFT 2
/* 0x03 CONFD */
#define STA32X_CONFD_HPB 0x01
#define STA32X_CONFD_HPB_SHIFT 0
#define STA32X_CONFD_DEMP 0x02
#define STA32X_CONFD_DEMP_SHIFT 1
#define STA32X_CONFD_DSPB 0x04
#define STA32X_CONFD_DSPB_SHIFT 2
#define STA32X_CONFD_PSL 0x08
#define STA32X_CONFD_PSL_SHIFT 3
#define STA32X_CONFD_BQL 0x10
#define STA32X_CONFD_BQL_SHIFT 4
#define STA32X_CONFD_DRC 0x20
#define STA32X_CONFD_DRC_SHIFT 5
#define STA32X_CONFD_ZDE 0x40
#define STA32X_CONFD_ZDE_SHIFT 6
#define STA32X_CONFD_MME 0x80
#define STA32X_CONFD_MME_SHIFT 7
/* 0x04 CONFE */
#define STA32X_CONFE_MPCV 0x01
#define STA32X_CONFE_MPCV_SHIFT 0
#define STA32X_CONFE_MPC 0x02
#define STA32X_CONFE_MPC_SHIFT 1
#define STA32X_CONFE_AME 0x08
#define STA32X_CONFE_AME_SHIFT 3
#define STA32X_CONFE_PWMS 0x10
#define STA32X_CONFE_PWMS_SHIFT 4
#define STA32X_CONFE_ZCE 0x40
#define STA32X_CONFE_ZCE_SHIFT 6
#define STA32X_CONFE_SVE 0x80
#define STA32X_CONFE_SVE_SHIFT 7
/* 0x05 CONFF */
#define STA32X_CONFF_OCFG_MASK 0x03
#define STA32X_CONFF_OCFG_SHIFT 0
#define STA32X_CONFF_IDE 0x04
#define STA32X_CONFF_IDE_SHIFT 3
#define STA32X_CONFF_BCLE 0x08
#define STA32X_CONFF_ECLE 0x20
#define STA32X_CONFF_PWDN 0x40
#define STA32X_CONFF_EAPD 0x80
/* 0x06 MMUTE */
#define STA32X_MMUTE_MMUTE 0x01
/* 0x0b AUTO1 */
#define STA32X_AUTO1_AMEQ_MASK 0x03
#define STA32X_AUTO1_AMEQ_SHIFT 0
#define STA32X_AUTO1_AMV_MASK 0xc0
#define STA32X_AUTO1_AMV_SHIFT 2
#define STA32X_AUTO1_AMGC_MASK 0x30
#define STA32X_AUTO1_AMGC_SHIFT 4
#define STA32X_AUTO1_AMPS 0x80
/* 0x0c AUTO2 */
#define STA32X_AUTO2_AMAME 0x01
#define STA32X_AUTO2_AMAM_MASK 0x0e
#define STA32X_AUTO2_AMAM_SHIFT 1
#define STA32X_AUTO2_XO_MASK 0xf0
#define STA32X_AUTO2_XO_SHIFT 4
/* 0x0d AUTO3 */
#define STA32X_AUTO3_PEQ_MASK 0x1f
#define STA32X_AUTO3_PEQ_SHIFT 0
/* 0x0e 0x0f 0x10 CxCFG */
#define STA32X_CxCFG_TCB 0x01 /* only C1 and C2 */
#define STA32X_CxCFG_TCB_SHIFT 0
#define STA32X_CxCFG_EQBP 0x02 /* only C1 and C2 */
#define STA32X_CxCFG_EQBP_SHIFT 1
#define STA32X_CxCFG_VBP 0x03
#define STA32X_CxCFG_VBP_SHIFT 2
#define STA32X_CxCFG_BO 0x04
#define STA32X_CxCFG_LS_MASK 0x30
#define STA32X_CxCFG_LS_SHIFT 4
#define STA32X_CxCFG_OM_MASK 0xc0
#define STA32X_CxCFG_OM_SHIFT 6
/* 0x11 TONE */
#define STA32X_TONE_BTC_SHIFT 0
#define STA32X_TONE_TTC_SHIFT 4
/* 0x12 0x13 0x14 0x15 limiter attack/release */
#define STA32X_LxA_SHIFT 0
#define STA32X_LxR_SHIFT 4
/* 0x26 CFUD */
#define STA32X_CFUD_W1 0x01
#define STA32X_CFUD_WA 0x02
#define STA32X_CFUD_R1 0x04
#define STA32X_CFUD_RA 0x08
/* biquad filter coefficient table offsets */
#define STA32X_C1_BQ_BASE 0
#define STA32X_C2_BQ_BASE 20
#define STA32X_CH_BQ_NUM 4
#define STA32X_BQ_NUM_COEF 5
#define STA32X_XO_HP_BQ_BASE 40
#define STA32X_XO_LP_BQ_BASE 45
#define STA32X_C1_PRESCALE 50
#define STA32X_C2_PRESCALE 51
#define STA32X_C1_POSTSCALE 52
#define STA32X_C2_POSTSCALE 53
#define STA32X_C3_POSTSCALE 54
#define STA32X_TW_POSTSCALE 55
#define STA32X_C1_MIX1 56
#define STA32X_C1_MIX2 57
#define STA32X_C2_MIX1 58
#define STA32X_C2_MIX2 59
#define STA32X_C3_MIX1 60
#define STA32X_C3_MIX2 61
#endif /* _ASOC_STA_32X_H */

View File

@ -226,11 +226,13 @@ static const char *aic3x_adc_hpf[] =
#define RDAC_ENUM 1
#define LHPCOM_ENUM 2
#define RHPCOM_ENUM 3
#define LINE1L_ENUM 4
#define LINE1R_ENUM 5
#define LINE2L_ENUM 6
#define LINE2R_ENUM 7
#define ADC_HPF_ENUM 8
#define LINE1L_2_L_ENUM 4
#define LINE1L_2_R_ENUM 5
#define LINE1R_2_L_ENUM 6
#define LINE1R_2_R_ENUM 7
#define LINE2L_ENUM 8
#define LINE2R_ENUM 9
#define ADC_HPF_ENUM 10
static const struct soc_enum aic3x_enum[] = {
SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux),
@ -238,6 +240,8 @@ static const struct soc_enum aic3x_enum[] = {
SOC_ENUM_SINGLE(HPLCOM_CFG, 4, 3, aic3x_left_hpcom_mux),
SOC_ENUM_SINGLE(HPRCOM_CFG, 3, 5, aic3x_right_hpcom_mux),
SOC_ENUM_SINGLE(LINE1L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
SOC_ENUM_SINGLE(LINE1L_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
SOC_ENUM_SINGLE(LINE1R_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
SOC_ENUM_SINGLE(LINE1R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
@ -490,12 +494,16 @@ static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = {
};
/* Left Line1 Mux */
static const struct snd_kcontrol_new aic3x_left_line1_mux_controls =
SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_ENUM]);
static const struct snd_kcontrol_new aic3x_left_line1l_mux_controls =
SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_2_L_ENUM]);
static const struct snd_kcontrol_new aic3x_right_line1l_mux_controls =
SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_2_R_ENUM]);
/* Right Line1 Mux */
static const struct snd_kcontrol_new aic3x_right_line1_mux_controls =
SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_ENUM]);
static const struct snd_kcontrol_new aic3x_right_line1r_mux_controls =
SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_2_R_ENUM]);
static const struct snd_kcontrol_new aic3x_left_line1r_mux_controls =
SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_2_L_ENUM]);
/* Left Line2 Mux */
static const struct snd_kcontrol_new aic3x_left_line2_mux_controls =
@ -535,9 +543,9 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
&aic3x_left_pga_mixer_controls[0],
ARRAY_SIZE(aic3x_left_pga_mixer_controls)),
SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0,
&aic3x_left_line1_mux_controls),
&aic3x_left_line1l_mux_controls),
SND_SOC_DAPM_MUX("Left Line1R Mux", SND_SOC_NOPM, 0, 0,
&aic3x_left_line1_mux_controls),
&aic3x_left_line1r_mux_controls),
SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0,
&aic3x_left_line2_mux_controls),
@ -548,9 +556,9 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
&aic3x_right_pga_mixer_controls[0],
ARRAY_SIZE(aic3x_right_pga_mixer_controls)),
SND_SOC_DAPM_MUX("Right Line1L Mux", SND_SOC_NOPM, 0, 0,
&aic3x_right_line1_mux_controls),
&aic3x_right_line1l_mux_controls),
SND_SOC_DAPM_MUX("Right Line1R Mux", SND_SOC_NOPM, 0, 0,
&aic3x_right_line1_mux_controls),
&aic3x_right_line1r_mux_controls),
SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,
&aic3x_right_line2_mux_controls),

View File

@ -954,9 +954,9 @@ static DECLARE_TLV_DB_SCALE(mic_preamp_tlv, -600, 600, 0);
/*
* MICGAIN volume control:
* from -6 to 30 dB in 6 dB steps
* from 6 to 30 dB in 6 dB steps
*/
static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -600, 600, 0);
static DECLARE_TLV_DB_SCALE(mic_amp_tlv, 600, 600, 0);
/*
* AFMGAIN volume control:

80
sound/soc/codecs/wm8782.c Normal file
View File

@ -0,0 +1,80 @@
/*
* sound/soc/codecs/wm8782.c
* simple, strap-pin configured 24bit 2ch ADC
*
* Copyright: 2011 Raumfeld GmbH
* Author: Johannes Stezenbach <js@sig21.net>
*
* based on ad73311.c
* Copyright: Analog Device Inc.
* Author: Cliff Cai <cliff.cai@analog.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 <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
static struct snd_soc_dai_driver wm8782_dai = {
.name = "wm8782",
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
/* For configurations with FSAMPEN=0 */
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
SNDRV_PCM_FMTBIT_S24_LE,
},
};
static struct snd_soc_codec_driver soc_codec_dev_wm8782;
static __devinit int wm8782_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_wm8782, &wm8782_dai, 1);
}
static int __devexit wm8782_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver wm8782_codec_driver = {
.driver = {
.name = "wm8782",
.owner = THIS_MODULE,
},
.probe = wm8782_probe,
.remove = wm8782_remove,
};
static int __init wm8782_init(void)
{
return platform_driver_register(&wm8782_codec_driver);
}
module_init(wm8782_init);
static void __exit wm8782_exit(void)
{
platform_driver_unregister(&wm8782_codec_driver);
}
module_exit(wm8782_exit);
MODULE_DESCRIPTION("ASoC WM8782 driver");
MODULE_AUTHOR("Johannes Stezenbach <js@sig21.net>");
MODULE_LICENSE("GPL");

View File

@ -1167,6 +1167,7 @@ static int wm8900_resume(struct snd_soc_codec *codec)
ret = wm8900_set_fll(codec, 0, fll_in, fll_out);
if (ret != 0) {
dev_err(codec->dev, "Failed to restart FLL\n");
kfree(cache);
return ret;
}
}

View File

@ -2560,6 +2560,7 @@ static __devexit int wm8904_i2c_remove(struct i2c_client *client)
static const struct i2c_device_id wm8904_i2c_id[] = {
{ "wm8904", WM8904 },
{ "wm8912", WM8912 },
{ "wm8918", WM8904 }, /* Actually a subset, updates to follow */
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8904_i2c_id);

View File

@ -41,14 +41,12 @@
#define HPOUT2L 4
#define HPOUT2R 8
#define WM8915_NUM_SUPPLIES 6
#define WM8915_NUM_SUPPLIES 4
static const char *wm8915_supply_names[WM8915_NUM_SUPPLIES] = {
"DCVDD",
"DBVDD",
"AVDD1",
"AVDD2",
"CPVDD",
"MICVDD",
};
struct wm8915_priv {
@ -57,6 +55,7 @@ struct wm8915_priv {
int ldo1ena;
int sysclk;
int sysclk_src;
int fll_src;
int fll_fref;
@ -76,6 +75,7 @@ struct wm8915_priv {
struct wm8915_pdata pdata;
int rx_rate[WM8915_AIFS];
int bclk_rate[WM8915_AIFS];
/* Platform dependant ReTune mobile configuration */
int num_retune_mobile_texts;
@ -113,8 +113,6 @@ WM8915_REGULATOR_EVENT(0)
WM8915_REGULATOR_EVENT(1)
WM8915_REGULATOR_EVENT(2)
WM8915_REGULATOR_EVENT(3)
WM8915_REGULATOR_EVENT(4)
WM8915_REGULATOR_EVENT(5)
static const u16 wm8915_reg[WM8915_MAX_REGISTER] = {
[WM8915_SOFTWARE_RESET] = 0x8915,
@ -1565,6 +1563,50 @@ static int wm8915_reset(struct snd_soc_codec *codec)
return snd_soc_write(codec, WM8915_SOFTWARE_RESET, 0x8915);
}
static const int bclk_divs[] = {
1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96
};
static void wm8915_update_bclk(struct snd_soc_codec *codec)
{
struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
int aif, best, cur_val, bclk_rate, bclk_reg, i;
/* Don't bother if we're in a low frequency idle mode that
* can't support audio.
*/
if (wm8915->sysclk < 64000)
return;
for (aif = 0; aif < WM8915_AIFS; aif++) {
switch (aif) {
case 0:
bclk_reg = WM8915_AIF1_BCLK;
break;
case 1:
bclk_reg = WM8915_AIF2_BCLK;
break;
}
bclk_rate = wm8915->bclk_rate[aif];
/* Pick a divisor for BCLK as close as we can get to ideal */
best = 0;
for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
cur_val = (wm8915->sysclk / bclk_divs[i]) - bclk_rate;
if (cur_val < 0) /* BCLK table is sorted */
break;
best = i;
}
bclk_rate = wm8915->sysclk / bclk_divs[best];
dev_dbg(codec->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
bclk_divs[best], bclk_rate);
snd_soc_update_bits(codec, bclk_reg,
WM8915_AIF1_BCLK_DIV_MASK, best);
}
}
static int wm8915_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
@ -1717,10 +1759,6 @@ static int wm8915_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
static const int bclk_divs[] = {
1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96
};
static const int dsp_divs[] = {
48000, 32000, 16000, 8000
};
@ -1731,17 +1769,11 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_codec *codec = dai->codec;
struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
int bits, i, bclk_rate, best, cur_val;
int bits, i, bclk_rate;
int aifdata = 0;
int bclk = 0;
int lrclk = 0;
int dsp = 0;
int aifdata_reg, bclk_reg, lrclk_reg, dsp_shift;
if (!wm8915->sysclk) {
dev_err(codec->dev, "SYSCLK not configured\n");
return -EINVAL;
}
int aifdata_reg, lrclk_reg, dsp_shift;
switch (dai->id) {
case 0:
@ -1753,7 +1785,6 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
aifdata_reg = WM8915_AIF1TX_DATA_CONFIGURATION_1;
lrclk_reg = WM8915_AIF1_TX_LRCLK_1;
}
bclk_reg = WM8915_AIF1_BCLK;
dsp_shift = 0;
break;
case 1:
@ -1765,7 +1796,6 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
aifdata_reg = WM8915_AIF2TX_DATA_CONFIGURATION_1;
lrclk_reg = WM8915_AIF2_TX_LRCLK_1;
}
bclk_reg = WM8915_AIF2_BCLK;
dsp_shift = WM8915_DSP2_DIV_SHIFT;
break;
default:
@ -1779,6 +1809,9 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
return bclk_rate;
}
wm8915->bclk_rate[dai->id] = bclk_rate;
wm8915->rx_rate[dai->id] = params_rate(params);
/* Needs looking at for TDM */
bits = snd_pcm_format_width(params_format(params));
if (bits < 0)
@ -1796,18 +1829,7 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
}
dsp |= i << dsp_shift;
/* Pick a divisor for BCLK as close as we can get to ideal */
best = 0;
for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
cur_val = (wm8915->sysclk / bclk_divs[i]) - bclk_rate;
if (cur_val < 0) /* BCLK table is sorted */
break;
best = i;
}
bclk_rate = wm8915->sysclk / bclk_divs[best];
dev_dbg(dai->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
bclk_divs[best], bclk_rate);
bclk |= best;
wm8915_update_bclk(codec);
lrclk = bclk_rate / params_rate(params);
dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
@ -1817,14 +1839,11 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
WM8915_AIF1TX_WL_MASK |
WM8915_AIF1TX_SLOT_LEN_MASK,
aifdata);
snd_soc_update_bits(codec, bclk_reg, WM8915_AIF1_BCLK_DIV_MASK, bclk);
snd_soc_update_bits(codec, lrclk_reg, WM8915_AIF1RX_RATE_MASK,
lrclk);
snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_2,
WM8915_DSP1_DIV_SHIFT << dsp_shift, dsp);
wm8915->rx_rate[dai->id] = params_rate(params);
return 0;
}
@ -1838,6 +1857,9 @@ static int wm8915_set_sysclk(struct snd_soc_dai *dai,
int src;
int old;
if (freq == wm8915->sysclk && clk_id == wm8915->sysclk_src)
return 0;
/* Disable SYSCLK while we reconfigure */
old = snd_soc_read(codec, WM8915_AIF_CLOCKING_1) & WM8915_SYSCLK_ENA;
snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
@ -1882,6 +1904,8 @@ static int wm8915_set_sysclk(struct snd_soc_dai *dai,
return -EINVAL;
}
wm8915_update_bclk(codec);
snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
WM8915_SYSCLK_SRC_MASK | WM8915_SYSCLK_DIV_MASK,
src << WM8915_SYSCLK_SRC_SHIFT | ratediv);
@ -1889,6 +1913,8 @@ static int wm8915_set_sysclk(struct snd_soc_dai *dai,
snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
WM8915_SYSCLK_ENA, old);
wm8915->sysclk_src = clk_id;
return 0;
}
@ -2007,6 +2033,7 @@ static int wm8915_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
unsigned int Fref, unsigned int Fout)
{
struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *i2c = to_i2c_client(codec->dev);
struct _fll_div fll_div;
unsigned long timeout;
int ret, reg;
@ -2093,7 +2120,18 @@ static int wm8915_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
else
timeout = msecs_to_jiffies(2);
wait_for_completion_timeout(&wm8915->fll_lock, timeout);
/* Allow substantially longer if we've actually got the IRQ */
if (i2c->irq)
timeout *= 1000;
ret = wait_for_completion_timeout(&wm8915->fll_lock, timeout);
if (ret == 0 && i2c->irq) {
dev_err(codec->dev, "Timed out waiting for FLL\n");
ret = -ETIMEDOUT;
} else {
ret = 0;
}
dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
@ -2101,7 +2139,7 @@ static int wm8915_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
wm8915->fll_fout = Fout;
wm8915->fll_src = source;
return 0;
return ret;
}
#ifdef CONFIG_GPIOLIB
@ -2293,6 +2331,12 @@ static void wm8915_micd(struct snd_soc_codec *codec)
SND_JACK_HEADSET | SND_JACK_BTN_0);
wm8915->jack_mic = true;
wm8915->detecting = false;
/* Increase poll rate to give better responsiveness
* for buttons */
snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
WM8915_MICD_RATE_MASK,
5 << WM8915_MICD_RATE_SHIFT);
}
/* If we detected a lower impedence during initial startup
@ -2333,15 +2377,17 @@ static void wm8915_micd(struct snd_soc_codec *codec)
SND_JACK_HEADPHONE,
SND_JACK_HEADSET |
SND_JACK_BTN_0);
/* Increase the detection rate a bit for
* responsiveness.
*/
snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
WM8915_MICD_RATE_MASK,
7 << WM8915_MICD_RATE_SHIFT);
wm8915->detecting = false;
}
}
/* Increase poll rate to give better responsiveness for buttons */
if (!wm8915->detecting)
snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
WM8915_MICD_RATE_MASK,
5 << WM8915_MICD_RATE_SHIFT);
}
static irqreturn_t wm8915_irq(int irq, void *data)
@ -2383,6 +2429,20 @@ static irqreturn_t wm8915_irq(int irq, void *data)
}
}
static irqreturn_t wm8915_edge_irq(int irq, void *data)
{
irqreturn_t ret = IRQ_NONE;
irqreturn_t val;
do {
val = wm8915_irq(irq, data);
if (val != IRQ_NONE)
ret = val;
} while (val != IRQ_NONE);
return ret;
}
static void wm8915_retune_mobile_pdata(struct snd_soc_codec *codec)
{
struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
@ -2482,8 +2542,6 @@ static int wm8915_probe(struct snd_soc_codec *codec)
wm8915->disable_nb[1].notifier_call = wm8915_regulator_event_1;
wm8915->disable_nb[2].notifier_call = wm8915_regulator_event_2;
wm8915->disable_nb[3].notifier_call = wm8915_regulator_event_3;
wm8915->disable_nb[4].notifier_call = wm8915_regulator_event_4;
wm8915->disable_nb[5].notifier_call = wm8915_regulator_event_5;
/* This should really be moved into the regulator core */
for (i = 0; i < ARRAY_SIZE(wm8915->supplies); i++) {
@ -2709,8 +2767,14 @@ static int wm8915_probe(struct snd_soc_codec *codec)
irq_flags |= IRQF_ONESHOT;
ret = request_threaded_irq(i2c->irq, NULL, wm8915_irq,
irq_flags, "wm8915", codec);
if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))
ret = request_threaded_irq(i2c->irq, NULL,
wm8915_edge_irq,
irq_flags, "wm8915", codec);
else
ret = request_threaded_irq(i2c->irq, NULL, wm8915_irq,
irq_flags, "wm8915", codec);
if (ret == 0) {
/* Unmask the interrupt */
snd_soc_update_bits(codec, WM8915_INTERRUPT_CONTROL,

View File

@ -297,8 +297,6 @@ static int wm8940_add_widgets(struct snd_soc_codec *codec)
if (ret)
goto error_ret;
ret = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
if (ret)
goto error_ret;
error_ret:
return ret;
@ -683,8 +681,6 @@ static int wm8940_resume(struct snd_soc_codec *codec)
}
}
ret = wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
if (ret)
goto error_ret;
error_ret:
return ret;
@ -730,9 +726,6 @@ static int wm8940_probe(struct snd_soc_codec *codec)
if (ret)
return ret;
ret = wm8940_add_widgets(codec);
if (ret)
return ret;
return ret;
}

View File

@ -78,6 +78,8 @@ struct wm8962_priv {
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio_chip;
#endif
int irq;
};
/* We can't use the same notifier block for more than one supply and
@ -1982,6 +1984,7 @@ static const unsigned int classd_tlv[] = {
0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0),
};
static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
/* The VU bits for the headphones are in a different register to the mute
* bits and only take effect on the PGA if it is actually powered.
@ -2119,6 +2122,18 @@ SOC_SINGLE_TLV("HPMIXR MIXINR Volume", WM8962_HEADPHONE_MIXER_4,
SOC_SINGLE_TLV("Speaker Boost Volume", WM8962_CLASS_D_CONTROL_2, 0, 7, 0,
classd_tlv),
SOC_SINGLE("EQ Switch", WM8962_EQ1, WM8962_EQ_ENA_SHIFT, 1, 0),
SOC_DOUBLE_R_TLV("EQ1 Volume", WM8962_EQ2, WM8962_EQ22,
WM8962_EQL_B1_GAIN_SHIFT, 31, 0, eq_tlv),
SOC_DOUBLE_R_TLV("EQ2 Volume", WM8962_EQ2, WM8962_EQ22,
WM8962_EQL_B2_GAIN_SHIFT, 31, 0, eq_tlv),
SOC_DOUBLE_R_TLV("EQ3 Volume", WM8962_EQ2, WM8962_EQ22,
WM8962_EQL_B3_GAIN_SHIFT, 31, 0, eq_tlv),
SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23,
WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv),
SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23,
WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv),
};
static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = {
@ -2184,6 +2199,8 @@ static int sysclk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
unsigned long timeout;
int src;
int fll;
@ -2203,9 +2220,19 @@ static int sysclk_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (fll)
if (fll) {
snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1,
WM8962_FLL_ENA, WM8962_FLL_ENA);
if (wm8962->irq) {
timeout = msecs_to_jiffies(5);
timeout = wait_for_completion_timeout(&wm8962->fll_lock,
timeout);
if (timeout == 0)
dev_err(codec->dev,
"Timed out starting FLL\n");
}
}
break;
case SND_SOC_DAPM_POST_PMD:
@ -2763,18 +2790,44 @@ static const int bclk_divs[] = {
1, -1, 2, 3, 4, -1, 6, 8, -1, 12, 16, 24, -1, 32, 32, 32
};
static const int sysclk_rates[] = {
64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536,
};
static void wm8962_configure_bclk(struct snd_soc_codec *codec)
{
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
int dspclk, i;
int clocking2 = 0;
int clocking4 = 0;
int aif2 = 0;
if (!wm8962->bclk) {
dev_dbg(codec->dev, "No BCLK rate configured\n");
if (!wm8962->sysclk_rate) {
dev_dbg(codec->dev, "No SYSCLK configured\n");
return;
}
if (!wm8962->bclk || !wm8962->lrclk) {
dev_dbg(codec->dev, "No audio clocks configured\n");
return;
}
for (i = 0; i < ARRAY_SIZE(sysclk_rates); i++) {
if (sysclk_rates[i] == wm8962->sysclk_rate / wm8962->lrclk) {
clocking4 |= i << WM8962_SYSCLK_RATE_SHIFT;
break;
}
}
if (i == ARRAY_SIZE(sysclk_rates)) {
dev_err(codec->dev, "Unsupported sysclk ratio %d\n",
wm8962->sysclk_rate / wm8962->lrclk);
return;
}
snd_soc_update_bits(codec, WM8962_CLOCKING_4,
WM8962_SYSCLK_RATE_MASK, clocking4);
dspclk = snd_soc_read(codec, WM8962_CLOCKING1);
if (dspclk < 0) {
dev_err(codec->dev, "Failed to read DSPCLK: %d\n", dspclk);
@ -2844,6 +2897,8 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec,
/* VMID 2*50k */
snd_soc_update_bits(codec, WM8962_PWR_MGMT_1,
WM8962_VMID_SEL_MASK, 0x80);
wm8962_configure_bclk(codec);
break;
case SND_SOC_BIAS_STANDBY:
@ -2876,8 +2931,6 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, WM8962_CLOCKING2,
WM8962_CLKREG_OVD,
WM8962_CLKREG_OVD);
wm8962_configure_bclk(codec);
}
/* VMID 2*250k */
@ -2918,10 +2971,6 @@ static const struct {
{ 96000, 6 },
};
static const int sysclk_rates[] = {
64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536,
};
static int wm8962_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@ -2929,41 +2978,27 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
int rate = params_rate(params);
int i;
int aif0 = 0;
int adctl3 = 0;
int clocking4 = 0;
wm8962->bclk = snd_soc_params_to_bclk(params);
wm8962->lrclk = params_rate(params);
for (i = 0; i < ARRAY_SIZE(sr_vals); i++) {
if (sr_vals[i].rate == rate) {
if (sr_vals[i].rate == wm8962->lrclk) {
adctl3 |= sr_vals[i].reg;
break;
}
}
if (i == ARRAY_SIZE(sr_vals)) {
dev_err(codec->dev, "Unsupported rate %dHz\n", rate);
dev_err(codec->dev, "Unsupported rate %dHz\n", wm8962->lrclk);
return -EINVAL;
}
if (rate % 8000 == 0)
if (wm8962->lrclk % 8000 == 0)
adctl3 |= WM8962_SAMPLE_RATE_INT_MODE;
for (i = 0; i < ARRAY_SIZE(sysclk_rates); i++) {
if (sysclk_rates[i] == wm8962->sysclk_rate / rate) {
clocking4 |= i << WM8962_SYSCLK_RATE_SHIFT;
break;
}
}
if (i == ARRAY_SIZE(sysclk_rates)) {
dev_err(codec->dev, "Unsupported sysclk ratio %d\n",
wm8962->sysclk_rate / rate);
return -EINVAL;
}
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
break;
@ -2985,8 +3020,6 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_3,
WM8962_SAMPLE_RATE_INT_MODE |
WM8962_SAMPLE_RATE_MASK, adctl3);
snd_soc_update_bits(codec, WM8962_CLOCKING_4,
WM8962_SYSCLK_RATE_MASK, clocking4);
wm8962_configure_bclk(codec);
@ -3261,16 +3294,31 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
/* This should be a massive overestimate */
timeout = msecs_to_jiffies(1);
ret = 0;
wait_for_completion_timeout(&wm8962->fll_lock, timeout);
if (fll1 & WM8962_FLL_ENA) {
/* This should be a massive overestimate but go even
* higher if we'll error out
*/
if (wm8962->irq)
timeout = msecs_to_jiffies(5);
else
timeout = msecs_to_jiffies(1);
timeout = wait_for_completion_timeout(&wm8962->fll_lock,
timeout);
if (timeout == 0 && wm8962->irq) {
dev_err(codec->dev, "FLL lock timed out");
ret = -ETIMEDOUT;
}
}
wm8962->fll_fref = Fref;
wm8962->fll_fout = Fout;
wm8962->fll_src = source;
return 0;
return ret;
}
static int wm8962_mute(struct snd_soc_dai *dai, int mute)
@ -3731,8 +3779,6 @@ static int wm8962_probe(struct snd_soc_codec *codec)
int ret;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
struct wm8962_pdata *pdata = dev_get_platdata(codec->dev);
struct i2c_client *i2c = container_of(codec->dev, struct i2c_client,
dev);
u16 *reg_cache = codec->reg_cache;
int i, trigger, irq_pol;
bool dmicclk, dmicdat;
@ -3871,6 +3917,9 @@ static int wm8962_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8962_HPOUTR_VOLUME,
WM8962_HPOUT_VU, WM8962_HPOUT_VU);
/* Stereo control for EQ */
snd_soc_update_bits(codec, WM8962_EQ1, WM8962_EQ_SHARED_COEFF, 0);
wm8962_add_widgets(codec);
/* Save boards having to disable DMIC when not in use */
@ -3899,7 +3948,7 @@ static int wm8962_probe(struct snd_soc_codec *codec)
wm8962_init_beep(codec);
wm8962_init_gpio(codec);
if (i2c->irq) {
if (wm8962->irq) {
if (pdata && pdata->irq_active_low) {
trigger = IRQF_TRIGGER_LOW;
irq_pol = WM8962_IRQ_POL;
@ -3911,12 +3960,13 @@ static int wm8962_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8962_INTERRUPT_CONTROL,
WM8962_IRQ_POL, irq_pol);
ret = request_threaded_irq(i2c->irq, NULL, wm8962_irq,
ret = request_threaded_irq(wm8962->irq, NULL, wm8962_irq,
trigger | IRQF_ONESHOT,
"wm8962", codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to request IRQ %d: %d\n",
i2c->irq, ret);
wm8962->irq, ret);
wm8962->irq = 0;
/* Non-fatal */
} else {
/* Enable some IRQs by default */
@ -3941,12 +3991,10 @@ err:
static int wm8962_remove(struct snd_soc_codec *codec)
{
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *i2c = container_of(codec->dev, struct i2c_client,
dev);
int i;
if (i2c->irq)
free_irq(i2c->irq, codec);
if (wm8962->irq)
free_irq(wm8962->irq, codec);
cancel_delayed_work_sync(&wm8962->mic_work);
@ -3986,6 +4034,8 @@ static __devinit int wm8962_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8962);
wm8962->irq = i2c->irq;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8962, &wm8962_dai, 1);
if (ret < 0)

1203
sound/soc/codecs/wm8983.c Normal file

File diff suppressed because it is too large Load Diff

1029
sound/soc/codecs/wm8983.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -876,7 +876,7 @@ SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0,
left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
SND_SOC_DAPM_MIXER("SPKR", WM8993_POWER_MANAGEMENT_3, 9, 0,
right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
};
static const struct snd_soc_dapm_route routes[] = {
@ -1434,6 +1434,7 @@ static int wm8993_probe(struct snd_soc_codec *codec)
wm8993->hubs_data.hp_startup_mode = 1;
wm8993->hubs_data.dcs_codes = -2;
wm8993->hubs_data.series_startup = 1;
ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
if (ret != 0) {

View File

@ -195,10 +195,6 @@ static int configure_aif_clock(struct snd_soc_codec *codec, int aif)
aif + 1, rate);
}
if (rate && rate < 3000000)
dev_warn(codec->dev, "AIF%dCLK is %dHz, should be >=3MHz for optimal performance\n",
aif + 1, rate);
wm8994->aifclk[aif] = rate;
snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1 + offset,
@ -1146,13 +1142,33 @@ SND_SOC_DAPM_PGA_E("Late DAC2L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
late_enable_ev, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_PGA_E("Late DAC2R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
late_enable_ev, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_PGA_E("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0,
late_enable_ev, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_MIXER_E("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer),
late_enable_ev, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_MIXER_E("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer),
late_enable_ev, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux,
late_enable_ev, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux,
late_enable_ev, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev)
};
static const struct snd_soc_dapm_widget wm8994_lateclk_widgets[] = {
SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, NULL, 0)
SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
};
static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = {
@ -1190,7 +1206,6 @@ SND_SOC_DAPM_INPUT("DMIC1DAT"),
SND_SOC_DAPM_INPUT("DMIC2DAT"),
SND_SOC_DAPM_INPUT("Clock"),
SND_SOC_DAPM_MICBIAS("MICBIAS", WM8994_MICBIAS, 2, 0),
SND_SOC_DAPM_SUPPLY_S("MICBIAS Supply", 1, SND_SOC_NOPM, 0, 0, micbias_ev,
SND_SOC_DAPM_PRE_PMU),
@ -1283,14 +1298,6 @@ SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0),
SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 1, 0),
SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
SND_SOC_DAPM_POST("Debug log", post_ev),
};
@ -1509,8 +1516,10 @@ static const struct snd_soc_dapm_route wm8994_revd_intercon[] = {
{ "AIF2DACDAT", NULL, "AIF1DACDAT" },
{ "AIF1ADCDAT", NULL, "AIF2ADCDAT" },
{ "AIF2ADCDAT", NULL, "AIF1ADCDAT" },
{ "MICBIAS", NULL, "CLK_SYS" },
{ "MICBIAS", NULL, "MICBIAS Supply" },
{ "MICBIAS1", NULL, "CLK_SYS" },
{ "MICBIAS1", NULL, "MICBIAS Supply" },
{ "MICBIAS2", NULL, "CLK_SYS" },
{ "MICBIAS2", NULL, "MICBIAS Supply" },
};
static const struct snd_soc_dapm_route wm8994_intercon[] = {
@ -1623,6 +1632,7 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
int reg_offset, ret;
struct fll_div fll;
u16 reg, aif1, aif2;
unsigned long timeout;
aif1 = snd_soc_read(codec, WM8994_AIF1_CLOCKING_1)
& WM8994_AIF1CLK_ENA;
@ -1704,6 +1714,9 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
(fll.clk_ref_div << WM8994_FLL1_REFCLK_DIV_SHIFT) |
(src - 1));
/* Clear any pending completion from a previous failure */
try_wait_for_completion(&wm8994->fll_locked[id]);
/* Enable (with fractional mode if required) */
if (freq_out) {
if (fll.k)
@ -1714,7 +1727,15 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
WM8994_FLL1_ENA | WM8994_FLL1_FRAC,
reg);
msleep(5);
if (wm8994->fll_locked_irq) {
timeout = wait_for_completion_timeout(&wm8994->fll_locked[id],
msecs_to_jiffies(10));
if (timeout == 0)
dev_warn(codec->dev,
"Timed out waiting for FLL lock\n");
} else {
msleep(5);
}
}
wm8994->fll[id].in = freq_in;
@ -1732,6 +1753,14 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
return 0;
}
static irqreturn_t wm8994_fll_locked_irq(int irq, void *data)
{
struct completion *completion = data;
complete(completion);
return IRQ_HANDLED;
}
static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 };
@ -2271,6 +2300,33 @@ static int wm8994_aif3_hw_params(struct snd_pcm_substream *substream,
return snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1);
}
static void wm8994_aif_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
int rate_reg = 0;
switch (dai->id) {
case 1:
rate_reg = WM8994_AIF1_RATE;
break;
case 2:
rate_reg = WM8994_AIF1_RATE;
break;
default:
break;
}
/* If the DAI is idle then configure the divider tree for the
* lowest output rate to save a little power if the clock is
* still active (eg, because it is system clock).
*/
if (rate_reg && !dai->playback_active && !dai->capture_active)
snd_soc_update_bits(codec, rate_reg,
WM8994_AIF1_SR_MASK |
WM8994_AIF1CLK_RATE_MASK, 0x9);
}
static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute)
{
struct snd_soc_codec *codec = codec_dai->codec;
@ -2337,6 +2393,7 @@ static struct snd_soc_dai_ops wm8994_aif1_dai_ops = {
.set_sysclk = wm8994_set_dai_sysclk,
.set_fmt = wm8994_set_dai_fmt,
.hw_params = wm8994_hw_params,
.shutdown = wm8994_aif_shutdown,
.digital_mute = wm8994_aif_mute,
.set_pll = wm8994_set_fll,
.set_tristate = wm8994_set_tristate,
@ -2346,6 +2403,7 @@ static struct snd_soc_dai_ops wm8994_aif2_dai_ops = {
.set_sysclk = wm8994_set_dai_sysclk,
.set_fmt = wm8994_set_dai_fmt,
.hw_params = wm8994_hw_params,
.shutdown = wm8994_aif_shutdown,
.digital_mute = wm8994_aif_mute,
.set_pll = wm8994_set_fll,
.set_tristate = wm8994_set_tristate,
@ -2763,7 +2821,7 @@ static void wm8958_default_micdet(u16 status, void *data)
report = SND_JACK_MICROPHONE;
/* Everything else is buttons; just assign slots */
if (status & 0x1c0)
if (status & 0x1c)
report |= SND_JACK_BTN_0;
done:
@ -2849,6 +2907,15 @@ out:
return IRQ_HANDLED;
}
static irqreturn_t wm8994_fifo_error(int irq, void *data)
{
struct snd_soc_codec *codec = data;
dev_err(codec->dev, "FIFO error\n");
return IRQ_HANDLED;
}
static int wm8994_codec_probe(struct snd_soc_codec *codec)
{
struct wm8994 *control;
@ -2867,6 +2934,9 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994->pdata = dev_get_platdata(codec->dev->parent);
wm8994->codec = codec;
for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
init_completion(&wm8994->fll_locked[i]);
if (wm8994->pdata && wm8994->pdata->micdet_irq)
wm8994->micdet_irq = wm8994->pdata->micdet_irq;
else if (wm8994->pdata && wm8994->pdata->irq_base)
@ -2905,6 +2975,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994->hubs.dcs_codes = -5;
wm8994->hubs.hp_startup_mode = 1;
wm8994->hubs.dcs_readback_mode = 1;
wm8994->hubs.series_startup = 1;
break;
default:
wm8994->hubs.dcs_readback_mode = 1;
@ -2919,6 +2990,15 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
break;
}
wm8994_request_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR,
wm8994_fifo_error, "FIFO error", codec);
ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_DCS_DONE,
wm_hubs_dcs_done, "DC servo done",
&wm8994->hubs);
if (ret == 0)
wm8994->hubs.dcs_done_irq = true;
switch (control->type) {
case WM8994:
if (wm8994->micdet_irq) {
@ -2975,6 +3055,16 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
}
}
wm8994->fll_locked_irq = true;
for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) {
ret = wm8994_request_irq(codec->control_data,
WM8994_IRQ_FLL1_LOCK + i,
wm8994_fll_locked_irq, "FLL lock",
&wm8994->fll_locked[i]);
if (ret != 0)
wm8994->fll_locked_irq = false;
}
/* Remember if AIFnLRCLK is configured as a GPIO. This should be
* configured on init - if a system wants to do this dynamically
* at runtime we can deal with that then.
@ -3050,10 +3140,18 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
1 << WM8994_AIF2DAC_3D_GAIN_SHIFT,
1 << WM8994_AIF2DAC_3D_GAIN_SHIFT);
/* Unconditionally enable AIF1 ADC TDM mode; it only affects
* behaviour on idle TDM clock cycles. */
snd_soc_update_bits(codec, WM8994_AIF1_CONTROL_1,
WM8994_AIF1ADC_TDM, WM8994_AIF1ADC_TDM);
/* Unconditionally enable AIF1 ADC TDM mode on chips which can
* use this; it only affects behaviour on idle TDM clock
* cycles. */
switch (control->type) {
case WM8994:
case WM8958:
snd_soc_update_bits(codec, WM8994_AIF1_CONTROL_1,
WM8994_AIF1ADC_TDM, WM8994_AIF1ADC_TDM);
break;
default:
break;
}
wm8994_update_class_w(codec);
@ -3152,6 +3250,12 @@ err_irq:
wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, wm8994);
if (wm8994->micdet_irq)
free_irq(wm8994->micdet_irq, wm8994);
for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
wm8994_free_irq(codec->control_data, WM8994_IRQ_FLL1_LOCK + i,
&wm8994->fll_locked[i]);
wm8994_free_irq(codec->control_data, WM8994_IRQ_DCS_DONE,
&wm8994->hubs);
wm8994_free_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR, codec);
err:
kfree(wm8994);
return ret;
@ -3161,11 +3265,20 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994 *control = codec->control_data;
int i;
wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF);
pm_runtime_disable(codec->dev);
for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
wm8994_free_irq(codec->control_data, WM8994_IRQ_FLL1_LOCK + i,
&wm8994->fll_locked[i]);
wm8994_free_irq(codec->control_data, WM8994_IRQ_DCS_DONE,
&wm8994->hubs);
wm8994_free_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR, codec);
switch (control->type) {
case WM8994:
if (wm8994->micdet_irq)

View File

@ -11,6 +11,7 @@
#include <sound/soc.h>
#include <linux/firmware.h>
#include <linux/completion.h>
#include "wm_hubs.h"
@ -79,6 +80,8 @@ struct wm8994_priv {
int mclk[2];
int aifclk[2];
struct wm8994_fll_config fll[2], fll_suspend[2];
struct completion fll_locked[2];
bool fll_locked_irq;
int dac_rates[2];
int lrclk_shared[2];

View File

@ -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"),

View File

@ -63,8 +63,10 @@ static const struct soc_enum speaker_mode =
static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op)
{
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
unsigned int reg;
int count = 0;
int timeout;
unsigned int val;
val = op | WM8993_DCS_ENA_CHAN_0 | WM8993_DCS_ENA_CHAN_1;
@ -74,18 +76,39 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op)
dev_dbg(codec->dev, "Waiting for DC servo...\n");
if (hubs->dcs_done_irq)
timeout = 4;
else
timeout = 400;
do {
count++;
msleep(1);
if (hubs->dcs_done_irq)
wait_for_completion_timeout(&hubs->dcs_done,
msecs_to_jiffies(250));
else
msleep(1);
reg = snd_soc_read(codec, WM8993_DC_SERVO_0);
dev_dbg(codec->dev, "DC servo: %x\n", reg);
} while (reg & op && count < 400);
} while (reg & op && count < timeout);
if (reg & op)
dev_err(codec->dev, "Timed out waiting for DC Servo %x\n",
op);
}
irqreturn_t wm_hubs_dcs_done(int irq, void *data)
{
struct wm_hubs_data *hubs = data;
complete(&hubs->dcs_done);
return IRQ_HANDLED;
}
EXPORT_SYMBOL_GPL(wm_hubs_dcs_done);
/*
* Startup calibration of the DC servo
*/
@ -107,8 +130,7 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
return;
}
/* Devices not using a DCS code correction have startup mode */
if (hubs->dcs_codes) {
if (hubs->series_startup) {
/* Set for 32 series updates */
snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
WM8993_DCS_SERIES_NO_01_MASK,
@ -134,9 +156,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");
@ -150,13 +172,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;
@ -168,8 +190,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
@ -195,7 +217,7 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
/* If we're applying an offset correction then updating the
* callibration would be likely to introduce further offsets. */
if (hubs->dcs_codes)
if (hubs->dcs_codes || hubs->no_series_update)
return ret;
/* Only need to do this if the outputs are active */
@ -599,9 +621,6 @@ SND_SOC_DAPM_MIXER("IN2L PGA", WM8993_POWER_MANAGEMENT_2, 7, 0,
SND_SOC_DAPM_MIXER("IN2R PGA", WM8993_POWER_MANAGEMENT_2, 5, 0,
in2r_pga, ARRAY_SIZE(in2r_pga)),
/* Dummy widgets to represent differential paths */
SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("MIXINL", WM8993_POWER_MANAGEMENT_2, 9, 0,
mixinl, ARRAY_SIZE(mixinl)),
SND_SOC_DAPM_MIXER("MIXINR", WM8993_POWER_MANAGEMENT_2, 8, 0,
@ -867,8 +886,11 @@ EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_controls);
int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
int lineout1_diff, int lineout2_diff)
{
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = &codec->dapm;
init_completion(&hubs->dcs_done);
snd_soc_dapm_add_routes(dapm, analogue_routes,
ARRAY_SIZE(analogue_routes));

View File

@ -14,6 +14,9 @@
#ifndef _WM_HUBS_H
#define _WM_HUBS_H
#include <linux/completion.h>
#include <linux/interrupt.h>
struct snd_soc_codec;
extern const unsigned int wm_hubs_spkmix_tlv[];
@ -23,9 +26,14 @@ struct wm_hubs_data {
int dcs_codes;
int dcs_readback_mode;
int hp_startup_mode;
int series_startup;
int no_series_update;
bool class_w;
u16 class_w_dcs;
bool dcs_done_irq;
struct completion dcs_done;
};
extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *);
@ -36,4 +44,6 @@ extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *,
int jd_scthr, int jd_thr,
int micbias1_lvl, int micbias2_lvl);
extern irqreturn_t wm_hubs_dcs_done(int irq, void *data);
#endif

View File

@ -46,11 +46,28 @@ static void print_buf_info(int slot, char *name)
}
#endif
#define DAVINCI_PCM_FMTBITS (\
SNDRV_PCM_FMTBIT_S8 |\
SNDRV_PCM_FMTBIT_U8 |\
SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S16_BE |\
SNDRV_PCM_FMTBIT_U16_LE |\
SNDRV_PCM_FMTBIT_U16_BE |\
SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S24_BE |\
SNDRV_PCM_FMTBIT_U24_LE |\
SNDRV_PCM_FMTBIT_U24_BE |\
SNDRV_PCM_FMTBIT_S32_LE |\
SNDRV_PCM_FMTBIT_S32_BE |\
SNDRV_PCM_FMTBIT_U32_LE |\
SNDRV_PCM_FMTBIT_U32_BE)
static struct snd_pcm_hardware pcm_hardware_playback = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
.formats = (SNDRV_PCM_FMTBIT_S16_LE),
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME|
SNDRV_PCM_INFO_BATCH),
.formats = DAVINCI_PCM_FMTBITS,
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
@ -59,7 +76,7 @@ static struct snd_pcm_hardware pcm_hardware_playback = {
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 2,
.channels_max = 2,
.channels_max = 384,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8 * 1024,
@ -71,8 +88,9 @@ static struct snd_pcm_hardware pcm_hardware_playback = {
static struct snd_pcm_hardware pcm_hardware_capture = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_LE),
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_BATCH),
.formats = DAVINCI_PCM_FMTBITS,
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
@ -81,7 +99,7 @@ static struct snd_pcm_hardware pcm_hardware_capture = {
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 2,
.channels_max = 2,
.channels_max = 384,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8 * 1024,
@ -139,6 +157,22 @@ struct davinci_runtime_data {
struct edmacc_param ram_params;
};
static void davinci_pcm_period_elapsed(struct snd_pcm_substream *substream)
{
struct davinci_runtime_data *prtd = substream->runtime->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
prtd->period++;
if (unlikely(prtd->period >= runtime->periods))
prtd->period = 0;
}
static void davinci_pcm_period_reset(struct snd_pcm_substream *substream)
{
struct davinci_runtime_data *prtd = substream->runtime->private_data;
prtd->period = 0;
}
/*
* Not used with ping/pong
*/
@ -199,10 +233,6 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
else
edma_set_transfer_params(link, acnt, fifo_level, count,
fifo_level, ABSYNC);
prtd->period++;
if (unlikely(prtd->period >= runtime->periods))
prtd->period = 0;
}
static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data)
@ -217,12 +247,13 @@ static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data)
return;
if (snd_pcm_running(substream)) {
spin_lock(&prtd->lock);
if (prtd->ram_channel < 0) {
/* No ping/pong must fix up link dma data*/
spin_lock(&prtd->lock);
davinci_pcm_enqueue_dma(substream);
spin_unlock(&prtd->lock);
}
davinci_pcm_period_elapsed(substream);
spin_unlock(&prtd->lock);
snd_pcm_period_elapsed(substream);
}
}
@ -425,7 +456,8 @@ static int request_ping_pong(struct snd_pcm_substream *substream,
edma_read_slot(link, &prtd->asp_params);
prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN);
prtd->asp_params.opt |= TCCHEN | EDMA_TCC(prtd->ram_channel & 0x3f);
prtd->asp_params.opt |= TCCHEN |
EDMA_TCC(prtd->ram_channel & 0x3f);
edma_write_slot(link, &prtd->asp_params);
/* pong */
@ -439,7 +471,7 @@ static int request_ping_pong(struct snd_pcm_substream *substream,
prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f));
/* interrupt after every pong completion */
prtd->asp_params.opt |= TCINTEN | TCCHEN |
EDMA_TCC(EDMA_CHAN_SLOT(prtd->ram_channel));
EDMA_TCC(prtd->ram_channel & 0x3f);
edma_write_slot(link, &prtd->asp_params);
/* ram */
@ -527,6 +559,13 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
edma_start(prtd->asp_channel);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
prtd->ram_channel >= 0) {
/* copy 1st iram buffer */
edma_start(prtd->ram_channel);
}
break;
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
edma_resume(prtd->asp_channel);
@ -550,6 +589,7 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream)
{
struct davinci_runtime_data *prtd = substream->runtime->private_data;
davinci_pcm_period_reset(substream);
if (prtd->ram_channel >= 0) {
int ret = ping_pong_dma_setup(substream);
if (ret < 0)
@ -565,21 +605,31 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream)
print_buf_info(prtd->asp_link[0], "asp_link[0]");
print_buf_info(prtd->asp_link[1], "asp_link[1]");
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* copy 1st iram buffer */
edma_start(prtd->ram_channel);
}
edma_start(prtd->asp_channel);
/*
* There is a phase offset of 2 periods between the position
* used by dma setup and the position reported in the pointer
* function.
*
* The phase offset, when not using ping-pong buffers, is due to
* the two consecutive calls to davinci_pcm_enqueue_dma() below.
*
* Whereas here, with ping-pong buffers, the phase is due to
* there being an entire buffer transfer complete before the
* first dma completion event triggers davinci_pcm_dma_irq().
*/
davinci_pcm_period_elapsed(substream);
davinci_pcm_period_elapsed(substream);
return 0;
}
prtd->period = 0;
davinci_pcm_enqueue_dma(substream);
davinci_pcm_period_elapsed(substream);
/* Copy self-linked parameter RAM entry into master channel */
edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
edma_write_slot(prtd->asp_channel, &prtd->asp_params);
davinci_pcm_enqueue_dma(substream);
edma_start(prtd->asp_channel);
davinci_pcm_period_elapsed(substream);
return 0;
}
@ -591,51 +641,23 @@ davinci_pcm_pointer(struct snd_pcm_substream *substream)
struct davinci_runtime_data *prtd = runtime->private_data;
unsigned int offset;
int asp_count;
dma_addr_t asp_src, asp_dst;
unsigned int period_size = snd_pcm_lib_period_bytes(substream);
/*
* There is a phase offset of 2 periods between the position used by dma
* setup and the position reported in the pointer function. Either +2 in
* the dma setup or -2 here in the pointer function (with wrapping,
* both) accounts for this offset -- choose the latter since it makes
* the first-time setup clearer.
*/
spin_lock(&prtd->lock);
if (prtd->ram_channel >= 0) {
int ram_count;
int mod_ram;
dma_addr_t ram_src, ram_dst;
unsigned int period_size = snd_pcm_lib_period_bytes(substream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* reading ram before asp should be safe
* as long as the asp transfers less than a ping size
* of bytes between the 2 reads
*/
edma_get_position(prtd->ram_channel,
&ram_src, &ram_dst);
edma_get_position(prtd->asp_channel,
&asp_src, &asp_dst);
asp_count = asp_src - prtd->asp_params.src;
ram_count = ram_src - prtd->ram_params.src;
mod_ram = ram_count % period_size;
mod_ram -= asp_count;
if (mod_ram < 0)
mod_ram += period_size;
else if (mod_ram == 0) {
if (snd_pcm_running(substream))
mod_ram += period_size;
}
ram_count -= mod_ram;
if (ram_count < 0)
ram_count += period_size * runtime->periods;
} else {
edma_get_position(prtd->ram_channel,
&ram_src, &ram_dst);
ram_count = ram_dst - prtd->ram_params.dst;
}
asp_count = ram_count;
} else {
edma_get_position(prtd->asp_channel, &asp_src, &asp_dst);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
asp_count = asp_src - runtime->dma_addr;
else
asp_count = asp_dst - runtime->dma_addr;
}
asp_count = prtd->period - 2;
spin_unlock(&prtd->lock);
if (asp_count < 0)
asp_count += runtime->periods;
asp_count *= period_size;
offset = bytes_to_frames(runtime, asp_count);
if (offset >= runtime->buffer_size)
offset = 0;
@ -811,9 +833,11 @@ static void davinci_pcm_free(struct snd_pcm *pcm)
static u64 davinci_pcm_dmamask = 0xffffffff;
static int davinci_pcm_new(struct snd_card *card,
struct snd_soc_dai *dai, struct snd_pcm *pcm)
static int davinci_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int ret;
if (!card->dev->dma_mask)

View File

@ -266,9 +266,11 @@ static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
static u64 ep93xx_pcm_dmamask = 0xffffffff;
static int ep93xx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)

View File

@ -294,9 +294,11 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
* Regardless of where the memory is actually allocated, since the device can
* technically DMA to any 36-bit address, we do need to set the DMA mask to 36.
*/
static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
static u64 fsl_dma_dmamask = DMA_BIT_MASK(36);
int ret;
@ -939,7 +941,7 @@ static int __devinit fsl_soc_dma_probe(struct platform_device *pdev)
iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL);
if (iprop)
dma->ssi_fifo_depth = *iprop;
dma->ssi_fifo_depth = be32_to_cpup(iprop);
else
/* Older 8610 DTs didn't have the fifo-depth property */
dma->ssi_fifo_depth = 8;

View File

@ -678,7 +678,12 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
kfree(ssi_private);
return ret;
}
ssi_private->ssi = ioremap(res.start, 1 + res.end - res.start);
ssi_private->ssi = of_iomap(np, 0);
if (!ssi_private->ssi) {
dev_err(&pdev->dev, "could not map device resources\n");
kfree(ssi_private);
return -ENOMEM;
}
ssi_private->ssi_phys = res.start;
ssi_private->irq = irq_of_parse_and_map(np, 0);
@ -691,7 +696,7 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
/* Determine the FIFO depth. */
iprop = of_get_property(np, "fsl,fifo-depth", NULL);
if (iprop)
ssi_private->fifo_depth = *iprop;
ssi_private->fifo_depth = be32_to_cpup(iprop);
else
/* Older 8610 DTs didn't have the fifo-depth property */
ssi_private->fifo_depth = 8;

View File

@ -299,10 +299,11 @@ static struct snd_pcm_ops psc_dma_ops = {
};
static u64 psc_dma_dmamask = 0xffffffff;
static int psc_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
static int psc_dma_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_pcm_runtime *rtd = pcm->private_data;
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai);
size_t size = psc_dma_hardware.buffer_bytes_max;
int rc = 0;

View File

@ -233,7 +233,7 @@ static int get_parent_cell_index(struct device_node *np)
if (!iprop)
return -1;
return *iprop;
return be32_to_cpup(iprop);
}
/**
@ -258,7 +258,7 @@ static int codec_node_dev_name(struct device_node *np, char *buf, size_t len)
if (!iprop)
return -EINVAL;
addr = *iprop;
addr = be32_to_cpup(iprop);
bus = get_parent_cell_index(np);
if (bus < 0)
@ -305,7 +305,7 @@ static int get_dma_channel(struct device_node *ssi_np,
return -EINVAL;
}
*dma_channel_id = *iprop;
*dma_channel_id = be32_to_cpup(iprop);
*dma_id = get_parent_cell_index(dma_channel_np);
of_node_put(dma_channel_np);
@ -379,7 +379,7 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev)
ret = -EINVAL;
goto error;
}
machine_data->ssi_id = *iprop;
machine_data->ssi_id = be32_to_cpup(iprop);
/* Get the serial format and clock direction. */
sprop = of_get_property(np, "fsl,mode", NULL);
@ -405,7 +405,7 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev)
ret = -EINVAL;
goto error;
}
machine_data->clk_frequency = *iprop;
machine_data->clk_frequency = be32_to_cpup(iprop);
} else if (strcasecmp(sprop, "i2s-master") == 0) {
machine_data->dai_format = SND_SOC_DAIFMT_I2S;
machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;

View File

@ -232,7 +232,7 @@ static int get_parent_cell_index(struct device_node *np)
iprop = of_get_property(parent, "cell-index", NULL);
if (iprop)
ret = *iprop;
ret = be32_to_cpup(iprop);
of_node_put(parent);
@ -261,7 +261,7 @@ static int codec_node_dev_name(struct device_node *np, char *buf, size_t len)
if (!iprop)
return -EINVAL;
addr = *iprop;
addr = be32_to_cpup(iprop);
bus = get_parent_cell_index(np);
if (bus < 0)
@ -308,7 +308,7 @@ static int get_dma_channel(struct device_node *ssi_np,
return -EINVAL;
}
*dma_channel_id = *iprop;
*dma_channel_id = be32_to_cpup(iprop);
*dma_id = get_parent_cell_index(dma_channel_np);
of_node_put(dma_channel_np);
@ -379,7 +379,7 @@ static int p1022_ds_probe(struct platform_device *pdev)
ret = -EINVAL;
goto error;
}
mdata->ssi_id = *iprop;
mdata->ssi_id = be32_to_cpup(iprop);
/* Get the serial format and clock direction. */
sprop = of_get_property(np, "fsl,mode", NULL);
@ -405,7 +405,7 @@ static int p1022_ds_probe(struct platform_device *pdev)
ret = -EINVAL;
goto error;
}
mdata->clk_frequency = *iprop;
mdata->clk_frequency = be32_to_cpup(iprop);
} else if (strcasecmp(sprop, "i2s-master") == 0) {
mdata->dai_format = SND_SOC_DAIFMT_I2S;
mdata->codec_clk_direction = SND_SOC_CLOCK_IN;

View File

@ -238,12 +238,14 @@ static struct snd_pcm_ops imx_pcm_ops = {
static int ssi_irq = 0;
static int imx_pcm_fiq_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int ret;
ret = imx_pcm_new(card, dai, pcm);
ret = imx_pcm_new(rtd);
if (ret)
return ret;

View File

@ -388,10 +388,11 @@ static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
static u64 imx_pcm_dmamask = DMA_BIT_MASK(32);
int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)

View File

@ -225,8 +225,7 @@ struct snd_soc_platform *imx_ssi_dma_mx2_init(struct platform_device *pdev,
struct imx_ssi *ssi);
int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma);
int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm);
int imx_pcm_new(struct snd_soc_pcm_runtime *rtd);
void imx_pcm_free(struct snd_pcm *pcm);
/*

View File

@ -299,9 +299,11 @@ static void jz4740_pcm_free(struct snd_pcm *pcm)
static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32);
int jz4740_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
int jz4740_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)

View File

@ -312,9 +312,11 @@ static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm,
return 0;
}
static int kirkwood_dma_new(struct snd_card *card,
struct snd_soc_dai *dai, struct snd_pcm *pcm)
static int kirkwood_dma_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int ret;
if (!card->dev->dma_mask)

View File

@ -402,9 +402,10 @@ static void sst_pcm_free(struct snd_pcm *pcm)
snd_pcm_lib_preallocate_free_for_all(pcm);
}
int sst_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int retval = 0;
pr_debug("sst_pcm_new called\n");

View File

@ -356,7 +356,7 @@ static int __devinit nuc900_ac97_drvprobe(struct platform_device *pdev)
nuc900_audio->irq_num = platform_get_irq(pdev, 0);
if (!nuc900_audio->irq_num) {
ret = -EBUSY;
goto out2;
goto out3;
}
nuc900_ac97_data = nuc900_audio;

View File

@ -315,9 +315,12 @@ static void nuc900_dma_free_dma_buffers(struct snd_pcm *pcm)
}
static u64 nuc900_pcm_dmamask = DMA_BIT_MASK(32);
static int nuc900_dma_new(struct snd_card *card,
struct snd_soc_dai *dai, struct snd_pcm *pcm)
static int nuc900_dma_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
if (!card->dev->dma_mask)
card->dev->dma_mask = &nuc900_pcm_dmamask;
if (!card->dev->coherent_dma_mask)

View File

@ -9,6 +9,9 @@ config SND_OMAP_SOC_MCBSP
config SND_OMAP_SOC_MCPDM
tristate
config SND_OMAP_SOC_HDMI
tristate
config SND_OMAP_SOC_N810
tristate "SoC Audio support for Nokia N810"
depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C
@ -100,6 +103,14 @@ config SND_OMAP_SOC_SDP4430
Say Y if you want to add support for SoC audio on Texas Instruments
SDP4430.
config SND_OMAP_SOC_OMAP4_HDMI
tristate "SoC Audio support for Texas Instruments OMAP4 HDMI"
depends on SND_OMAP_SOC && OMAP4_DSS_HDMI && OMAP2_DSS && ARCH_OMAP4
select SND_OMAP_SOC_HDMI
help
Say Y if you want to add support for SoC HDMI audio on Texas Instruments
OMAP4 chips
config SND_OMAP_SOC_OMAP3_PANDORA
tristate "SoC Audio support for OMAP3 Pandora"
depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3_PANDORA

View File

@ -2,10 +2,12 @@
snd-soc-omap-objs := omap-pcm.o
snd-soc-omap-mcbsp-objs := omap-mcbsp.o
snd-soc-omap-mcpdm-objs := omap-mcpdm.o mcpdm.o
snd-soc-omap-hdmi-objs := omap-hdmi.o
obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o
obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o
obj-$(CONFIG_SND_OMAP_SOC_HDMI) += snd-soc-omap-hdmi.o
# OMAP Machine Support
snd-soc-n810-objs := n810.o
@ -21,6 +23,7 @@ snd-soc-omap3pandora-objs := omap3pandora.o
snd-soc-omap3beagle-objs := omap3beagle.o
snd-soc-zoom2-objs := zoom2.o
snd-soc-igep0020-objs := igep0020.o
snd-soc-omap4-hdmi-objs := omap4-hdmi-card.o
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o
@ -36,3 +39,4 @@ obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o
obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o
obj-$(CONFIG_SND_OMAP_SOC_IGEP0020) += snd-soc-igep0020.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) += snd-soc-omap4-hdmi.o

View File

@ -427,7 +427,8 @@ static struct snd_soc_ops ams_delta_ops = {
/* Board specific codec bias level control */
static int ams_delta_set_bias_level(struct snd_soc_card *card,
enum snd_soc_bias_level level)
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
struct snd_soc_codec *codec = card->rtd->codec;

158
sound/soc/omap/omap-hdmi.c Normal file
View File

@ -0,0 +1,158 @@
/*
* omap-hdmi.c
*
* OMAP ALSA SoC DAI driver for HDMI audio on OMAP4 processors.
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
* Authors: Jorge Candelaria <jorge.candelaria@ti.com>
* Ricardo Neri <ricardo.neri@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <plat/dma.h>
#include "omap-pcm.h"
#include "omap-hdmi.h"
#define DRV_NAME "hdmi-audio-dai"
static struct omap_pcm_dma_data omap_hdmi_dai_dma_params = {
.name = "HDMI playback",
.sync_mode = OMAP_DMA_SYNC_PACKET,
};
static int omap_hdmi_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int err;
/*
* Make sure that the period bytes are multiple of the DMA packet size.
* Largest packet size we use is 32 32-bit words = 128 bytes
*/
err = snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128);
if (err < 0)
return err;
return 0;
}
static int omap_hdmi_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
int err = 0;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
omap_hdmi_dai_dma_params.packet_size = 16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
omap_hdmi_dai_dma_params.packet_size = 32;
break;
default:
err = -EINVAL;
}
omap_hdmi_dai_dma_params.data_type = OMAP_DMA_DATA_TYPE_S32;
snd_soc_dai_set_dma_data(dai, substream,
&omap_hdmi_dai_dma_params);
return err;
}
static struct snd_soc_dai_ops omap_hdmi_dai_ops = {
.startup = omap_hdmi_dai_startup,
.hw_params = omap_hdmi_dai_hw_params,
};
static struct snd_soc_dai_driver omap_hdmi_dai = {
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = OMAP_HDMI_RATES,
.formats = OMAP_HDMI_FORMATS,
},
.ops = &omap_hdmi_dai_ops,
};
static __devinit int omap_hdmi_probe(struct platform_device *pdev)
{
int ret;
struct resource *hdmi_rsrc;
hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!hdmi_rsrc) {
dev_err(&pdev->dev, "Cannot obtain IORESOURCE_MEM HDMI\n");
return -EINVAL;
}
omap_hdmi_dai_dma_params.port_addr = hdmi_rsrc->start
+ OMAP_HDMI_AUDIO_DMA_PORT;
hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!hdmi_rsrc) {
dev_err(&pdev->dev, "Cannot obtain IORESOURCE_DMA HDMI\n");
return -EINVAL;
}
omap_hdmi_dai_dma_params.dma_req = hdmi_rsrc->start;
ret = snd_soc_register_dai(&pdev->dev, &omap_hdmi_dai);
return ret;
}
static int __devexit omap_hdmi_remove(struct platform_device *pdev)
{
snd_soc_unregister_dai(&pdev->dev);
return 0;
}
static struct platform_driver hdmi_dai_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
.probe = omap_hdmi_probe,
.remove = __devexit_p(omap_hdmi_remove),
};
static int __init hdmi_dai_init(void)
{
return platform_driver_register(&hdmi_dai_driver);
}
module_init(hdmi_dai_init);
static void __exit hdmi_dai_exit(void)
{
platform_driver_unregister(&hdmi_dai_driver);
}
module_exit(hdmi_dai_exit);
MODULE_AUTHOR("Jorge Candelaria <jorge.candelaria@ti.com>");
MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
MODULE_DESCRIPTION("OMAP HDMI SoC Interface");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);

View File

@ -0,0 +1,36 @@
/*
* omap-hdmi.h
*
* Definitions for OMAP ALSA SoC DAI driver for HDMI audio on OMAP4 processors.
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
* Authors: Jorge Candelaria <jorge.candelaria@ti.com>
* Ricardo Neri <ricardo.neri@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __OMAP_HDMI_H__
#define __OMAP_HDMI_H__
#define OMAP_HDMI_AUDIO_DMA_PORT 0x8c
#define OMAP_HDMI_RATES (SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
#define OMAP_HDMI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE)
#endif

View File

@ -366,9 +366,11 @@ static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm)
}
}
static int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
static int omap_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)

View File

@ -0,0 +1,129 @@
/*
* omap4-hdmi-card.c
*
* OMAP ALSA SoC machine driver for TI OMAP4 HDMI
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
* Author: Ricardo Neri <ricardo.neri@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <sound/pcm.h>
#include <sound/soc.h>
#include <asm/mach-types.h>
#include <video/omapdss.h>
#define DRV_NAME "omap4-hdmi-audio"
static int omap4_hdmi_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
int i;
struct omap_overlay_manager *mgr = NULL;
struct device *dev = substream->pcm->card->dev;
/* Find DSS HDMI device */
for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
mgr = omap_dss_get_overlay_manager(i);
if (mgr && mgr->device
&& mgr->device->type == OMAP_DISPLAY_TYPE_HDMI)
break;
}
if (i == omap_dss_get_num_overlay_managers()) {
dev_err(dev, "HDMI display device not found!\n");
return -ENODEV;
}
/* Make sure HDMI is power-on to avoid L3 interconnect errors */
if (mgr->device->state != OMAP_DSS_DISPLAY_ACTIVE) {
dev_err(dev, "HDMI display is not active!\n");
return -EIO;
}
return 0;
}
static struct snd_soc_ops omap4_hdmi_dai_ops = {
.hw_params = omap4_hdmi_dai_hw_params,
};
static struct snd_soc_dai_link omap4_hdmi_dai = {
.name = "HDMI",
.stream_name = "HDMI",
.cpu_dai_name = "hdmi-audio-dai",
.platform_name = "omap-pcm-audio",
.codec_name = "omapdss_hdmi",
.codec_dai_name = "hdmi-audio-codec",
.ops = &omap4_hdmi_dai_ops,
};
static struct snd_soc_card snd_soc_omap4_hdmi = {
.name = "OMAP4HDMI",
.dai_link = &omap4_hdmi_dai,
.num_links = 1,
};
static __devinit int omap4_hdmi_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &snd_soc_omap4_hdmi;
int ret;
card->dev = &pdev->dev;
ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
card->dev = NULL;
return ret;
}
return 0;
}
static int __devexit omap4_hdmi_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
snd_soc_unregister_card(card);
card->dev = NULL;
return 0;
}
static struct platform_driver omap4_hdmi_driver = {
.driver = {
.name = "omap4-hdmi-audio",
.owner = THIS_MODULE,
},
.probe = omap4_hdmi_probe,
.remove = __devexit_p(omap4_hdmi_remove),
};
static int __init omap4_hdmi_init(void)
{
return platform_driver_register(&omap4_hdmi_driver);
}
module_init(omap4_hdmi_init);
static void __exit omap4_hdmi_exit(void)
{
platform_driver_unregister(&omap4_hdmi_driver);
}
module_exit(omap4_hdmi_exit);
MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
MODULE_DESCRIPTION("OMAP4 HDMI machine ASoC driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);

View File

@ -85,9 +85,10 @@ static struct snd_pcm_ops pxa2xx_pcm_ops = {
static u64 pxa2xx_pcm_dmamask = DMA_BIT_MASK(32);
static int pxa2xx_soc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
static int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)

View File

@ -443,10 +443,11 @@ static void s6000_pcm_free(struct snd_pcm *pcm)
static u64 s6000_pcm_dmamask = DMA_BIT_MASK(32);
static int s6000_pcm_new(struct snd_card *card,
struct snd_soc_dai *dai, struct snd_pcm *pcm)
static int s6000_pcm_new(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_pcm_runtime *runtime = pcm->private_data;
struct snd_card *card = runtime->card->snd_card;
struct snd_soc_dai *dai = runtime->cpu_dai;
struct snd_pcm *pcm = runtime->pcm;
struct s6000_pcm_dma_params *params;
int res;

View File

@ -158,7 +158,7 @@ config SND_SOC_GONI_AQUILA_WM8994
config SND_SOC_SAMSUNG_SMDK_SPDIF
tristate "SoC S/PDIF Audio support for SMDK"
depends on SND_SOC_SAMSUNG && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210)
depends on SND_SOC_SAMSUNG && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210 || MACH_SMDKV310)
select SND_SAMSUNG_SPDIF
help
Say Y if you want to add support for SoC S/PDIF audio on the SMDK.
@ -171,9 +171,23 @@ 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
select SND_SAMSUNG_I2S
select SND_SOC_WM8915
select SND_SOC_WM9081
config SND_SOC_SPEYSIDE_WM8962
tristate "Audio support for Wolfson Speyside with WM8962"
depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
select SND_SAMSUNG_I2S
select SND_SOC_WM8962

View File

@ -35,7 +35,9 @@ 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
obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@ -54,4 +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

View File

@ -425,9 +425,11 @@ static void dma_free_dma_buffers(struct snd_pcm *pcm)
static u64 dma_mask = DMA_BIT_MASK(32);
static int dma_new(struct snd_card *card,
struct snd_soc_dai *dai, struct snd_pcm *pcm)
static int dma_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
pr_debug("Entered %s\n", __func__);

View 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 */

View File

@ -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)

View File

@ -8,6 +8,7 @@
*/
#include "../codecs/wm8994.h"
#include <sound/pcm_params.h>
/*
* Default CFG switch settings to use this driver:
@ -44,7 +45,9 @@ static int smdk_hw_params(struct snd_pcm_substream *substream,
int ret;
/* AIF1CLK should be >=3MHz for optimal performance */
if (params_rate(params) == 8000 || params_rate(params) == 11025)
if (params_format(params) == SNDRV_PCM_FORMAT_S24_LE)
pll_out = params_rate(params) * 384;
else if (params_rate(params) == 8000 || params_rate(params) == 11025)
pll_out = params_rate(params) * 512;
else
pll_out = params_rate(params) * 256;

View 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");

View File

@ -20,24 +20,29 @@
#define WM8915_HPSEL_GPIO 214
static int speyside_set_bias_level(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
int ret;
if (dapm->dev != codec_dai->dev)
return 0;
switch (level) {
case SND_SOC_BIAS_STANDBY:
ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK1,
ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK2,
32768, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK1,
ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK2,
0, 0, 0);
if (ret < 0) {
pr_err("Failed to stop FLL\n");
return ret;
}
break;
default:
break;
@ -46,6 +51,45 @@ static int speyside_set_bias_level(struct snd_soc_card *card,
return 0;
}
static int speyside_set_bias_level_post(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
int ret;
if (dapm->dev != codec_dai->dev)
return 0;
switch (level) {
case SND_SOC_BIAS_PREPARE:
if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
ret = snd_soc_dai_set_pll(codec_dai, 0,
WM8915_FLL_MCLK2,
32768, 48000 * 256);
if (ret < 0) {
pr_err("Failed to start FLL\n");
return ret;
}
ret = snd_soc_dai_set_sysclk(codec_dai,
WM8915_SYSCLK_FLL,
48000 * 256,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
}
break;
default:
break;
}
card->dapm.bias_level = level;
return 0;
}
static int speyside_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@ -66,16 +110,6 @@ static int speyside_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(codec_dai, 0, WM8915_FLL_MCLK1,
32768, 256 * 48000);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_FLL,
256 * 48000, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
return 0;
}
@ -127,7 +161,7 @@ static int speyside_wm8915_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_codec *codec = rtd->codec;
int ret;
ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK1, 32768, 0);
ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK2, 32768, 0);
if (ret < 0)
return ret;
@ -267,6 +301,7 @@ static struct snd_soc_card speyside = {
.num_configs = ARRAY_SIZE(speyside_codec_conf),
.set_bias_level = speyside_set_bias_level,
.set_bias_level_post = speyside_set_bias_level_post,
.controls = controls,
.num_controls = ARRAY_SIZE(controls),

View File

@ -0,0 +1,264 @@
/*
* Speyside with WM8962 audio support
*
* Copyright 2011 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 <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/jack.h>
#include <linux/gpio.h>
#include "../codecs/wm8962.h"
static int speyside_wm8962_set_bias_level(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
int ret;
switch (level) {
case SND_SOC_BIAS_PREPARE:
if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
WM8962_FLL_MCLK, 32768,
44100 * 256);
if (ret < 0)
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) {
pr_err("Failed to set SYSCLK: %d\n");
return ret;
}
}
break;
default:
break;
}
return 0;
}
static int speyside_wm8962_set_bias_level_post(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
int ret;
switch (level) {
case SND_SOC_BIAS_STANDBY:
ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
32768, SND_SOC_CLOCK_IN);
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: %d\n", ret);
return ret;
}
break;
default:
break;
}
dapm->bias_level = level;
return 0;
}
static int speyside_wm8962_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(codec_dai, SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return 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 < 0)
return ret;
return 0;
}
static struct snd_soc_ops speyside_wm8962_ops = {
.hw_params = speyside_wm8962_hw_params,
};
static struct snd_soc_dai_link speyside_wm8962_dai[] = {
{
.name = "CPU",
.stream_name = "CPU",
.cpu_dai_name = "samsung-i2s.0",
.codec_dai_name = "wm8962",
.platform_name = "samsung-audio",
.codec_name = "wm8962.1-001a",
.ops = &speyside_wm8962_ops,
},
};
static const struct snd_kcontrol_new controls[] = {
SOC_DAPM_PIN_SWITCH("Main Speaker"),
};
static struct snd_soc_dapm_widget widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_MIC("DMIC", NULL),
SND_SOC_DAPM_SPK("Main Speaker", NULL),
};
static struct snd_soc_dapm_route audio_paths[] = {
{ "Headphone", NULL, "HPOUTL" },
{ "Headphone", NULL, "HPOUTR" },
{ "Main Speaker", NULL, "SPKOUTL" },
{ "Main Speaker", NULL, "SPKOUTR" },
{ "MICBIAS", NULL, "Headset Mic" },
{ "IN4L", NULL, "MICBIAS" },
{ "IN4R", NULL, "MICBIAS" },
{ "MICBIAS", NULL, "DMIC" },
{ "DMICDAT", NULL, "MICBIAS" },
};
static struct snd_soc_jack speyside_wm8962_headset;
/* Headset jack detection DAPM pins */
static struct snd_soc_jack_pin speyside_wm8962_headset_pins[] = {
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
{
.pin = "Headphone",
.mask = SND_JACK_MICROPHONE,
},
};
static int speyside_wm8962_late_probe(struct snd_soc_card *card)
{
struct snd_soc_codec *codec = card->rtd[0].codec;
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
int ret;
ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
32768, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
ret = snd_soc_jack_new(codec, "Headset",
SND_JACK_HEADSET | SND_JACK_BTN_0,
&speyside_wm8962_headset);
if (ret)
return ret;
ret = snd_soc_jack_add_pins(&speyside_wm8962_headset,
ARRAY_SIZE(speyside_wm8962_headset_pins),
speyside_wm8962_headset_pins);
if (ret)
return ret;
wm8962_mic_detect(codec, &speyside_wm8962_headset);
return 0;
}
static struct snd_soc_card speyside_wm8962 = {
.name = "Speyside WM8962",
.dai_link = speyside_wm8962_dai,
.num_links = ARRAY_SIZE(speyside_wm8962_dai),
.set_bias_level = speyside_wm8962_set_bias_level,
.set_bias_level_post = speyside_wm8962_set_bias_level_post,
.controls = controls,
.num_controls = ARRAY_SIZE(controls),
.dapm_widgets = widgets,
.num_dapm_widgets = ARRAY_SIZE(widgets),
.dapm_routes = audio_paths,
.num_dapm_routes = ARRAY_SIZE(audio_paths),
.late_probe = speyside_wm8962_late_probe,
};
static __devinit int speyside_wm8962_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &speyside_wm8962;
int ret;
card->dev = &pdev->dev;
ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
return ret;
}
return 0;
}
static int __devexit speyside_wm8962_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
snd_soc_unregister_card(card);
return 0;
}
static struct platform_driver speyside_wm8962_driver = {
.driver = {
.name = "speyside-wm8962",
.owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
},
.probe = speyside_wm8962_probe,
.remove = __devexit_p(speyside_wm8962_remove),
};
static int __init speyside_wm8962_audio_init(void)
{
return platform_driver_register(&speyside_wm8962_driver);
}
module_init(speyside_wm8962_audio_init);
static void __exit speyside_wm8962_audio_exit(void)
{
platform_driver_unregister(&speyside_wm8962_driver);
}
module_exit(speyside_wm8962_audio_exit);
MODULE_DESCRIPTION("Speyside WM8962 audio support");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:speyside-wm8962");

View File

@ -327,10 +327,10 @@ static void camelot_pcm_free(struct snd_pcm *pcm)
snd_pcm_lib_preallocate_free_for_all(pcm);
}
static int camelot_pcm_new(struct snd_card *card,
struct snd_soc_dai *dai,
struct snd_pcm *pcm)
static int camelot_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
/* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
* in MMAP mode (i.e. aplay -M)
*/

View File

@ -97,7 +97,7 @@ static int fsi_ak4642_remove(struct platform_device *pdev)
static struct fsi_ak4642_data fsi_a_ak4642 = {
.name = "AK4642",
.card = "FSIA (AK4642)",
.card = "FSIA-AK4642",
.cpu_dai = "fsia-dai",
.codec = "ak4642-codec.0-0012",
.platform = "sh_fsi.0",
@ -106,7 +106,7 @@ static struct fsi_ak4642_data fsi_a_ak4642 = {
static struct fsi_ak4642_data fsi_b_ak4642 = {
.name = "AK4642",
.card = "FSIB (AK4642)",
.card = "FSIB-AK4642",
.cpu_dai = "fsib-dai",
.codec = "ak4642-codec.0-0012",
.platform = "sh_fsi.0",
@ -115,7 +115,7 @@ static struct fsi_ak4642_data fsi_b_ak4642 = {
static struct fsi_ak4642_data fsi_a_ak4643 = {
.name = "AK4643",
.card = "FSIA (AK4643)",
.card = "FSIA-AK4643",
.cpu_dai = "fsia-dai",
.codec = "ak4642-codec.0-0013",
.platform = "sh_fsi.0",
@ -124,7 +124,7 @@ static struct fsi_ak4642_data fsi_a_ak4643 = {
static struct fsi_ak4642_data fsi_b_ak4643 = {
.name = "AK4643",
.card = "FSIB (AK4643)",
.card = "FSIB-AK4643",
.cpu_dai = "fsib-dai",
.codec = "ak4642-codec.0-0013",
.platform = "sh_fsi.0",
@ -133,7 +133,7 @@ static struct fsi_ak4642_data fsi_b_ak4643 = {
static struct fsi_ak4642_data fsi2_a_ak4642 = {
.name = "AK4642",
.card = "FSI2A (AK4642)",
.card = "FSI2A-AK4642",
.cpu_dai = "fsia-dai",
.codec = "ak4642-codec.0-0012",
.platform = "sh_fsi2",
@ -142,7 +142,7 @@ static struct fsi_ak4642_data fsi2_a_ak4642 = {
static struct fsi_ak4642_data fsi2_b_ak4642 = {
.name = "AK4642",
.card = "FSI2B (AK4642)",
.card = "FSI2B-AK4642",
.cpu_dai = "fsib-dai",
.codec = "ak4642-codec.0-0012",
.platform = "sh_fsi2",
@ -151,7 +151,7 @@ static struct fsi_ak4642_data fsi2_b_ak4642 = {
static struct fsi_ak4642_data fsi2_a_ak4643 = {
.name = "AK4643",
.card = "FSI2A (AK4643)",
.card = "FSI2A-AK4643",
.cpu_dai = "fsia-dai",
.codec = "ak4642-codec.0-0013",
.platform = "sh_fsi2",
@ -160,7 +160,7 @@ static struct fsi_ak4642_data fsi2_a_ak4643 = {
static struct fsi_ak4642_data fsi2_b_ak4643 = {
.name = "AK4643",
.card = "FSI2B (AK4643)",
.card = "FSI2B-AK4643",
.cpu_dai = "fsib-dai",
.codec = "ak4642-codec.0-0013",
.platform = "sh_fsi2",

View File

@ -42,7 +42,7 @@ static struct snd_soc_dai_link fsi_da7210_dai = {
};
static struct snd_soc_card fsi_soc_card = {
.name = "FSI (DA7210)",
.name = "FSI-DA7210",
.dai_link = &fsi_da7210_dai,
.num_links = 1,
};

View File

@ -83,13 +83,13 @@ static int fsi_hdmi_remove(struct platform_device *pdev)
static struct fsi_hdmi_data fsi2_a_hdmi = {
.cpu_dai = "fsia-dai",
.card = "FSI2A (SH MOBILE HDMI)",
.card = "FSI2A-HDMI",
.id = FSI_PORT_A,
};
static struct fsi_hdmi_data fsi2_b_hdmi = {
.cpu_dai = "fsib-dai",
.card = "FSI2B (SH MOBILE HDMI)",
.card = "FSI2B-HDMI",
.id = FSI_PORT_B,
};

View File

@ -118,10 +118,38 @@ typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int ena
/*
* FSI driver use below type name for variable
*
* xxx_len : data length
* xxx_width : data width
* xxx_offset : data offset
* xxx_num : number of data
* xxx_pos : position of data
* xxx_capa : capacity of data
*/
/*
* period/frame/sample image
*
* ex) PCM (2ch)
*
* period pos period pos
* [n] [n + 1]
* |<-------------------- period--------------------->|
* ==|============================================ ... =|==
* | |
* ||<----- frame ----->|<------ frame ----->| ... |
* |+--------------------+--------------------+- ... |
* ||[ sample ][ sample ]|[ sample ][ sample ]| ... |
* |+--------------------+--------------------+- ... |
* ==|============================================ ... =|==
*/
/*
* FSI FIFO image
*
* | |
* | |
* | [ sample ] |
* | [ sample ] |
* | [ sample ] |
* | [ sample ] |
* --> go to codecs
*/
/*
@ -131,12 +159,11 @@ typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int ena
struct fsi_stream {
struct snd_pcm_substream *substream;
int fifo_max_num;
int buff_offset;
int buff_len;
int period_len;
int period_num;
int fifo_sample_capa; /* sample capacity of FSI FIFO */
int buff_sample_capa; /* sample capacity of ALSA buffer */
int buff_sample_pos; /* sample position of ALSA buffer */
int period_samples; /* sample number / 1 period */
int period_pos; /* current period position */
int uerr_num;
int oerr_num;
@ -149,17 +176,14 @@ struct fsi_priv {
struct fsi_stream playback;
struct fsi_stream capture;
u32 do_fmt;
u32 di_fmt;
int chan_num:16;
int clk_master:1;
int spdif:1;
long rate;
/* for suspend/resume */
u32 saved_do_fmt;
u32 saved_di_fmt;
u32 saved_ckg1;
u32 saved_ckg2;
u32 saved_out_sel;
};
struct fsi_core {
@ -180,14 +204,6 @@ struct fsi_master {
struct fsi_core *core;
struct sh_fsi_platform_info *info;
spinlock_t lock;
/* for suspend/resume */
u32 saved_a_mclk;
u32 saved_b_mclk;
u32 saved_iemsk;
u32 saved_imsk;
u32 saved_clk_rst;
u32 saved_soft_rst;
};
/*
@ -271,6 +287,11 @@ static int fsi_is_port_a(struct fsi_priv *fsi)
return fsi->master->base == fsi->base;
}
static int fsi_is_spdif(struct fsi_priv *fsi)
{
return fsi->spdif;
}
static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@ -342,28 +363,59 @@ static u32 fsi_get_port_shift(struct fsi_priv *fsi, int is_play)
return shift;
}
static void fsi_stream_push(struct fsi_priv *fsi,
int is_play,
struct snd_pcm_substream *substream,
u32 buffer_len,
u32 period_len)
static int fsi_frame2sample(struct fsi_priv *fsi, int frames)
{
return frames * fsi->chan_num;
}
static int fsi_sample2frame(struct fsi_priv *fsi, int samples)
{
return samples / fsi->chan_num;
}
static int fsi_stream_is_working(struct fsi_priv *fsi,
int is_play)
{
struct fsi_stream *io = fsi_get_stream(fsi, is_play);
struct fsi_master *master = fsi_get_master(fsi);
unsigned long flags;
int ret;
spin_lock_irqsave(&master->lock, flags);
ret = !!io->substream;
spin_unlock_irqrestore(&master->lock, flags);
return ret;
}
static void fsi_stream_push(struct fsi_priv *fsi,
int is_play,
struct snd_pcm_substream *substream)
{
struct fsi_stream *io = fsi_get_stream(fsi, is_play);
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsi_master *master = fsi_get_master(fsi);
unsigned long flags;
spin_lock_irqsave(&master->lock, flags);
io->substream = substream;
io->buff_len = buffer_len;
io->buff_offset = 0;
io->period_len = period_len;
io->period_num = 0;
io->buff_sample_capa = fsi_frame2sample(fsi, runtime->buffer_size);
io->buff_sample_pos = 0;
io->period_samples = fsi_frame2sample(fsi, runtime->period_size);
io->period_pos = 0;
io->oerr_num = -1; /* ignore 1st err */
io->uerr_num = -1; /* ignore 1st err */
spin_unlock_irqrestore(&master->lock, flags);
}
static void fsi_stream_pop(struct fsi_priv *fsi, int is_play)
{
struct fsi_stream *io = fsi_get_stream(fsi, is_play);
struct snd_soc_dai *dai = fsi_get_dai(io->substream);
struct fsi_master *master = fsi_get_master(fsi);
unsigned long flags;
spin_lock_irqsave(&master->lock, flags);
if (io->oerr_num > 0)
dev_err(dai->dev, "over_run = %d\n", io->oerr_num);
@ -372,47 +424,27 @@ static void fsi_stream_pop(struct fsi_priv *fsi, int is_play)
dev_err(dai->dev, "under_run = %d\n", io->uerr_num);
io->substream = NULL;
io->buff_len = 0;
io->buff_offset = 0;
io->period_len = 0;
io->period_num = 0;
io->buff_sample_capa = 0;
io->buff_sample_pos = 0;
io->period_samples = 0;
io->period_pos = 0;
io->oerr_num = 0;
io->uerr_num = 0;
spin_unlock_irqrestore(&master->lock, flags);
}
static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play)
static int fsi_get_current_fifo_samples(struct fsi_priv *fsi, int is_play)
{
u32 status;
int data_num;
int frames;
status = is_play ?
fsi_reg_read(fsi, DOFF_ST) :
fsi_reg_read(fsi, DIFF_ST);
data_num = 0x1ff & (status >> 8);
data_num *= fsi->chan_num;
frames = 0x1ff & (status >> 8);
return data_num;
}
static int fsi_len2num(int len, int width)
{
return len / width;
}
#define fsi_num2offset(a, b) fsi_num2len(a, b)
static int fsi_num2len(int num, int width)
{
return num * width;
}
static int fsi_get_frame_width(struct fsi_priv *fsi, int is_play)
{
struct fsi_stream *io = fsi_get_stream(fsi, is_play);
struct snd_pcm_substream *substream = io->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
return frames_to_bytes(runtime, 1) / fsi->chan_num;
return fsi_frame2sample(fsi, frames);
}
static void fsi_count_fifo_err(struct fsi_priv *fsi)
@ -444,8 +476,10 @@ static u8 *fsi_dma_get_area(struct fsi_priv *fsi, int stream)
{
int is_play = fsi_stream_is_play(stream);
struct fsi_stream *io = fsi_get_stream(fsi, is_play);
struct snd_pcm_runtime *runtime = io->substream->runtime;
return io->substream->runtime->dma_area + io->buff_offset;
return runtime->dma_area +
samples_to_bytes(runtime, io->buff_sample_pos);
}
static void fsi_dma_soft_push16(struct fsi_priv *fsi, int num)
@ -559,37 +593,94 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable)
/*
* clock function
*/
#define fsi_module_init(m, d) __fsi_module_clk_ctrl(m, d, 1)
#define fsi_module_kill(m, d) __fsi_module_clk_ctrl(m, d, 0)
static void __fsi_module_clk_ctrl(struct fsi_master *master,
struct device *dev,
int enable)
{
pm_runtime_get_sync(dev);
if (enable) {
/* enable only SR */
fsi_master_mask_set(master, SOFT_RST, FSISR, FSISR);
fsi_master_mask_set(master, SOFT_RST, PASR | PBSR, 0);
} else {
/* clear all registers */
fsi_master_mask_set(master, SOFT_RST, FSISR, 0);
}
pm_runtime_put_sync(dev);
}
#define fsi_port_start(f) __fsi_port_clk_ctrl(f, 1)
#define fsi_port_stop(f) __fsi_port_clk_ctrl(f, 0)
static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int enable)
static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi,
long rate, int enable)
{
struct fsi_master *master = fsi_get_master(fsi);
u32 soft = fsi_is_port_a(fsi) ? PASR : PBSR;
u32 clk = fsi_is_port_a(fsi) ? CRA : CRB;
int is_master = fsi_is_clk_master(fsi);
set_rate_func set_rate = fsi_get_info_set_rate(master);
int fsi_ver = master->core->ver;
int ret;
fsi_master_mask_set(master, SOFT_RST, soft, (enable) ? soft : 0);
if (is_master)
ret = set_rate(dev, fsi_is_port_a(fsi), rate, enable);
if (ret < 0) /* error */
return ret;
if (!enable)
return 0;
if (ret > 0) {
u32 data = 0;
switch (ret & SH_FSI_ACKMD_MASK) {
default:
/* FALL THROUGH */
case SH_FSI_ACKMD_512:
data |= (0x0 << 12);
break;
case SH_FSI_ACKMD_256:
data |= (0x1 << 12);
break;
case SH_FSI_ACKMD_128:
data |= (0x2 << 12);
break;
case SH_FSI_ACKMD_64:
data |= (0x3 << 12);
break;
case SH_FSI_ACKMD_32:
if (fsi_ver < 2)
dev_err(dev, "unsupported ACKMD\n");
else
data |= (0x4 << 12);
break;
}
switch (ret & SH_FSI_BPFMD_MASK) {
default:
/* FALL THROUGH */
case SH_FSI_BPFMD_32:
data |= (0x0 << 8);
break;
case SH_FSI_BPFMD_64:
data |= (0x1 << 8);
break;
case SH_FSI_BPFMD_128:
data |= (0x2 << 8);
break;
case SH_FSI_BPFMD_256:
data |= (0x3 << 8);
break;
case SH_FSI_BPFMD_512:
data |= (0x4 << 8);
break;
case SH_FSI_BPFMD_16:
if (fsi_ver < 2)
dev_err(dev, "unsupported ACKMD\n");
else
data |= (0x7 << 8);
break;
}
fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
udelay(10);
ret = 0;
}
return ret;
}
#define fsi_port_start(f, i) __fsi_port_clk_ctrl(f, i, 1)
#define fsi_port_stop(f, i) __fsi_port_clk_ctrl(f, i, 0)
static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int is_play, int enable)
{
struct fsi_master *master = fsi_get_master(fsi);
u32 clk = fsi_is_port_a(fsi) ? CRA : CRB;
if (enable)
fsi_irq_enable(fsi, is_play);
else
fsi_irq_disable(fsi, is_play);
if (fsi_is_clk_master(fsi))
fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
}
@ -598,18 +689,19 @@ static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int enable)
*/
static void fsi_fifo_init(struct fsi_priv *fsi,
int is_play,
struct snd_soc_dai *dai)
struct device *dev)
{
struct fsi_master *master = fsi_get_master(fsi);
struct fsi_stream *io = fsi_get_stream(fsi, is_play);
u32 shift, i;
int frame_capa;
/* get on-chip RAM capacity */
shift = fsi_master_read(master, FIFO_SZ);
shift >>= fsi_get_port_shift(fsi, is_play);
shift &= FIFO_SZ_MASK;
io->fifo_max_num = 256 << shift;
dev_dbg(dai->dev, "fifo = %d words\n", io->fifo_max_num);
frame_capa = 256 << shift;
dev_dbg(dev, "fifo = %d words\n", frame_capa);
/*
* The maximum number of sample data varies depending
@ -631,9 +723,11 @@ static void fsi_fifo_init(struct fsi_priv *fsi,
* 8 channels: 32 ( 32 x 8 = 256)
*/
for (i = 1; i < fsi->chan_num; i <<= 1)
io->fifo_max_num >>= 1;
dev_dbg(dai->dev, "%d channel %d store\n",
fsi->chan_num, io->fifo_max_num);
frame_capa >>= 1;
dev_dbg(dev, "%d channel %d store\n",
fsi->chan_num, frame_capa);
io->fifo_sample_capa = fsi_frame2sample(fsi, frame_capa);
/*
* set interrupt generation factor
@ -654,10 +748,10 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
struct snd_pcm_substream *substream = NULL;
int is_play = fsi_stream_is_play(stream);
struct fsi_stream *io = fsi_get_stream(fsi, is_play);
int data_residue_num;
int data_num;
int data_num_max;
int ch_width;
int sample_residues;
int sample_width;
int samples;
int samples_max;
int over_period;
void (*fn)(struct fsi_priv *fsi, int size);
@ -673,36 +767,35 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
/* FSI FIFO has limit.
* So, this driver can not send periods data at a time
*/
if (io->buff_offset >=
fsi_num2offset(io->period_num + 1, io->period_len)) {
if (io->buff_sample_pos >=
io->period_samples * (io->period_pos + 1)) {
over_period = 1;
io->period_num = (io->period_num + 1) % runtime->periods;
io->period_pos = (io->period_pos + 1) % runtime->periods;
if (0 == io->period_num)
io->buff_offset = 0;
if (0 == io->period_pos)
io->buff_sample_pos = 0;
}
/* get 1 channel data width */
ch_width = fsi_get_frame_width(fsi, is_play);
/* get 1 sample data width */
sample_width = samples_to_bytes(runtime, 1);
/* get residue data number of alsa */
data_residue_num = fsi_len2num(io->buff_len - io->buff_offset,
ch_width);
/* get number of residue samples */
sample_residues = io->buff_sample_capa - io->buff_sample_pos;
if (is_play) {
/*
* for play-back
*
* data_num_max : number of FSI fifo free space
* data_num : number of ALSA residue data
* samples_max : number of FSI fifo free samples space
* samples : number of ALSA residue samples
*/
data_num_max = io->fifo_max_num * fsi->chan_num;
data_num_max -= fsi_get_fifo_data_num(fsi, is_play);
samples_max = io->fifo_sample_capa;
samples_max -= fsi_get_current_fifo_samples(fsi, is_play);
data_num = data_residue_num;
samples = sample_residues;
switch (ch_width) {
switch (sample_width) {
case 2:
fn = fsi_dma_soft_push16;
break;
@ -716,13 +809,13 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
/*
* for capture
*
* data_num_max : number of ALSA free space
* data_num : number of data in FSI fifo
* samples_max : number of ALSA free samples space
* samples : number of samples in FSI fifo
*/
data_num_max = data_residue_num;
data_num = fsi_get_fifo_data_num(fsi, is_play);
samples_max = sample_residues;
samples = fsi_get_current_fifo_samples(fsi, is_play);
switch (ch_width) {
switch (sample_width) {
case 2:
fn = fsi_dma_soft_pop16;
break;
@ -734,12 +827,12 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
}
}
data_num = min(data_num, data_num_max);
samples = min(samples, samples_max);
fn(fsi, data_num);
fn(fsi, samples);
/* update buff_offset */
io->buff_offset += fsi_num2offset(data_num, ch_width);
/* update buff_sample_pos */
io->buff_sample_pos += samples;
if (over_period)
snd_pcm_period_elapsed(substream);
@ -788,16 +881,20 @@ static irqreturn_t fsi_interrupt(int irq, void *data)
* dai ops
*/
static int fsi_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
static int fsi_hw_startup(struct fsi_priv *fsi,
int is_play,
struct device *dev)
{
struct fsi_priv *fsi = fsi_get_priv(substream);
u32 flags = fsi_get_info_flags(fsi);
u32 data;
int is_play = fsi_is_play(substream);
u32 data = 0;
pm_runtime_get_sync(dai->dev);
pm_runtime_get_sync(dev);
/* clock setting */
if (fsi_is_clk_master(fsi))
data = DIMD | DOMD;
fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data);
/* clock inversion (CKG2) */
data = 0;
@ -812,54 +909,70 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
fsi_reg_write(fsi, CKG2, data);
/* set format */
fsi_reg_write(fsi, DO_FMT, fsi->do_fmt);
fsi_reg_write(fsi, DI_FMT, fsi->di_fmt);
/* spdif ? */
if (fsi_is_spdif(fsi)) {
fsi_spdif_clk_ctrl(fsi, 1);
fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD);
}
/* irq clear */
fsi_irq_disable(fsi, is_play);
fsi_irq_clear_status(fsi);
/* fifo init */
fsi_fifo_init(fsi, is_play, dai);
fsi_fifo_init(fsi, is_play, dev);
return 0;
}
static void fsi_hw_shutdown(struct fsi_priv *fsi,
int is_play,
struct device *dev)
{
if (fsi_is_clk_master(fsi))
fsi_set_master_clk(dev, fsi, fsi->rate, 0);
pm_runtime_put_sync(dev);
}
static int fsi_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsi_priv *fsi = fsi_get_priv(substream);
int is_play = fsi_is_play(substream);
return fsi_hw_startup(fsi, is_play, dai->dev);
}
static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsi_priv *fsi = fsi_get_priv(substream);
int is_play = fsi_is_play(substream);
struct fsi_master *master = fsi_get_master(fsi);
set_rate_func set_rate = fsi_get_info_set_rate(master);
fsi_irq_disable(fsi, is_play);
if (fsi_is_clk_master(fsi))
set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0);
fsi_hw_shutdown(fsi, is_play, dai->dev);
fsi->rate = 0;
pm_runtime_put_sync(dai->dev);
}
static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct fsi_priv *fsi = fsi_get_priv(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int is_play = fsi_is_play(substream);
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
fsi_stream_push(fsi, is_play, substream,
frames_to_bytes(runtime, runtime->buffer_size),
frames_to_bytes(runtime, runtime->period_size));
fsi_stream_push(fsi, is_play, substream);
ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi);
fsi_irq_enable(fsi, is_play);
fsi_port_start(fsi);
fsi_port_start(fsi, is_play);
break;
case SNDRV_PCM_TRIGGER_STOP:
fsi_port_stop(fsi);
fsi_irq_disable(fsi, is_play);
fsi_port_stop(fsi, is_play);
fsi_stream_pop(fsi, is_play);
break;
}
@ -884,8 +997,8 @@ static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt)
return -EINVAL;
}
fsi_reg_write(fsi, DO_FMT, data);
fsi_reg_write(fsi, DI_FMT, data);
fsi->do_fmt = data;
fsi->di_fmt = data;
return 0;
}
@ -900,11 +1013,10 @@ static int fsi_set_fmt_spdif(struct fsi_priv *fsi)
data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM;
fsi->chan_num = 2;
fsi_spdif_clk_ctrl(fsi, 1);
fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD);
fsi->spdif = 1;
fsi_reg_write(fsi, DO_FMT, data);
fsi_reg_write(fsi, DI_FMT, data);
fsi->do_fmt = data;
fsi->di_fmt = data;
return 0;
}
@ -915,32 +1027,24 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
struct fsi_master *master = fsi_get_master(fsi);
set_rate_func set_rate = fsi_get_info_set_rate(master);
u32 flags = fsi_get_info_flags(fsi);
u32 data = 0;
int ret;
pm_runtime_get_sync(dai->dev);
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
data = DIMD | DOMD;
fsi->clk_master = 1;
break;
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
ret = -EINVAL;
goto set_fmt_exit;
return -EINVAL;
}
if (fsi_is_clk_master(fsi) && !set_rate) {
dev_err(dai->dev, "platform doesn't have set_rate\n");
ret = -EINVAL;
goto set_fmt_exit;
return -EINVAL;
}
fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data);
/* set format */
switch (flags & SH_FSI_FMT_MASK) {
case SH_FSI_FMT_DAI:
@ -953,9 +1057,6 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
ret = -EINVAL;
}
set_fmt_exit:
pm_runtime_put_sync(dai->dev);
return ret;
}
@ -964,79 +1065,19 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsi_priv *fsi = fsi_get_priv(substream);
struct fsi_master *master = fsi_get_master(fsi);
set_rate_func set_rate = fsi_get_info_set_rate(master);
int fsi_ver = master->core->ver;
long rate = params_rate(params);
int ret;
if (!fsi_is_clk_master(fsi))
return 0;
ret = set_rate(dai->dev, fsi_is_port_a(fsi), rate, 1);
if (ret < 0) /* error */
ret = fsi_set_master_clk(dai->dev, fsi, rate, 1);
if (ret < 0)
return ret;
fsi->rate = rate;
if (ret > 0) {
u32 data = 0;
switch (ret & SH_FSI_ACKMD_MASK) {
default:
/* FALL THROUGH */
case SH_FSI_ACKMD_512:
data |= (0x0 << 12);
break;
case SH_FSI_ACKMD_256:
data |= (0x1 << 12);
break;
case SH_FSI_ACKMD_128:
data |= (0x2 << 12);
break;
case SH_FSI_ACKMD_64:
data |= (0x3 << 12);
break;
case SH_FSI_ACKMD_32:
if (fsi_ver < 2)
dev_err(dai->dev, "unsupported ACKMD\n");
else
data |= (0x4 << 12);
break;
}
switch (ret & SH_FSI_BPFMD_MASK) {
default:
/* FALL THROUGH */
case SH_FSI_BPFMD_32:
data |= (0x0 << 8);
break;
case SH_FSI_BPFMD_64:
data |= (0x1 << 8);
break;
case SH_FSI_BPFMD_128:
data |= (0x2 << 8);
break;
case SH_FSI_BPFMD_256:
data |= (0x3 << 8);
break;
case SH_FSI_BPFMD_512:
data |= (0x4 << 8);
break;
case SH_FSI_BPFMD_16:
if (fsi_ver < 2)
dev_err(dai->dev, "unsupported ACKMD\n");
else
data |= (0x7 << 8);
break;
}
fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
udelay(10);
ret = 0;
}
return ret;
}
static struct snd_soc_dai_ops fsi_dai_ops = {
@ -1097,16 +1138,14 @@ static int fsi_hw_free(struct snd_pcm_substream *substream)
static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsi_priv *fsi = fsi_get_priv(substream);
struct fsi_stream *io = fsi_get_stream(fsi, fsi_is_play(substream));
long location;
int samples_pos = io->buff_sample_pos - 1;
location = (io->buff_offset - 1);
if (location < 0)
location = 0;
if (samples_pos < 0)
samples_pos = 0;
return bytes_to_frames(runtime, location);
return fsi_sample2frame(fsi, samples_pos);
}
static struct snd_pcm_ops fsi_pcm_ops = {
@ -1129,10 +1168,10 @@ static void fsi_pcm_free(struct snd_pcm *pcm)
snd_pcm_lib_preallocate_free_for_all(pcm);
}
static int fsi_pcm_new(struct snd_card *card,
struct snd_soc_dai *dai,
struct snd_pcm *pcm)
static int fsi_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
/*
* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
* in MMAP mode (i.e. aplay -M)
@ -1246,8 +1285,6 @@ static int fsi_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
dev_set_drvdata(&pdev->dev, master);
fsi_module_init(master, &pdev->dev);
ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED,
id_entry->name, master);
if (ret) {
@ -1290,8 +1327,6 @@ static int fsi_remove(struct platform_device *pdev)
master = dev_get_drvdata(&pdev->dev);
fsi_module_kill(master, &pdev->dev);
free_irq(master->irq, master);
pm_runtime_disable(&pdev->dev);
@ -1305,53 +1340,43 @@ static int fsi_remove(struct platform_device *pdev)
}
static void __fsi_suspend(struct fsi_priv *fsi,
struct device *dev,
set_rate_func set_rate)
int is_play,
struct device *dev)
{
fsi->saved_do_fmt = fsi_reg_read(fsi, DO_FMT);
fsi->saved_di_fmt = fsi_reg_read(fsi, DI_FMT);
fsi->saved_ckg1 = fsi_reg_read(fsi, CKG1);
fsi->saved_ckg2 = fsi_reg_read(fsi, CKG2);
fsi->saved_out_sel = fsi_reg_read(fsi, OUT_SEL);
if (!fsi_stream_is_working(fsi, is_play))
return;
if (fsi_is_clk_master(fsi))
set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 0);
fsi_port_stop(fsi, is_play);
fsi_hw_shutdown(fsi, is_play, dev);
}
static void __fsi_resume(struct fsi_priv *fsi,
struct device *dev,
set_rate_func set_rate)
int is_play,
struct device *dev)
{
fsi_reg_write(fsi, DO_FMT, fsi->saved_do_fmt);
fsi_reg_write(fsi, DI_FMT, fsi->saved_di_fmt);
fsi_reg_write(fsi, CKG1, fsi->saved_ckg1);
fsi_reg_write(fsi, CKG2, fsi->saved_ckg2);
fsi_reg_write(fsi, OUT_SEL, fsi->saved_out_sel);
if (!fsi_stream_is_working(fsi, is_play))
return;
fsi_hw_startup(fsi, is_play, dev);
if (fsi_is_clk_master(fsi) && fsi->rate)
fsi_set_master_clk(dev, fsi, fsi->rate, 1);
fsi_port_start(fsi, is_play);
if (fsi_is_clk_master(fsi))
set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 1);
}
static int fsi_suspend(struct device *dev)
{
struct fsi_master *master = dev_get_drvdata(dev);
set_rate_func set_rate = fsi_get_info_set_rate(master);
struct fsi_priv *fsia = &master->fsia;
struct fsi_priv *fsib = &master->fsib;
pm_runtime_get_sync(dev);
__fsi_suspend(fsia, 1, dev);
__fsi_suspend(fsia, 0, dev);
__fsi_suspend(&master->fsia, dev, set_rate);
__fsi_suspend(&master->fsib, dev, set_rate);
master->saved_a_mclk = fsi_core_read(master, a_mclk);
master->saved_b_mclk = fsi_core_read(master, b_mclk);
master->saved_iemsk = fsi_core_read(master, iemsk);
master->saved_imsk = fsi_core_read(master, imsk);
master->saved_clk_rst = fsi_master_read(master, CLK_RST);
master->saved_soft_rst = fsi_master_read(master, SOFT_RST);
fsi_module_kill(master, dev);
pm_runtime_put_sync(dev);
__fsi_suspend(fsib, 1, dev);
__fsi_suspend(fsib, 0, dev);
return 0;
}
@ -1359,23 +1384,14 @@ static int fsi_suspend(struct device *dev)
static int fsi_resume(struct device *dev)
{
struct fsi_master *master = dev_get_drvdata(dev);
set_rate_func set_rate = fsi_get_info_set_rate(master);
struct fsi_priv *fsia = &master->fsia;
struct fsi_priv *fsib = &master->fsib;
pm_runtime_get_sync(dev);
__fsi_resume(fsia, 1, dev);
__fsi_resume(fsia, 0, dev);
fsi_module_init(master, dev);
fsi_master_mask_set(master, SOFT_RST, 0xffff, master->saved_soft_rst);
fsi_master_mask_set(master, CLK_RST, 0xffff, master->saved_clk_rst);
fsi_core_mask_set(master, a_mclk, 0xffff, master->saved_a_mclk);
fsi_core_mask_set(master, b_mclk, 0xffff, master->saved_b_mclk);
fsi_core_mask_set(master, iemsk, 0xffff, master->saved_iemsk);
fsi_core_mask_set(master, imsk, 0xffff, master->saved_imsk);
__fsi_resume(&master->fsia, dev, set_rate);
__fsi_resume(&master->fsib, dev, set_rate);
pm_runtime_put_sync(dev);
__fsi_resume(fsib, 1, dev);
__fsi_resume(fsib, 0, dev);
return 0;
}

View File

@ -527,10 +527,11 @@ static snd_pcm_uframes_t siu_pcm_pointer_dma(struct snd_pcm_substream *ss)
return bytes_to_frames(ss->runtime, ptr);
}
static int siu_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
static int siu_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
/* card->dev == socdev->dev, see snd_soc_new_pcms() */
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
struct siu_info *info = siu_i2s_data;
struct platform_device *pdev = to_platform_device(card->dev);
int ret;

View File

@ -20,422 +20,6 @@
#include <trace/events/asoc.h>
#ifdef CONFIG_SPI_MASTER
static int do_spi_write(void *control, const char *data, int len)
{
struct spi_device *spi = control;
int ret;
ret = spi_write(spi, data, len);
if (ret < 0)
return ret;
return len;
}
#endif
static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value, const void *data, int len)
{
int ret;
if (!snd_soc_codec_volatile_register(codec, reg) &&
reg < codec->driver->reg_cache_size &&
!codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0)
return -1;
}
if (codec->cache_only) {
codec->cache_sync = 1;
return 0;
}
ret = codec->hw_write(codec->control_data, data, len);
if (ret == len)
return 0;
if (ret < 0)
return ret;
else
return -EIO;
}
static unsigned int do_hw_read(struct snd_soc_codec *codec, unsigned int reg)
{
int ret;
unsigned int val;
if (reg >= codec->driver->reg_cache_size ||
snd_soc_codec_volatile_register(codec, reg) ||
codec->cache_bypass) {
if (codec->cache_only)
return -1;
BUG_ON(!codec->hw_read);
return codec->hw_read(codec, reg);
}
ret = snd_soc_cache_read(codec, reg, &val);
if (ret < 0)
return -1;
return val;
}
static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
unsigned int reg)
{
return do_hw_read(codec, reg);
}
static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u16 data;
data = cpu_to_be16((reg << 12) | (value & 0xffffff));
return do_hw_write(codec, reg, value, &data, 2);
}
static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
unsigned int reg)
{
return do_hw_read(codec, reg);
}
static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[2];
data[0] = (reg << 1) | ((value >> 8) & 0x0001);
data[1] = value & 0x00ff;
return do_hw_write(codec, reg, value, data, 2);
}
static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[2];
reg &= 0xff;
data[0] = reg;
data[1] = value & 0xff;
return do_hw_write(codec, reg, value, data, 2);
}
static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec,
unsigned int reg)
{
return do_hw_read(codec, reg);
}
static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[3];
data[0] = reg;
data[1] = (value >> 8) & 0xff;
data[2] = value & 0xff;
return do_hw_write(codec, reg, value, data, 3);
}
static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec,
unsigned int reg)
{
return do_hw_read(codec, reg);
}
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int do_i2c_read(struct snd_soc_codec *codec,
void *reg, int reglen,
void *data, int datalen)
{
struct i2c_msg xfer[2];
int ret;
struct i2c_client *client = codec->control_data;
/* Write register */
xfer[0].addr = client->addr;
xfer[0].flags = 0;
xfer[0].len = reglen;
xfer[0].buf = reg;
/* Read data */
xfer[1].addr = client->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = datalen;
xfer[1].buf = data;
ret = i2c_transfer(client->adapter, xfer, 2);
if (ret == 2)
return 0;
else if (ret < 0)
return ret;
else
return -EIO;
}
#endif
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
u8 reg = r;
u8 data;
int ret;
ret = do_i2c_read(codec, &reg, 1, &data, 1);
if (ret < 0)
return 0;
return data;
}
#else
#define snd_soc_8_8_read_i2c NULL
#endif
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
u8 reg = r;
u16 data;
int ret;
ret = do_i2c_read(codec, &reg, 1, &data, 2);
if (ret < 0)
return 0;
return (data >> 8) | ((data & 0xff) << 8);
}
#else
#define snd_soc_8_16_read_i2c NULL
#endif
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
u16 reg = r;
u8 data;
int ret;
ret = do_i2c_read(codec, &reg, 2, &data, 1);
if (ret < 0)
return 0;
return data;
}
#else
#define snd_soc_16_8_read_i2c NULL
#endif
static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec,
unsigned int reg)
{
return do_hw_read(codec, reg);
}
static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[3];
data[0] = (reg >> 8) & 0xff;
data[1] = reg & 0xff;
data[2] = value;
return do_hw_write(codec, reg, value, data, 3);
}
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
u16 reg = cpu_to_be16(r);
u16 data;
int ret;
ret = do_i2c_read(codec, &reg, 2, &data, 2);
if (ret < 0)
return 0;
return be16_to_cpu(data);
}
#else
#define snd_soc_16_16_read_i2c NULL
#endif
static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec,
unsigned int reg)
{
return do_hw_read(codec, reg);
}
static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[4];
data[0] = (reg >> 8) & 0xff;
data[1] = reg & 0xff;
data[2] = (value >> 8) & 0xff;
data[3] = value & 0xff;
return do_hw_write(codec, reg, value, data, 4);
}
/* Primitive bulk write support for soc-cache. The data pointed to by
* `data' needs to already be in the form the hardware expects
* including any leading register specific data. Any data written
* through this function will not go through the cache as it only
* handles writing to volatile or out of bounds registers.
*/
static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int reg,
const void *data, size_t len)
{
int ret;
/* To ensure that we don't get out of sync with the cache, check
* whether the base register is volatile or if we've directly asked
* to bypass the cache. Out of bounds registers are considered
* volatile.
*/
if (!codec->cache_bypass
&& !snd_soc_codec_volatile_register(codec, reg)
&& reg < codec->driver->reg_cache_size)
return -EINVAL;
switch (codec->control_type) {
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
case SND_SOC_I2C:
ret = i2c_master_send(codec->control_data, data, len);
break;
#endif
#if defined(CONFIG_SPI_MASTER)
case SND_SOC_SPI:
ret = spi_write(codec->control_data, data, len);
break;
#endif
default:
BUG();
}
if (ret == len)
return 0;
if (ret < 0)
return ret;
else
return -EIO;
}
static struct {
int addr_bits;
int data_bits;
int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int);
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
} io_types[] = {
{
.addr_bits = 4, .data_bits = 12,
.write = snd_soc_4_12_write, .read = snd_soc_4_12_read,
},
{
.addr_bits = 7, .data_bits = 9,
.write = snd_soc_7_9_write, .read = snd_soc_7_9_read,
},
{
.addr_bits = 8, .data_bits = 8,
.write = snd_soc_8_8_write, .read = snd_soc_8_8_read,
.i2c_read = snd_soc_8_8_read_i2c,
},
{
.addr_bits = 8, .data_bits = 16,
.write = snd_soc_8_16_write, .read = snd_soc_8_16_read,
.i2c_read = snd_soc_8_16_read_i2c,
},
{
.addr_bits = 16, .data_bits = 8,
.write = snd_soc_16_8_write, .read = snd_soc_16_8_read,
.i2c_read = snd_soc_16_8_read_i2c,
},
{
.addr_bits = 16, .data_bits = 16,
.write = snd_soc_16_16_write, .read = snd_soc_16_16_read,
.i2c_read = snd_soc_16_16_read_i2c,
},
};
/**
* snd_soc_codec_set_cache_io: Set up standard I/O functions.
*
* @codec: CODEC to configure.
* @addr_bits: Number of bits of register address data.
* @data_bits: Number of bits of data per register.
* @control: Control bus used.
*
* Register formats are frequently shared between many I2C and SPI
* devices. In order to promote code reuse the ASoC core provides
* some standard implementations of CODEC read and write operations
* which can be set up using this function.
*
* The caller is responsible for allocating and initialising the
* actual cache.
*
* Note that at present this code cannot be used by CODECs with
* volatile registers.
*/
int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
int addr_bits, int data_bits,
enum snd_soc_control_type control)
{
int i;
for (i = 0; i < ARRAY_SIZE(io_types); i++)
if (io_types[i].addr_bits == addr_bits &&
io_types[i].data_bits == data_bits)
break;
if (i == ARRAY_SIZE(io_types)) {
printk(KERN_ERR
"No I/O functions for %d bit address %d bit data\n",
addr_bits, data_bits);
return -EINVAL;
}
codec->write = io_types[i].write;
codec->read = io_types[i].read;
codec->bulk_write_raw = snd_soc_hw_bulk_write_raw;
switch (control) {
case SND_SOC_I2C:
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
codec->hw_write = (hw_write_t)i2c_master_send;
#endif
if (io_types[i].i2c_read)
codec->hw_read = io_types[i].i2c_read;
codec->control_data = container_of(codec->dev,
struct i2c_client,
dev);
break;
case SND_SOC_SPI:
#ifdef CONFIG_SPI_MASTER
codec->hw_write = do_spi_write;
#endif
codec->control_data = container_of(codec->dev,
struct spi_device,
dev);
break;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
static bool snd_soc_set_cache_val(void *base, unsigned int idx,
unsigned int val, unsigned int word_size)
{
@ -483,31 +67,86 @@ static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx,
}
struct snd_soc_rbtree_node {
struct rb_node node;
unsigned int reg;
unsigned int value;
unsigned int defval;
struct rb_node node; /* the actual rbtree node holding this block */
unsigned int base_reg; /* base register handled by this block */
unsigned int word_size; /* number of bytes needed to represent the register index */
void *block; /* block of adjacent registers */
unsigned int blklen; /* number of registers available in the block */
} __attribute__ ((packed));
struct snd_soc_rbtree_ctx {
struct rb_root root;
struct snd_soc_rbtree_node *cached_rbnode;
};
static inline void snd_soc_rbtree_get_base_top_reg(
struct snd_soc_rbtree_node *rbnode,
unsigned int *base, unsigned int *top)
{
*base = rbnode->base_reg;
*top = rbnode->base_reg + rbnode->blklen - 1;
}
static unsigned int snd_soc_rbtree_get_register(
struct snd_soc_rbtree_node *rbnode, unsigned int idx)
{
unsigned int val;
switch (rbnode->word_size) {
case 1: {
u8 *p = rbnode->block;
val = p[idx];
return val;
}
case 2: {
u16 *p = rbnode->block;
val = p[idx];
return val;
}
default:
BUG();
break;
}
return -1;
}
static void snd_soc_rbtree_set_register(struct snd_soc_rbtree_node *rbnode,
unsigned int idx, unsigned int val)
{
switch (rbnode->word_size) {
case 1: {
u8 *p = rbnode->block;
p[idx] = val;
break;
}
case 2: {
u16 *p = rbnode->block;
p[idx] = val;
break;
}
default:
BUG();
break;
}
}
static struct snd_soc_rbtree_node *snd_soc_rbtree_lookup(
struct rb_root *root, unsigned int reg)
{
struct rb_node *node;
struct snd_soc_rbtree_node *rbnode;
unsigned int base_reg, top_reg;
node = root->rb_node;
while (node) {
rbnode = container_of(node, struct snd_soc_rbtree_node, node);
if (rbnode->reg < reg)
node = node->rb_left;
else if (rbnode->reg > reg)
node = node->rb_right;
else
snd_soc_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
if (reg >= base_reg && reg <= top_reg)
return rbnode;
else if (reg > top_reg)
node = node->rb_right;
else if (reg < base_reg)
node = node->rb_left;
}
return NULL;
@ -518,19 +157,28 @@ static int snd_soc_rbtree_insert(struct rb_root *root,
{
struct rb_node **new, *parent;
struct snd_soc_rbtree_node *rbnode_tmp;
unsigned int base_reg_tmp, top_reg_tmp;
unsigned int base_reg;
parent = NULL;
new = &root->rb_node;
while (*new) {
rbnode_tmp = container_of(*new, struct snd_soc_rbtree_node,
node);
/* base and top registers of the current rbnode */
snd_soc_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp,
&top_reg_tmp);
/* base register of the rbnode to be added */
base_reg = rbnode->base_reg;
parent = *new;
if (rbnode_tmp->reg < rbnode->reg)
new = &((*new)->rb_left);
else if (rbnode_tmp->reg > rbnode->reg)
new = &((*new)->rb_right);
else
/* if this register has already been inserted, just return */
if (base_reg >= base_reg_tmp &&
base_reg <= top_reg_tmp)
return 0;
else if (base_reg > top_reg_tmp)
new = &((*new)->rb_right);
else if (base_reg < base_reg_tmp)
new = &((*new)->rb_left);
}
/* insert the node into the rbtree */
@ -545,58 +193,146 @@ static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec)
struct snd_soc_rbtree_ctx *rbtree_ctx;
struct rb_node *node;
struct snd_soc_rbtree_node *rbnode;
unsigned int val;
unsigned int regtmp;
unsigned int val, def;
int ret;
int i;
rbtree_ctx = codec->reg_cache;
for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
rbnode = rb_entry(node, struct snd_soc_rbtree_node, node);
if (rbnode->value == rbnode->defval)
continue;
WARN_ON(codec->writable_register &&
codec->writable_register(codec, rbnode->reg));
ret = snd_soc_cache_read(codec, rbnode->reg, &val);
if (ret)
return ret;
codec->cache_bypass = 1;
ret = snd_soc_write(codec, rbnode->reg, val);
codec->cache_bypass = 0;
if (ret)
return ret;
dev_dbg(codec->dev, "Synced register %#x, value = %#x\n",
rbnode->reg, val);
for (i = 0; i < rbnode->blklen; ++i) {
regtmp = rbnode->base_reg + i;
WARN_ON(codec->writable_register &&
codec->writable_register(codec, regtmp));
val = snd_soc_rbtree_get_register(rbnode, i);
def = snd_soc_get_cache_val(codec->reg_def_copy, i,
rbnode->word_size);
if (val == def)
continue;
codec->cache_bypass = 1;
ret = snd_soc_write(codec, regtmp, val);
codec->cache_bypass = 0;
if (ret)
return ret;
dev_dbg(codec->dev, "Synced register %#x, value = %#x\n",
regtmp, val);
}
}
return 0;
}
static int snd_soc_rbtree_insert_to_block(struct snd_soc_rbtree_node *rbnode,
unsigned int pos, unsigned int reg,
unsigned int value)
{
u8 *blk;
blk = krealloc(rbnode->block,
(rbnode->blklen + 1) * rbnode->word_size, GFP_KERNEL);
if (!blk)
return -ENOMEM;
/* insert the register value in the correct place in the rbnode block */
memmove(blk + (pos + 1) * rbnode->word_size,
blk + pos * rbnode->word_size,
(rbnode->blklen - pos) * rbnode->word_size);
/* update the rbnode block, its size and the base register */
rbnode->block = blk;
rbnode->blklen++;
if (!pos)
rbnode->base_reg = reg;
snd_soc_rbtree_set_register(rbnode, pos, value);
return 0;
}
static int snd_soc_rbtree_cache_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int value)
{
struct snd_soc_rbtree_ctx *rbtree_ctx;
struct snd_soc_rbtree_node *rbnode;
struct snd_soc_rbtree_node *rbnode, *rbnode_tmp;
struct rb_node *node;
unsigned int val;
unsigned int reg_tmp;
unsigned int base_reg, top_reg;
unsigned int pos;
int i;
int ret;
rbtree_ctx = codec->reg_cache;
/* look up the required register in the cached rbnode */
rbnode = rbtree_ctx->cached_rbnode;
if (rbnode) {
snd_soc_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
if (reg >= base_reg && reg <= top_reg) {
reg_tmp = reg - base_reg;
val = snd_soc_rbtree_get_register(rbnode, reg_tmp);
if (val == value)
return 0;
snd_soc_rbtree_set_register(rbnode, reg_tmp, value);
return 0;
}
}
/* if we can't locate it in the cached rbnode we'll have
* to traverse the rbtree looking for it.
*/
rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg);
if (rbnode) {
if (rbnode->value == value)
reg_tmp = reg - rbnode->base_reg;
val = snd_soc_rbtree_get_register(rbnode, reg_tmp);
if (val == value)
return 0;
rbnode->value = value;
snd_soc_rbtree_set_register(rbnode, reg_tmp, value);
rbtree_ctx->cached_rbnode = rbnode;
} else {
/* bail out early, no need to create the rbnode yet */
if (!value)
return 0;
/*
* for uninitialized registers whose value is changed
* from the default zero, create an rbnode and insert
* it into the tree.
/* look for an adjacent register to the one we are about to add */
for (node = rb_first(&rbtree_ctx->root); node;
node = rb_next(node)) {
rbnode_tmp = rb_entry(node, struct snd_soc_rbtree_node, node);
for (i = 0; i < rbnode_tmp->blklen; ++i) {
reg_tmp = rbnode_tmp->base_reg + i;
if (abs(reg_tmp - reg) != 1)
continue;
/* decide where in the block to place our register */
if (reg_tmp + 1 == reg)
pos = i + 1;
else
pos = i;
ret = snd_soc_rbtree_insert_to_block(rbnode_tmp, pos,
reg, value);
if (ret)
return ret;
rbtree_ctx->cached_rbnode = rbnode_tmp;
return 0;
}
}
/* we did not manage to find a place to insert it in an existing
* block so create a new rbnode with a single register in its block.
* This block will get populated further if any other adjacent
* registers get modified in the future.
*/
rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL);
if (!rbnode)
return -ENOMEM;
rbnode->reg = reg;
rbnode->value = value;
rbnode->blklen = 1;
rbnode->base_reg = reg;
rbnode->word_size = codec->driver->reg_word_size;
rbnode->block = kmalloc(rbnode->blklen * rbnode->word_size,
GFP_KERNEL);
if (!rbnode->block) {
kfree(rbnode);
return -ENOMEM;
}
snd_soc_rbtree_set_register(rbnode, 0, value);
snd_soc_rbtree_insert(&rbtree_ctx->root, rbnode);
rbtree_ctx->cached_rbnode = rbnode;
}
return 0;
@ -607,11 +343,28 @@ static int snd_soc_rbtree_cache_read(struct snd_soc_codec *codec,
{
struct snd_soc_rbtree_ctx *rbtree_ctx;
struct snd_soc_rbtree_node *rbnode;
unsigned int base_reg, top_reg;
unsigned int reg_tmp;
rbtree_ctx = codec->reg_cache;
/* look up the required register in the cached rbnode */
rbnode = rbtree_ctx->cached_rbnode;
if (rbnode) {
snd_soc_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
if (reg >= base_reg && reg <= top_reg) {
reg_tmp = reg - base_reg;
*value = snd_soc_rbtree_get_register(rbnode, reg_tmp);
return 0;
}
}
/* if we can't locate it in the cached rbnode we'll have
* to traverse the rbtree looking for it.
*/
rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg);
if (rbnode) {
*value = rbnode->value;
reg_tmp = reg - rbnode->base_reg;
*value = snd_soc_rbtree_get_register(rbnode, reg_tmp);
rbtree_ctx->cached_rbnode = rbnode;
} else {
/* uninitialized registers default to 0 */
*value = 0;
@ -637,6 +390,7 @@ static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec)
rbtree_node = rb_entry(next, struct snd_soc_rbtree_node, node);
next = rb_next(&rbtree_node->node);
rb_erase(&rbtree_node->node, &rbtree_ctx->root);
kfree(rbtree_node->block);
kfree(rbtree_node);
}
@ -649,10 +403,9 @@ static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec)
static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec)
{
struct snd_soc_rbtree_node *rbtree_node;
struct snd_soc_rbtree_ctx *rbtree_ctx;
unsigned int val;
unsigned int word_size;
unsigned int val;
int i;
int ret;
@ -662,32 +415,27 @@ static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec)
rbtree_ctx = codec->reg_cache;
rbtree_ctx->root = RB_ROOT;
rbtree_ctx->cached_rbnode = NULL;
if (!codec->reg_def_copy)
return 0;
/*
* populate the rbtree with the initialized registers. All other
* registers will be inserted when they are first modified.
*/
word_size = codec->driver->reg_word_size;
for (i = 0; i < codec->driver->reg_cache_size; ++i) {
val = snd_soc_get_cache_val(codec->reg_def_copy, i, word_size);
val = snd_soc_get_cache_val(codec->reg_def_copy, i,
word_size);
if (!val)
continue;
rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL);
if (!rbtree_node) {
ret = -ENOMEM;
snd_soc_cache_exit(codec);
break;
}
rbtree_node->reg = i;
rbtree_node->value = val;
rbtree_node->defval = val;
snd_soc_rbtree_insert(&rbtree_ctx->root, rbtree_node);
ret = snd_soc_rbtree_cache_write(codec, i, val);
if (ret)
goto err;
}
return 0;
err:
snd_soc_cache_exit(codec);
return ret;
}
#ifdef CONFIG_SND_SOC_CACHE_LZO

View File

@ -44,7 +44,6 @@
#define NAME_SIZE 32
static DEFINE_MUTEX(pcm_mutex);
static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
#ifdef CONFIG_DEBUG_FS
@ -58,7 +57,7 @@ static LIST_HEAD(dai_list);
static LIST_HEAD(platform_list);
static LIST_HEAD(codec_list);
static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
/*
* This is a timeout to do a DAPM powerdown after a stream is closed().
@ -485,552 +484,6 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
}
#endif
static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream)
{
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;
if (!codec_dai->driver->symmetric_rates &&
!cpu_dai->driver->symmetric_rates &&
!rtd->dai_link->symmetric_rates)
return 0;
/* This can happen if multiple streams are starting simultaneously -
* the second can need to get its constraints before the first has
* picked a rate. Complain and allow the application to carry on.
*/
if (!rtd->rate) {
dev_warn(&rtd->dev,
"Not enforcing symmetric_rates due to race\n");
return 0;
}
dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", rtd->rate);
ret = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
rtd->rate, rtd->rate);
if (ret < 0) {
dev_err(&rtd->dev,
"Unable to apply rate symmetry constraint: %d\n", ret);
return ret;
}
return 0;
}
/*
* Called by ALSA when a PCM substream is opened, the runtime->hw record is
* then initialized and any private data can be allocated. This also calls
* startup for the cpu DAI, platform, machine and codec DAI.
*/
static int soc_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver;
int ret = 0;
mutex_lock(&pcm_mutex);
/* startup the audio subsystem */
if (cpu_dai->driver->ops->startup) {
ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open interface %s\n",
cpu_dai->name);
goto out;
}
}
if (platform->driver->ops && platform->driver->ops->open) {
ret = platform->driver->ops->open(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open platform %s\n", platform->name);
goto platform_err;
}
}
if (codec_dai->driver->ops->startup) {
ret = codec_dai->driver->ops->startup(substream, codec_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open codec %s\n",
codec_dai->name);
goto codec_dai_err;
}
}
if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
ret = rtd->dai_link->ops->startup(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: %s startup failed\n", rtd->dai_link->name);
goto machine_err;
}
}
/* Check that the codec and cpu DAIs are compatible */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
runtime->hw.rate_min =
max(codec_dai_drv->playback.rate_min,
cpu_dai_drv->playback.rate_min);
runtime->hw.rate_max =
min(codec_dai_drv->playback.rate_max,
cpu_dai_drv->playback.rate_max);
runtime->hw.channels_min =
max(codec_dai_drv->playback.channels_min,
cpu_dai_drv->playback.channels_min);
runtime->hw.channels_max =
min(codec_dai_drv->playback.channels_max,
cpu_dai_drv->playback.channels_max);
runtime->hw.formats =
codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats;
runtime->hw.rates =
codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates;
if (codec_dai_drv->playback.rates
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
runtime->hw.rates |= cpu_dai_drv->playback.rates;
if (cpu_dai_drv->playback.rates
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
runtime->hw.rates |= codec_dai_drv->playback.rates;
} else {
runtime->hw.rate_min =
max(codec_dai_drv->capture.rate_min,
cpu_dai_drv->capture.rate_min);
runtime->hw.rate_max =
min(codec_dai_drv->capture.rate_max,
cpu_dai_drv->capture.rate_max);
runtime->hw.channels_min =
max(codec_dai_drv->capture.channels_min,
cpu_dai_drv->capture.channels_min);
runtime->hw.channels_max =
min(codec_dai_drv->capture.channels_max,
cpu_dai_drv->capture.channels_max);
runtime->hw.formats =
codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats;
runtime->hw.rates =
codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates;
if (codec_dai_drv->capture.rates
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
runtime->hw.rates |= cpu_dai_drv->capture.rates;
if (cpu_dai_drv->capture.rates
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
runtime->hw.rates |= codec_dai_drv->capture.rates;
}
ret = -EINVAL;
snd_pcm_limit_hw_rates(runtime);
if (!runtime->hw.rates) {
printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
codec_dai->name, cpu_dai->name);
goto config_err;
}
if (!runtime->hw.formats) {
printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
codec_dai->name, cpu_dai->name);
goto config_err;
}
if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
runtime->hw.channels_min > runtime->hw.channels_max) {
printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
codec_dai->name, cpu_dai->name);
goto config_err;
}
/* Symmetry only applies if we've already got an active stream. */
if (cpu_dai->active || codec_dai->active) {
ret = soc_pcm_apply_symmetry(substream);
if (ret != 0)
goto config_err;
}
pr_debug("asoc: %s <-> %s info:\n",
codec_dai->name, cpu_dai->name);
pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
runtime->hw.channels_max);
pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
runtime->hw.rate_max);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
cpu_dai->playback_active++;
codec_dai->playback_active++;
} else {
cpu_dai->capture_active++;
codec_dai->capture_active++;
}
cpu_dai->active++;
codec_dai->active++;
rtd->codec->active++;
mutex_unlock(&pcm_mutex);
return 0;
config_err:
if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
rtd->dai_link->ops->shutdown(substream);
machine_err:
if (codec_dai->driver->ops->shutdown)
codec_dai->driver->ops->shutdown(substream, codec_dai);
codec_dai_err:
if (platform->driver->ops && platform->driver->ops->close)
platform->driver->ops->close(substream);
platform_err:
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
out:
mutex_unlock(&pcm_mutex);
return ret;
}
/*
* Power down the audio subsystem pmdown_time msecs after close is called.
* This is to ensure there are no pops or clicks in between any music tracks
* due to DAPM power cycling.
*/
static void close_delayed_work(struct work_struct *work)
{
struct snd_soc_pcm_runtime *rtd =
container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
struct snd_soc_dai *codec_dai = rtd->codec_dai;
mutex_lock(&pcm_mutex);
pr_debug("pop wq checking: %s status: %s waiting: %s\n",
codec_dai->driver->playback.stream_name,
codec_dai->playback_active ? "active" : "inactive",
codec_dai->pop_wait ? "yes" : "no");
/* are we waiting on this codec DAI stream */
if (codec_dai->pop_wait == 1) {
codec_dai->pop_wait = 0;
snd_soc_dapm_stream_event(rtd,
codec_dai->driver->playback.stream_name,
SND_SOC_DAPM_STREAM_STOP);
}
mutex_unlock(&pcm_mutex);
}
/*
* Called by ALSA when a PCM substream is closed. Private data can be
* freed here. The cpu DAI, codec DAI, machine and platform are also
* shutdown.
*/
static int soc_codec_close(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_codec *codec = rtd->codec;
mutex_lock(&pcm_mutex);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
cpu_dai->playback_active--;
codec_dai->playback_active--;
} else {
cpu_dai->capture_active--;
codec_dai->capture_active--;
}
cpu_dai->active--;
codec_dai->active--;
codec->active--;
/* Muting the DAC suppresses artifacts caused during digital
* shutdown, for example from stopping clocks.
*/
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_soc_dai_digital_mute(codec_dai, 1);
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
if (codec_dai->driver->ops->shutdown)
codec_dai->driver->ops->shutdown(substream, codec_dai);
if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
rtd->dai_link->ops->shutdown(substream);
if (platform->driver->ops && platform->driver->ops->close)
platform->driver->ops->close(substream);
cpu_dai->runtime = NULL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* start delayed pop wq here for playback streams */
codec_dai->pop_wait = 1;
schedule_delayed_work(&rtd->delayed_work,
msecs_to_jiffies(rtd->pmdown_time));
} else {
/* capture streams can be powered down now */
snd_soc_dapm_stream_event(rtd,
codec_dai->driver->capture.stream_name,
SND_SOC_DAPM_STREAM_STOP);
}
mutex_unlock(&pcm_mutex);
return 0;
}
/*
* Called by ALSA when the PCM substream is prepared, can set format, sample
* rate, etc. This function is non atomic and can be called multiple times,
* it can refer to the runtime info.
*/
static int soc_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret = 0;
mutex_lock(&pcm_mutex);
if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) {
ret = rtd->dai_link->ops->prepare(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: machine prepare error\n");
goto out;
}
}
if (platform->driver->ops && platform->driver->ops->prepare) {
ret = platform->driver->ops->prepare(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: platform prepare error\n");
goto out;
}
}
if (codec_dai->driver->ops->prepare) {
ret = codec_dai->driver->ops->prepare(substream, codec_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: codec DAI prepare error\n");
goto out;
}
}
if (cpu_dai->driver->ops->prepare) {
ret = cpu_dai->driver->ops->prepare(substream, cpu_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: cpu DAI prepare error\n");
goto out;
}
}
/* cancel any delayed stream shutdown that is pending */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
codec_dai->pop_wait) {
codec_dai->pop_wait = 0;
cancel_delayed_work(&rtd->delayed_work);
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_soc_dapm_stream_event(rtd,
codec_dai->driver->playback.stream_name,
SND_SOC_DAPM_STREAM_START);
else
snd_soc_dapm_stream_event(rtd,
codec_dai->driver->capture.stream_name,
SND_SOC_DAPM_STREAM_START);
snd_soc_dai_digital_mute(codec_dai, 0);
out:
mutex_unlock(&pcm_mutex);
return ret;
}
/*
* Called by ALSA when the hardware params are set by application. This
* function can also be called multiple times and can allocate buffers
* (using snd_pcm_lib_* ). It's non-atomic.
*/
static int soc_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_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret = 0;
mutex_lock(&pcm_mutex);
if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
ret = rtd->dai_link->ops->hw_params(substream, params);
if (ret < 0) {
printk(KERN_ERR "asoc: machine hw_params failed\n");
goto out;
}
}
if (codec_dai->driver->ops->hw_params) {
ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: can't set codec %s hw params\n",
codec_dai->name);
goto codec_err;
}
}
if (cpu_dai->driver->ops->hw_params) {
ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: interface %s hw params failed\n",
cpu_dai->name);
goto interface_err;
}
}
if (platform->driver->ops && platform->driver->ops->hw_params) {
ret = platform->driver->ops->hw_params(substream, params);
if (ret < 0) {
printk(KERN_ERR "asoc: platform %s hw params failed\n",
platform->name);
goto platform_err;
}
}
rtd->rate = params_rate(params);
out:
mutex_unlock(&pcm_mutex);
return ret;
platform_err:
if (cpu_dai->driver->ops->hw_free)
cpu_dai->driver->ops->hw_free(substream, cpu_dai);
interface_err:
if (codec_dai->driver->ops->hw_free)
codec_dai->driver->ops->hw_free(substream, codec_dai);
codec_err:
if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
rtd->dai_link->ops->hw_free(substream);
mutex_unlock(&pcm_mutex);
return ret;
}
/*
* Frees resources allocated by hw_params, can be called multiple times
*/
static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_codec *codec = rtd->codec;
mutex_lock(&pcm_mutex);
/* apply codec digital mute */
if (!codec->active)
snd_soc_dai_digital_mute(codec_dai, 1);
/* free any machine hw params */
if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
rtd->dai_link->ops->hw_free(substream);
/* free any DMA resources */
if (platform->driver->ops && platform->driver->ops->hw_free)
platform->driver->ops->hw_free(substream);
/* now free hw params for the DAIs */
if (codec_dai->driver->ops->hw_free)
codec_dai->driver->ops->hw_free(substream, codec_dai);
if (cpu_dai->driver->ops->hw_free)
cpu_dai->driver->ops->hw_free(substream, cpu_dai);
mutex_unlock(&pcm_mutex);
return 0;
}
static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret;
if (codec_dai->driver->ops->trigger) {
ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai);
if (ret < 0)
return ret;
}
if (platform->driver->ops && platform->driver->ops->trigger) {
ret = platform->driver->ops->trigger(substream, cmd);
if (ret < 0)
return ret;
}
if (cpu_dai->driver->ops->trigger) {
ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai);
if (ret < 0)
return ret;
}
return 0;
}
/*
* soc level wrapper for pointer callback
* If cpu_dai, codec_dai, platform driver has the delay callback, than
* the runtime->delay will be updated accordingly.
*/
static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t offset = 0;
snd_pcm_sframes_t delay = 0;
if (platform->driver->ops && platform->driver->ops->pointer)
offset = platform->driver->ops->pointer(substream);
if (cpu_dai->driver->ops->delay)
delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
if (codec_dai->driver->ops->delay)
delay += codec_dai->driver->ops->delay(substream, codec_dai);
if (platform->driver->delay)
delay += platform->driver->delay(substream, codec_dai);
runtime->delay = delay;
return offset;
}
/* ASoC PCM operations */
static struct snd_pcm_ops soc_pcm_ops = {
.open = soc_pcm_open,
.close = soc_codec_close,
.hw_params = soc_pcm_hw_params,
.hw_free = soc_pcm_hw_free,
.prepare = soc_pcm_prepare,
.trigger = soc_pcm_trigger,
.pointer = soc_pcm_pointer,
};
#ifdef CONFIG_PM_SLEEP
/* powers down audio subsystem for suspend */
int snd_soc_suspend(struct device *dev)
@ -1256,7 +709,7 @@ static void soc_resume_deferred(struct work_struct *work)
int snd_soc_resume(struct device *dev)
{
struct snd_soc_card *card = dev_get_drvdata(dev);
int i;
int i, ac97_control = 0;
/* AC97 devices might have other drivers hanging off them so
* need to resume immediately. Other drivers don't have that
@ -1265,14 +718,15 @@ int snd_soc_resume(struct device *dev)
*/
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
if (cpu_dai->driver->ac97_control) {
dev_dbg(dev, "Resuming AC97 immediately\n");
soc_resume_deferred(&card->deferred_resume_work);
} else {
dev_dbg(dev, "Scheduling resume work\n");
if (!schedule_work(&card->deferred_resume_work))
dev_err(dev, "resume work item may be lost\n");
}
ac97_control |= cpu_dai->driver->ac97_control;
}
if (ac97_control) {
dev_dbg(dev, "Resuming AC97 immediately\n");
soc_resume_deferred(&card->deferred_resume_work);
} else {
dev_dbg(dev, "Scheduling resume work\n");
if (!schedule_work(&card->deferred_resume_work))
dev_err(dev, "resume work item may be lost\n");
}
return 0;
@ -1393,7 +847,7 @@ static void soc_remove_codec(struct snd_soc_codec *codec)
module_put(codec->dev->driver->owner);
}
static void soc_remove_dai_link(struct snd_soc_card *card, int num)
static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)
{
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_codec *codec = rtd->codec;
@ -1410,7 +864,8 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
}
/* remove the CODEC DAI */
if (codec_dai && codec_dai->probed) {
if (codec_dai && codec_dai->probed &&
codec_dai->driver->remove_order == order) {
if (codec_dai->driver->remove) {
err = codec_dai->driver->remove(codec_dai);
if (err < 0)
@ -1421,7 +876,8 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
}
/* remove the platform */
if (platform && platform->probed) {
if (platform && platform->probed &&
platform->driver->remove_order == order) {
if (platform->driver->remove) {
err = platform->driver->remove(platform);
if (err < 0)
@ -1433,11 +889,13 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
}
/* remove the CODEC */
if (codec && codec->probed)
if (codec && codec->probed &&
codec->driver->remove_order == order)
soc_remove_codec(codec);
/* remove the cpu_dai */
if (cpu_dai && cpu_dai->probed) {
if (cpu_dai && cpu_dai->probed &&
cpu_dai->driver->remove_order == order) {
if (cpu_dai->driver->remove) {
err = cpu_dai->driver->remove(cpu_dai);
if (err < 0)
@ -1451,11 +909,13 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
static void soc_remove_dai_links(struct snd_soc_card *card)
{
int i;
for (i = 0; i < card->num_rtd; i++)
soc_remove_dai_link(card, i);
int dai, order;
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
order++) {
for (dai = 0; dai < card->num_rtd; dai++)
soc_remove_dai_link(card, dai, order);
}
card->num_rtd = 0;
}
@ -1526,6 +986,52 @@ 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;
platform->dapm.card = card;
if (!try_module_get(platform->dev->driver->owner))
return -ENODEV;
if (driver->dapm_widgets)
snd_soc_dapm_new_controls(&platform->dapm,
driver->dapm_widgets, driver->num_dapm_widgets);
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;
}
}
if (driver->controls)
snd_soc_add_platform_controls(platform, driver->controls,
driver->num_controls);
if (driver->dapm_routes)
snd_soc_dapm_add_routes(&platform->dapm, driver->dapm_routes,
driver->num_dapm_routes);
/* mark platform as probed and add to card platform list */
platform->probed = 1;
list_add(&platform->card_list, &card->platform_dev_list);
list_add(&platform->dapm.list, &card->dapm_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,
@ -1572,6 +1078,7 @@ static int soc_post_component_init(struct snd_soc_card *card,
rtd->dev.parent = card->dev;
rtd->dev.release = rtd_release;
rtd->dev.init_name = name;
mutex_init(&rtd->pcm_mutex);
ret = device_register(&rtd->dev);
if (ret < 0) {
dev_err(card->dev,
@ -1596,7 +1103,7 @@ static int soc_post_component_init(struct snd_soc_card *card,
return 0;
}
static int soc_probe_dai_link(struct snd_soc_card *card, int num)
static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
{
struct snd_soc_dai_link *dai_link = &card->dai_link[num];
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
@ -1605,7 +1112,8 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;
int ret;
dev_dbg(card->dev, "probe %s dai link %d\n", card->name, num);
dev_dbg(card->dev, "probe %s dai link %d late %d\n",
card->name, num, order);
/* config components */
codec_dai->codec = codec;
@ -1617,7 +1125,8 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
rtd->pmdown_time = pmdown_time;
/* probe the cpu_dai */
if (!cpu_dai->probed) {
if (!cpu_dai->probed &&
cpu_dai->driver->probe_order == order) {
if (!try_module_get(cpu_dai->dev->driver->owner))
return -ENODEV;
@ -1636,33 +1145,23 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
}
/* probe the CODEC */
if (!codec->probed) {
if (!codec->probed &&
codec->driver->probe_order == order) {
ret = soc_probe_codec(card, codec);
if (ret < 0)
return ret;
}
/* probe the platform */
if (!platform->probed) {
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);
if (!platform->probed &&
platform->driver->probe_order == order) {
ret = soc_probe_platform(card, platform);
if (ret < 0)
return ret;
}
/* probe the CODEC DAI */
if (!codec_dai->probed) {
if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
if (codec_dai->driver->probe) {
ret = codec_dai->driver->probe(codec_dai);
if (ret < 0) {
@ -1677,8 +1176,9 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
list_add(&codec_dai->card_list, &card->dai_dev_list);
}
/* DAPM dai link stream work */
INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
/* complete DAI probe during last probe */
if (order != SND_SOC_COMP_ORDER_LAST)
return 0;
ret = soc_post_component_init(card, codec, num, 0);
if (ret)
@ -1817,7 +1317,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
struct snd_soc_codec *codec;
struct snd_soc_codec_conf *codec_conf;
enum snd_soc_compress_type compress_type;
int ret, i;
int ret, i, order;
mutex_lock(&card->mutex);
@ -1895,12 +1395,16 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
goto card_probe_error;
}
for (i = 0; i < card->num_links; i++) {
ret = soc_probe_dai_link(card, i);
if (ret < 0) {
pr_err("asoc: failed to instantiate card %s: %d\n",
/* early DAI link probe */
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
order++) {
for (i = 0; i < card->num_links; i++) {
ret = soc_probe_dai_link(card, i, order);
if (ret < 0) {
pr_err("asoc: failed to instantiate card %s: %d\n",
card->name, ret);
goto probe_dai_err;
goto probe_dai_err;
}
}
}
@ -2096,67 +1600,6 @@ static struct platform_driver soc_driver = {
.remove = soc_remove,
};
/* create a new pcm */
static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_pcm *pcm;
char new_name[64];
int ret = 0, playback = 0, capture = 0;
/* check client and interface hw capabilities */
snprintf(new_name, sizeof(new_name), "%s %s-%d",
rtd->dai_link->stream_name, codec_dai->name, num);
if (codec_dai->driver->playback.channels_min)
playback = 1;
if (codec_dai->driver->capture.channels_min)
capture = 1;
dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name);
ret = snd_pcm_new(rtd->card->snd_card, new_name,
num, playback, capture, &pcm);
if (ret < 0) {
printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
return ret;
}
rtd->pcm = pcm;
pcm->private_data = rtd;
if (platform->driver->ops) {
soc_pcm_ops.mmap = platform->driver->ops->mmap;
soc_pcm_ops.pointer = platform->driver->ops->pointer;
soc_pcm_ops.ioctl = platform->driver->ops->ioctl;
soc_pcm_ops.copy = platform->driver->ops->copy;
soc_pcm_ops.silence = platform->driver->ops->silence;
soc_pcm_ops.ack = platform->driver->ops->ack;
soc_pcm_ops.page = platform->driver->ops->page;
}
if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
if (platform->driver->pcm_new) {
ret = platform->driver->pcm_new(rtd->card->snd_card,
codec_dai, pcm);
if (ret < 0) {
pr_err("asoc: platform pcm constructor failed\n");
return ret;
}
}
pcm->private_free = platform->driver->pcm_free;
printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
cpu_dai->name);
return ret;
}
/**
* snd_soc_codec_volatile_register: Report if a register is volatile.
*
@ -2211,6 +1654,38 @@ 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);
trace_snd_soc_preg_read(platform, 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);
trace_snd_soc_preg_write(platform, 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
@ -2323,7 +1798,7 @@ int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
return ret;
old = ret;
new = (old & ~mask) | value;
new = (old & ~mask) | (value & mask);
change = old != new;
if (change) {
ret = snd_soc_write(codec, reg, new);
@ -2489,6 +1964,36 @@ int snd_soc_add_controls(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_soc_add_controls);
/**
* snd_soc_add_platform_controls - add an array of controls to a platform.
* Convienience function to add a list of controls.
*
* @platform: platform to add controls to
* @controls: array of controls to add
* @num_controls: number of elements in the array
*
* Return 0 for success, else error.
*/
int snd_soc_add_platform_controls(struct snd_soc_platform *platform,
const struct snd_kcontrol_new *controls, int num_controls)
{
struct snd_card *card = platform->card->snd_card;
int err, i;
for (i = 0; i < num_controls; i++) {
const struct snd_kcontrol_new *control = &controls[i];
err = snd_ctl_add(card, snd_soc_cnew(control, platform,
control->name, NULL));
if (err < 0) {
dev_err(platform->dev, "Failed to add %s %d\n",control->name, err);
return err;
}
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_add_platform_controls);
/**
* snd_soc_info_enum_double - enumerated double mixer info callback
* @kcontrol: mixer control
@ -3633,6 +3138,8 @@ int snd_soc_register_platform(struct device *dev,
platform->dev = dev;
platform->driver = platform_drv;
platform->dapm.dev = dev;
platform->dapm.platform = platform;
mutex_lock(&client_mutex);
list_add(&platform->list, &platform_list);

View File

@ -124,6 +124,51 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
}
static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg)
{
if (w->codec)
return snd_soc_read(w->codec, reg);
else if (w->platform)
return snd_soc_platform_read(w->platform, reg);
dev_err(w->dapm->dev, "no valid widget read method\n");
return -1;
}
static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg, int val)
{
if (w->codec)
return snd_soc_write(w->codec, reg, val);
else if (w->platform)
return snd_soc_platform_write(w->platform, reg, val);
dev_err(w->dapm->dev, "no valid widget write method\n");
return -1;
}
static int soc_widget_update_bits(struct snd_soc_dapm_widget *w,
unsigned short reg, unsigned int mask, unsigned int value)
{
int change;
unsigned int old, new;
int ret;
ret = soc_widget_read(w, reg);
if (ret < 0)
return ret;
old = ret;
new = (old & ~mask) | (value & mask);
change = old != new;
if (change) {
ret = soc_widget_write(w, reg, new);
if (ret < 0)
return ret;
}
return change;
}
/**
* snd_soc_dapm_set_bias_level - set the bias level for the system
* @dapm: DAPM context
@ -139,39 +184,26 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
struct snd_soc_card *card = dapm->card;
int ret = 0;
switch (level) {
case SND_SOC_BIAS_ON:
dev_dbg(dapm->dev, "Setting full bias\n");
break;
case SND_SOC_BIAS_PREPARE:
dev_dbg(dapm->dev, "Setting bias prepare\n");
break;
case SND_SOC_BIAS_STANDBY:
dev_dbg(dapm->dev, "Setting standby bias\n");
break;
case SND_SOC_BIAS_OFF:
dev_dbg(dapm->dev, "Setting bias off\n");
break;
default:
dev_err(dapm->dev, "Setting invalid bias %d\n", level);
return -EINVAL;
}
trace_snd_soc_bias_level_start(card, level);
if (card && card->set_bias_level)
ret = card->set_bias_level(card, level);
if (ret == 0) {
if (dapm->codec && dapm->codec->driver->set_bias_level)
ret = dapm->codec->driver->set_bias_level(dapm->codec, level);
ret = card->set_bias_level(card, dapm, level);
if (ret != 0)
goto out;
if (dapm->codec) {
if (dapm->codec->driver->set_bias_level)
ret = dapm->codec->driver->set_bias_level(dapm->codec,
level);
else
dapm->bias_level = level;
}
if (ret == 0) {
if (card && card->set_bias_level_post)
ret = card->set_bias_level_post(card, level);
}
if (ret != 0)
goto out;
if (card && card->set_bias_level_post)
ret = card->set_bias_level_post(card, dapm, level);
out:
trace_snd_soc_bias_level_done(card, level);
return ret;
@ -194,7 +226,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
val = snd_soc_read(w->codec, reg);
val = soc_widget_read(w, reg);
val = (val >> shift) & mask;
if ((invert && !val) || (!invert && val))
@ -209,8 +241,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
int val, item, bitmask;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
;
val = snd_soc_read(w->codec, e->reg);
;
val = soc_widget_read(w, e->reg);
item = (val >> e->shift_l) & (bitmask - 1);
p->connect = 0;
@ -240,7 +272,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
w->kcontrol_news[i].private_value;
int val, item;
val = snd_soc_read(w->codec, e->reg);
val = soc_widget_read(w, e->reg);
val = (val >> e->shift_l) & e->mask;
for (item = 0; item < e->max; item++) {
if (val == e->values[item])
@ -606,6 +638,9 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
}
list_for_each_entry(path, &widget->sinks, list_source) {
if (path->weak)
continue;
if (path->walked)
continue;
@ -656,6 +691,9 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
}
list_for_each_entry(path, &widget->sources, list_sink) {
if (path->weak)
continue;
if (path->walked)
continue;
@ -681,7 +719,7 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w,
else
val = w->off_val;
snd_soc_update_bits(w->codec, -(w->reg + 1),
soc_widget_update_bits(w, -(w->reg + 1),
w->mask << w->shift, val << w->shift);
return 0;
@ -737,6 +775,9 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
/* Check if one of our outputs is connected */
list_for_each_entry(path, &w->sinks, list_source) {
if (path->weak)
continue;
if (path->connected &&
!path->connected(path->source, path->sink))
continue;
@ -885,11 +926,17 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm,
}
if (reg >= 0) {
/* Any widget will do, they should all be updating the
* same register.
*/
w = list_first_entry(pending, struct snd_soc_dapm_widget,
power_list);
pop_dbg(dapm->dev, card->pop_time,
"pop test : Applying 0x%x/0x%x to %x in %dms\n",
value, mask, reg, card->pop_time);
pop_wait(card->pop_time);
snd_soc_update_bits(dapm->codec, reg, mask, value);
soc_widget_update_bits(w, reg, mask, value);
}
list_for_each_entry(w, pending, power_list) {
@ -942,7 +989,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
INIT_LIST_HEAD(&pending);
cur_sort = -1;
cur_subseq = -1;
cur_subseq = INT_MIN;
cur_reg = SND_SOC_NOPM;
cur_dapm = NULL;
}
@ -1041,16 +1088,17 @@ static void dapm_pre_sequence_async(void *data, async_cookie_t cookie)
struct snd_soc_dapm_context *d = data;
int ret;
if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) {
/* If we're off and we're not supposed to be go into STANDBY */
if (d->bias_level == SND_SOC_BIAS_OFF &&
d->target_bias_level != SND_SOC_BIAS_OFF) {
ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);
if (ret != 0)
dev_err(d->dev,
"Failed to turn on bias: %d\n", ret);
}
/* If we're changing to all on or all off then prepare */
if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) ||
(!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) {
/* Prepare for a STADDBY->ON or ON->STANDBY transition */
if (d->bias_level != d->target_bias_level) {
ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE);
if (ret != 0)
dev_err(d->dev,
@ -1067,7 +1115,9 @@ static void dapm_post_sequence_async(void *data, async_cookie_t cookie)
int ret;
/* If we just powered the last thing off drop to standby bias */
if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) {
if (d->bias_level == SND_SOC_BIAS_PREPARE &&
(d->target_bias_level == SND_SOC_BIAS_STANDBY ||
d->target_bias_level == SND_SOC_BIAS_OFF)) {
ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);
if (ret != 0)
dev_err(d->dev, "Failed to apply standby bias: %d\n",
@ -1075,14 +1125,16 @@ static void dapm_post_sequence_async(void *data, async_cookie_t cookie)
}
/* If we're in standby and can support bias off then do that */
if (d->bias_level == SND_SOC_BIAS_STANDBY && d->idle_bias_off) {
if (d->bias_level == SND_SOC_BIAS_STANDBY &&
d->target_bias_level == SND_SOC_BIAS_OFF) {
ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF);
if (ret != 0)
dev_err(d->dev, "Failed to turn off bias: %d\n", ret);
}
/* If we just powered up then move to active bias */
if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) {
if (d->bias_level == SND_SOC_BIAS_PREPARE &&
d->target_bias_level == SND_SOC_BIAS_ON) {
ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON);
if (ret != 0)
dev_err(d->dev, "Failed to apply active bias: %d\n",
@ -1107,13 +1159,19 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
LIST_HEAD(up_list);
LIST_HEAD(down_list);
LIST_HEAD(async_domain);
enum snd_soc_bias_level bias;
int power;
trace_snd_soc_dapm_start(card);
list_for_each_entry(d, &card->dapm_list, list)
if (d->n_widgets || d->codec == NULL)
d->dev_power = 0;
list_for_each_entry(d, &card->dapm_list, list) {
if (d->n_widgets || d->codec == NULL) {
if (d->idle_bias_off)
d->target_bias_level = SND_SOC_BIAS_OFF;
else
d->target_bias_level = SND_SOC_BIAS_STANDBY;
}
}
/* Check which widgets we need to power and store them in
* lists indicating if they should be powered up or down.
@ -1135,8 +1193,27 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
power = w->power_check(w);
else
power = 1;
if (power)
w->dapm->dev_power = 1;
if (power) {
d = w->dapm;
/* Supplies and micbiases only bring
* the context up to STANDBY as unless
* something else is active and
* passing audio they generally don't
* require full power.
*/
switch (w->id) {
case snd_soc_dapm_supply:
case snd_soc_dapm_micbias:
if (d->target_bias_level < SND_SOC_BIAS_STANDBY)
d->target_bias_level = SND_SOC_BIAS_STANDBY;
break;
default:
d->target_bias_level = SND_SOC_BIAS_ON;
break;
}
}
if (w->power == power)
continue;
@ -1160,24 +1237,19 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
switch (event) {
case SND_SOC_DAPM_STREAM_START:
case SND_SOC_DAPM_STREAM_RESUME:
dapm->dev_power = 1;
dapm->target_bias_level = SND_SOC_BIAS_ON;
break;
case SND_SOC_DAPM_STREAM_STOP:
dapm->dev_power = !!dapm->codec->active;
if (dapm->codec->active)
dapm->target_bias_level = SND_SOC_BIAS_ON;
else
dapm->target_bias_level = SND_SOC_BIAS_STANDBY;
break;
case SND_SOC_DAPM_STREAM_SUSPEND:
dapm->dev_power = 0;
dapm->target_bias_level = SND_SOC_BIAS_STANDBY;
break;
case SND_SOC_DAPM_STREAM_NOP:
switch (dapm->bias_level) {
case SND_SOC_BIAS_STANDBY:
case SND_SOC_BIAS_OFF:
dapm->dev_power = 0;
break;
default:
dapm->dev_power = 1;
break;
}
dapm->target_bias_level = dapm->bias_level;
break;
default:
break;
@ -1185,12 +1257,12 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
}
/* Force all contexts in the card to the same bias state */
power = 0;
bias = SND_SOC_BIAS_OFF;
list_for_each_entry(d, &card->dapm_list, list)
if (d->dev_power)
power = 1;
if (d->target_bias_level > bias)
bias = d->target_bias_level;
list_for_each_entry(d, &card->dapm_list, list)
d->dev_power = power;
d->target_bias_level = bias;
/* Run all the bias changes in parallel */
@ -1794,6 +1866,84 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route)
{
struct snd_soc_dapm_widget *source = dapm_find_widget(dapm,
route->source,
true);
struct snd_soc_dapm_widget *sink = dapm_find_widget(dapm,
route->sink,
true);
struct snd_soc_dapm_path *path;
int count = 0;
if (!source) {
dev_err(dapm->dev, "Unable to find source %s for weak route\n",
route->source);
return -ENODEV;
}
if (!sink) {
dev_err(dapm->dev, "Unable to find sink %s for weak route\n",
route->sink);
return -ENODEV;
}
if (route->control || route->connected)
dev_warn(dapm->dev, "Ignoring control for weak route %s->%s\n",
route->source, route->sink);
list_for_each_entry(path, &source->sinks, list_source) {
if (path->sink == sink) {
path->weak = 1;
count++;
}
}
if (count == 0)
dev_err(dapm->dev, "No path found for weak route %s->%s\n",
route->source, route->sink);
if (count > 1)
dev_warn(dapm->dev, "%d paths found for weak route %s->%s\n",
count, route->source, route->sink);
return 0;
}
/**
* snd_soc_dapm_weak_routes - Mark routes between DAPM widgets as weak
* @dapm: DAPM context
* @route: audio routes
* @num: number of routes
*
* Mark existing routes matching those specified in the passed array
* as being weak, meaning that they are ignored for the purpose of
* power decisions. The main intended use case is for sidetone paths
* which couple audio between other independent paths if they are both
* active in order to make the combination work better at the user
* level but which aren't intended to be "used".
*
* Note that CODEC drivers should not use this as sidetone type paths
* can frequently also be used as bypass paths.
*/
int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num)
{
int i, err;
int ret = 0;
for (i = 0; i < num; i++) {
err = snd_soc_dapm_weak_route(dapm, route);
if (err)
ret = err;
route++;
}
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes);
/**
* snd_soc_dapm_new_widgets - add new dapm widgets
* @dapm: DAPM context
@ -1865,7 +2015,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
/* Read the initial power state from the device */
if (w->reg >= 0) {
val = snd_soc_read(w->codec, w->reg);
val = soc_widget_read(w, w->reg);
val &= 1 << w->shift;
if (w->invert)
val = !val;
@ -2353,6 +2503,7 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
dapm->n_widgets++;
w->dapm = dapm;
w->codec = dapm->codec;
w->platform = dapm->platform;
INIT_LIST_HEAD(&w->sources);
INIT_LIST_HEAD(&w->sinks);
INIT_LIST_HEAD(&w->list);

396
sound/soc/soc-io.c Normal file
View File

@ -0,0 +1,396 @@
/*
* soc-io.c -- ASoC register I/O helpers
*
* Copyright 2009-2011 Wolfson Microelectronics PLC.
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.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 <linux/i2c.h>
#include <linux/spi/spi.h>
#include <sound/soc.h>
#include <trace/events/asoc.h>
#ifdef CONFIG_SPI_MASTER
static int do_spi_write(void *control, const char *data, int len)
{
struct spi_device *spi = control;
int ret;
ret = spi_write(spi, data, len);
if (ret < 0)
return ret;
return len;
}
#endif
static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value, const void *data, int len)
{
int ret;
if (!snd_soc_codec_volatile_register(codec, reg) &&
reg < codec->driver->reg_cache_size &&
!codec->cache_bypass) {
ret = snd_soc_cache_write(codec, reg, value);
if (ret < 0)
return -1;
}
if (codec->cache_only) {
codec->cache_sync = 1;
return 0;
}
ret = codec->hw_write(codec->control_data, data, len);
if (ret == len)
return 0;
if (ret < 0)
return ret;
else
return -EIO;
}
static unsigned int hw_read(struct snd_soc_codec *codec, unsigned int reg)
{
int ret;
unsigned int val;
if (reg >= codec->driver->reg_cache_size ||
snd_soc_codec_volatile_register(codec, reg) ||
codec->cache_bypass) {
if (codec->cache_only)
return -1;
BUG_ON(!codec->hw_read);
return codec->hw_read(codec, reg);
}
ret = snd_soc_cache_read(codec, reg, &val);
if (ret < 0)
return -1;
return val;
}
static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u16 data;
data = cpu_to_be16((reg << 12) | (value & 0xffffff));
return do_hw_write(codec, reg, value, &data, 2);
}
static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u16 data;
data = cpu_to_be16((reg << 9) | (value & 0x1ff));
return do_hw_write(codec, reg, value, &data, 2);
}
static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[2];
reg &= 0xff;
data[0] = reg;
data[1] = value & 0xff;
return do_hw_write(codec, reg, value, data, 2);
}
static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[3];
u16 val = cpu_to_be16(value);
data[0] = reg;
memcpy(&data[1], &val, sizeof(val));
return do_hw_write(codec, reg, value, data, 3);
}
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int do_i2c_read(struct snd_soc_codec *codec,
void *reg, int reglen,
void *data, int datalen)
{
struct i2c_msg xfer[2];
int ret;
struct i2c_client *client = codec->control_data;
/* Write register */
xfer[0].addr = client->addr;
xfer[0].flags = 0;
xfer[0].len = reglen;
xfer[0].buf = reg;
/* Read data */
xfer[1].addr = client->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = datalen;
xfer[1].buf = data;
ret = i2c_transfer(client->adapter, xfer, 2);
if (ret == 2)
return 0;
else if (ret < 0)
return ret;
else
return -EIO;
}
#endif
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
u8 reg = r;
u8 data;
int ret;
ret = do_i2c_read(codec, &reg, 1, &data, 1);
if (ret < 0)
return 0;
return data;
}
#else
#define snd_soc_8_8_read_i2c NULL
#endif
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
u8 reg = r;
u16 data;
int ret;
ret = do_i2c_read(codec, &reg, 1, &data, 2);
if (ret < 0)
return 0;
return (data >> 8) | ((data & 0xff) << 8);
}
#else
#define snd_soc_8_16_read_i2c NULL
#endif
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
u16 reg = r;
u8 data;
int ret;
ret = do_i2c_read(codec, &reg, 2, &data, 1);
if (ret < 0)
return 0;
return data;
}
#else
#define snd_soc_16_8_read_i2c NULL
#endif
static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[3];
u16 rval = cpu_to_be16(reg);
memcpy(data, &rval, sizeof(rval));
data[2] = value;
return do_hw_write(codec, reg, value, data, 3);
}
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec,
unsigned int r)
{
u16 reg = cpu_to_be16(r);
u16 data;
int ret;
ret = do_i2c_read(codec, &reg, 2, &data, 2);
if (ret < 0)
return 0;
return be16_to_cpu(data);
}
#else
#define snd_soc_16_16_read_i2c NULL
#endif
static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u16 data[2];
data[0] = cpu_to_be16(reg);
data[1] = cpu_to_be16(value);
return do_hw_write(codec, reg, value, data, sizeof(data));
}
/* Primitive bulk write support for soc-cache. The data pointed to by
* `data' needs to already be in the form the hardware expects
* including any leading register specific data. Any data written
* through this function will not go through the cache as it only
* handles writing to volatile or out of bounds registers.
*/
static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int reg,
const void *data, size_t len)
{
int ret;
/* To ensure that we don't get out of sync with the cache, check
* whether the base register is volatile or if we've directly asked
* to bypass the cache. Out of bounds registers are considered
* volatile.
*/
if (!codec->cache_bypass
&& !snd_soc_codec_volatile_register(codec, reg)
&& reg < codec->driver->reg_cache_size)
return -EINVAL;
switch (codec->control_type) {
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
case SND_SOC_I2C:
ret = i2c_master_send(to_i2c_client(codec->dev), data, len);
break;
#endif
#if defined(CONFIG_SPI_MASTER)
case SND_SOC_SPI:
ret = spi_write(to_spi_device(codec->dev), data, len);
break;
#endif
default:
BUG();
}
if (ret == len)
return 0;
if (ret < 0)
return ret;
else
return -EIO;
}
static struct {
int addr_bits;
int data_bits;
int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int);
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
} io_types[] = {
{
.addr_bits = 4, .data_bits = 12,
.write = snd_soc_4_12_write,
},
{
.addr_bits = 7, .data_bits = 9,
.write = snd_soc_7_9_write,
},
{
.addr_bits = 8, .data_bits = 8,
.write = snd_soc_8_8_write,
.i2c_read = snd_soc_8_8_read_i2c,
},
{
.addr_bits = 8, .data_bits = 16,
.write = snd_soc_8_16_write,
.i2c_read = snd_soc_8_16_read_i2c,
},
{
.addr_bits = 16, .data_bits = 8,
.write = snd_soc_16_8_write,
.i2c_read = snd_soc_16_8_read_i2c,
},
{
.addr_bits = 16, .data_bits = 16,
.write = snd_soc_16_16_write,
.i2c_read = snd_soc_16_16_read_i2c,
},
};
/**
* snd_soc_codec_set_cache_io: Set up standard I/O functions.
*
* @codec: CODEC to configure.
* @addr_bits: Number of bits of register address data.
* @data_bits: Number of bits of data per register.
* @control: Control bus used.
*
* Register formats are frequently shared between many I2C and SPI
* devices. In order to promote code reuse the ASoC core provides
* some standard implementations of CODEC read and write operations
* which can be set up using this function.
*
* The caller is responsible for allocating and initialising the
* actual cache.
*
* Note that at present this code cannot be used by CODECs with
* volatile registers.
*/
int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
int addr_bits, int data_bits,
enum snd_soc_control_type control)
{
int i;
for (i = 0; i < ARRAY_SIZE(io_types); i++)
if (io_types[i].addr_bits == addr_bits &&
io_types[i].data_bits == data_bits)
break;
if (i == ARRAY_SIZE(io_types)) {
printk(KERN_ERR
"No I/O functions for %d bit address %d bit data\n",
addr_bits, data_bits);
return -EINVAL;
}
codec->write = io_types[i].write;
codec->read = hw_read;
codec->bulk_write_raw = snd_soc_hw_bulk_write_raw;
switch (control) {
case SND_SOC_I2C:
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
codec->hw_write = (hw_write_t)i2c_master_send;
#endif
if (io_types[i].i2c_read)
codec->hw_read = io_types[i].i2c_read;
codec->control_data = container_of(codec->dev,
struct i2c_client,
dev);
break;
case SND_SOC_SPI:
#ifdef CONFIG_SPI_MASTER
codec->hw_write = do_spi_write;
#endif
codec->control_data = container_of(codec->dev,
struct spi_device,
dev);
break;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);

639
sound/soc/soc-pcm.c Normal file
View File

@ -0,0 +1,639 @@
/*
* soc-pcm.c -- ALSA SoC PCM
*
* Copyright 2005 Wolfson Microelectronics PLC.
* Copyright 2005 Openedhand Ltd.
* Copyright (C) 2010 Slimlogic Ltd.
* Copyright (C) 2010 Texas Instruments Inc.
*
* Authors: Liam Girdwood <lrg@ti.com>
* Mark Brown <broonie@opensource.wolfsonmicro.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 <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
static DEFINE_MUTEX(pcm_mutex);
static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream)
{
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;
if (!codec_dai->driver->symmetric_rates &&
!cpu_dai->driver->symmetric_rates &&
!rtd->dai_link->symmetric_rates)
return 0;
/* This can happen if multiple streams are starting simultaneously -
* the second can need to get its constraints before the first has
* picked a rate. Complain and allow the application to carry on.
*/
if (!rtd->rate) {
dev_warn(&rtd->dev,
"Not enforcing symmetric_rates due to race\n");
return 0;
}
dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", rtd->rate);
ret = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
rtd->rate, rtd->rate);
if (ret < 0) {
dev_err(&rtd->dev,
"Unable to apply rate symmetry constraint: %d\n", ret);
return ret;
}
return 0;
}
/*
* Called by ALSA when a PCM substream is opened, the runtime->hw record is
* then initialized and any private data can be allocated. This also calls
* startup for the cpu DAI, platform, machine and codec DAI.
*/
static int soc_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver;
int ret = 0;
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
/* startup the audio subsystem */
if (cpu_dai->driver->ops->startup) {
ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open interface %s\n",
cpu_dai->name);
goto out;
}
}
if (platform->driver->ops && platform->driver->ops->open) {
ret = platform->driver->ops->open(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open platform %s\n", platform->name);
goto platform_err;
}
}
if (codec_dai->driver->ops->startup) {
ret = codec_dai->driver->ops->startup(substream, codec_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open codec %s\n",
codec_dai->name);
goto codec_dai_err;
}
}
if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
ret = rtd->dai_link->ops->startup(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: %s startup failed\n", rtd->dai_link->name);
goto machine_err;
}
}
/* Check that the codec and cpu DAIs are compatible */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
runtime->hw.rate_min =
max(codec_dai_drv->playback.rate_min,
cpu_dai_drv->playback.rate_min);
runtime->hw.rate_max =
min(codec_dai_drv->playback.rate_max,
cpu_dai_drv->playback.rate_max);
runtime->hw.channels_min =
max(codec_dai_drv->playback.channels_min,
cpu_dai_drv->playback.channels_min);
runtime->hw.channels_max =
min(codec_dai_drv->playback.channels_max,
cpu_dai_drv->playback.channels_max);
runtime->hw.formats =
codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats;
runtime->hw.rates =
codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates;
if (codec_dai_drv->playback.rates
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
runtime->hw.rates |= cpu_dai_drv->playback.rates;
if (cpu_dai_drv->playback.rates
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
runtime->hw.rates |= codec_dai_drv->playback.rates;
} else {
runtime->hw.rate_min =
max(codec_dai_drv->capture.rate_min,
cpu_dai_drv->capture.rate_min);
runtime->hw.rate_max =
min(codec_dai_drv->capture.rate_max,
cpu_dai_drv->capture.rate_max);
runtime->hw.channels_min =
max(codec_dai_drv->capture.channels_min,
cpu_dai_drv->capture.channels_min);
runtime->hw.channels_max =
min(codec_dai_drv->capture.channels_max,
cpu_dai_drv->capture.channels_max);
runtime->hw.formats =
codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats;
runtime->hw.rates =
codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates;
if (codec_dai_drv->capture.rates
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
runtime->hw.rates |= cpu_dai_drv->capture.rates;
if (cpu_dai_drv->capture.rates
& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
runtime->hw.rates |= codec_dai_drv->capture.rates;
}
ret = -EINVAL;
snd_pcm_limit_hw_rates(runtime);
if (!runtime->hw.rates) {
printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
codec_dai->name, cpu_dai->name);
goto config_err;
}
if (!runtime->hw.formats) {
printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
codec_dai->name, cpu_dai->name);
goto config_err;
}
if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
runtime->hw.channels_min > runtime->hw.channels_max) {
printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
codec_dai->name, cpu_dai->name);
goto config_err;
}
/* Symmetry only applies if we've already got an active stream. */
if (cpu_dai->active || codec_dai->active) {
ret = soc_pcm_apply_symmetry(substream);
if (ret != 0)
goto config_err;
}
pr_debug("asoc: %s <-> %s info:\n",
codec_dai->name, cpu_dai->name);
pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
runtime->hw.channels_max);
pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
runtime->hw.rate_max);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
cpu_dai->playback_active++;
codec_dai->playback_active++;
} else {
cpu_dai->capture_active++;
codec_dai->capture_active++;
}
cpu_dai->active++;
codec_dai->active++;
rtd->codec->active++;
mutex_unlock(&rtd->pcm_mutex);
return 0;
config_err:
if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
rtd->dai_link->ops->shutdown(substream);
machine_err:
if (codec_dai->driver->ops->shutdown)
codec_dai->driver->ops->shutdown(substream, codec_dai);
codec_dai_err:
if (platform->driver->ops && platform->driver->ops->close)
platform->driver->ops->close(substream);
platform_err:
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
out:
mutex_unlock(&rtd->pcm_mutex);
return ret;
}
/*
* Power down the audio subsystem pmdown_time msecs after close is called.
* This is to ensure there are no pops or clicks in between any music tracks
* due to DAPM power cycling.
*/
static void close_delayed_work(struct work_struct *work)
{
struct snd_soc_pcm_runtime *rtd =
container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
struct snd_soc_dai *codec_dai = rtd->codec_dai;
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
pr_debug("pop wq checking: %s status: %s waiting: %s\n",
codec_dai->driver->playback.stream_name,
codec_dai->playback_active ? "active" : "inactive",
codec_dai->pop_wait ? "yes" : "no");
/* are we waiting on this codec DAI stream */
if (codec_dai->pop_wait == 1) {
codec_dai->pop_wait = 0;
snd_soc_dapm_stream_event(rtd,
codec_dai->driver->playback.stream_name,
SND_SOC_DAPM_STREAM_STOP);
}
mutex_unlock(&rtd->pcm_mutex);
}
/*
* Called by ALSA when a PCM substream is closed. Private data can be
* freed here. The cpu DAI, codec DAI, machine and platform are also
* shutdown.
*/
static int soc_pcm_close(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_codec *codec = rtd->codec;
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
cpu_dai->playback_active--;
codec_dai->playback_active--;
} else {
cpu_dai->capture_active--;
codec_dai->capture_active--;
}
cpu_dai->active--;
codec_dai->active--;
codec->active--;
/* Muting the DAC suppresses artifacts caused during digital
* shutdown, for example from stopping clocks.
*/
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_soc_dai_digital_mute(codec_dai, 1);
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
if (codec_dai->driver->ops->shutdown)
codec_dai->driver->ops->shutdown(substream, codec_dai);
if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
rtd->dai_link->ops->shutdown(substream);
if (platform->driver->ops && platform->driver->ops->close)
platform->driver->ops->close(substream);
cpu_dai->runtime = NULL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* start delayed pop wq here for playback streams */
codec_dai->pop_wait = 1;
schedule_delayed_work(&rtd->delayed_work,
msecs_to_jiffies(rtd->pmdown_time));
} else {
/* capture streams can be powered down now */
snd_soc_dapm_stream_event(rtd,
codec_dai->driver->capture.stream_name,
SND_SOC_DAPM_STREAM_STOP);
}
mutex_unlock(&rtd->pcm_mutex);
return 0;
}
/*
* Called by ALSA when the PCM substream is prepared, can set format, sample
* rate, etc. This function is non atomic and can be called multiple times,
* it can refer to the runtime info.
*/
static int soc_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret = 0;
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) {
ret = rtd->dai_link->ops->prepare(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: machine prepare error\n");
goto out;
}
}
if (platform->driver->ops && platform->driver->ops->prepare) {
ret = platform->driver->ops->prepare(substream);
if (ret < 0) {
printk(KERN_ERR "asoc: platform prepare error\n");
goto out;
}
}
if (codec_dai->driver->ops->prepare) {
ret = codec_dai->driver->ops->prepare(substream, codec_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: codec DAI prepare error\n");
goto out;
}
}
if (cpu_dai->driver->ops->prepare) {
ret = cpu_dai->driver->ops->prepare(substream, cpu_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: cpu DAI prepare error\n");
goto out;
}
}
/* cancel any delayed stream shutdown that is pending */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
codec_dai->pop_wait) {
codec_dai->pop_wait = 0;
cancel_delayed_work(&rtd->delayed_work);
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_soc_dapm_stream_event(rtd,
codec_dai->driver->playback.stream_name,
SND_SOC_DAPM_STREAM_START);
else
snd_soc_dapm_stream_event(rtd,
codec_dai->driver->capture.stream_name,
SND_SOC_DAPM_STREAM_START);
snd_soc_dai_digital_mute(codec_dai, 0);
out:
mutex_unlock(&rtd->pcm_mutex);
return ret;
}
/*
* Called by ALSA when the hardware params are set by application. This
* function can also be called multiple times and can allocate buffers
* (using snd_pcm_lib_* ). It's non-atomic.
*/
static int soc_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_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret = 0;
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
ret = rtd->dai_link->ops->hw_params(substream, params);
if (ret < 0) {
printk(KERN_ERR "asoc: machine hw_params failed\n");
goto out;
}
}
if (codec_dai->driver->ops->hw_params) {
ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: can't set codec %s hw params\n",
codec_dai->name);
goto codec_err;
}
}
if (cpu_dai->driver->ops->hw_params) {
ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: interface %s hw params failed\n",
cpu_dai->name);
goto interface_err;
}
}
if (platform->driver->ops && platform->driver->ops->hw_params) {
ret = platform->driver->ops->hw_params(substream, params);
if (ret < 0) {
printk(KERN_ERR "asoc: platform %s hw params failed\n",
platform->name);
goto platform_err;
}
}
rtd->rate = params_rate(params);
out:
mutex_unlock(&rtd->pcm_mutex);
return ret;
platform_err:
if (cpu_dai->driver->ops->hw_free)
cpu_dai->driver->ops->hw_free(substream, cpu_dai);
interface_err:
if (codec_dai->driver->ops->hw_free)
codec_dai->driver->ops->hw_free(substream, codec_dai);
codec_err:
if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
rtd->dai_link->ops->hw_free(substream);
mutex_unlock(&rtd->pcm_mutex);
return ret;
}
/*
* Frees resources allocated by hw_params, can be called multiple times
*/
static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_codec *codec = rtd->codec;
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
/* apply codec digital mute */
if (!codec->active)
snd_soc_dai_digital_mute(codec_dai, 1);
/* free any machine hw params */
if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
rtd->dai_link->ops->hw_free(substream);
/* free any DMA resources */
if (platform->driver->ops && platform->driver->ops->hw_free)
platform->driver->ops->hw_free(substream);
/* now free hw params for the DAIs */
if (codec_dai->driver->ops->hw_free)
codec_dai->driver->ops->hw_free(substream, codec_dai);
if (cpu_dai->driver->ops->hw_free)
cpu_dai->driver->ops->hw_free(substream, cpu_dai);
mutex_unlock(&rtd->pcm_mutex);
return 0;
}
static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret;
if (codec_dai->driver->ops->trigger) {
ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai);
if (ret < 0)
return ret;
}
if (platform->driver->ops && platform->driver->ops->trigger) {
ret = platform->driver->ops->trigger(substream, cmd);
if (ret < 0)
return ret;
}
if (cpu_dai->driver->ops->trigger) {
ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai);
if (ret < 0)
return ret;
}
return 0;
}
/*
* soc level wrapper for pointer callback
* If cpu_dai, codec_dai, platform driver has the delay callback, than
* the runtime->delay will be updated accordingly.
*/
static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t offset = 0;
snd_pcm_sframes_t delay = 0;
if (platform->driver->ops && platform->driver->ops->pointer)
offset = platform->driver->ops->pointer(substream);
if (cpu_dai->driver->ops->delay)
delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
if (codec_dai->driver->ops->delay)
delay += codec_dai->driver->ops->delay(substream, codec_dai);
if (platform->driver->delay)
delay += platform->driver->delay(substream, codec_dai);
runtime->delay = delay;
return offset;
}
/* ASoC PCM operations */
static struct snd_pcm_ops soc_pcm_ops = {
.open = soc_pcm_open,
.close = soc_pcm_close,
.hw_params = soc_pcm_hw_params,
.hw_free = soc_pcm_hw_free,
.prepare = soc_pcm_prepare,
.trigger = soc_pcm_trigger,
.pointer = soc_pcm_pointer,
};
/* create a new pcm */
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_pcm *pcm;
char new_name[64];
int ret = 0, playback = 0, capture = 0;
/* check client and interface hw capabilities */
snprintf(new_name, sizeof(new_name), "%s %s-%d",
rtd->dai_link->stream_name, codec_dai->name, num);
if (codec_dai->driver->playback.channels_min)
playback = 1;
if (codec_dai->driver->capture.channels_min)
capture = 1;
dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name);
ret = snd_pcm_new(rtd->card->snd_card, new_name,
num, playback, capture, &pcm);
if (ret < 0) {
printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
return ret;
}
/* DAPM dai link stream work */
INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
rtd->pcm = pcm;
pcm->private_data = rtd;
if (platform->driver->ops) {
soc_pcm_ops.mmap = platform->driver->ops->mmap;
soc_pcm_ops.pointer = platform->driver->ops->pointer;
soc_pcm_ops.ioctl = platform->driver->ops->ioctl;
soc_pcm_ops.copy = platform->driver->ops->copy;
soc_pcm_ops.silence = platform->driver->ops->silence;
soc_pcm_ops.ack = platform->driver->ops->ack;
soc_pcm_ops.page = platform->driver->ops->page;
}
if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
if (platform->driver->pcm_new) {
ret = platform->driver->pcm_new(rtd);
if (ret < 0) {
pr_err("asoc: platform pcm constructor failed\n");
return ret;
}
}
pcm->private_free = platform->driver->pcm_free;
printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
cpu_dai->name);
return ret;
}

View File

@ -12,6 +12,15 @@ config SND_SOC_TEGRA_I2S
Tegra I2S interface. You will also need to select the individual
machine drivers to support below.
config SND_SOC_TEGRA_SPDIF
tristate
depends on SND_SOC_TEGRA
default m
help
Say Y or M if you want to add support for the SPDIF interface.
You will also need to select the individual machine drivers to support
below.
config MACH_HAS_SND_SOC_TEGRA_WM8903
bool
help

View File

@ -2,12 +2,14 @@
snd-soc-tegra-das-objs := tegra_das.o
snd-soc-tegra-pcm-objs := tegra_pcm.o
snd-soc-tegra-i2s-objs := tegra_i2s.o
snd-soc-tegra-spdif-objs := tegra_spdif.o
snd-soc-tegra-utils-objs += tegra_asoc_utils.o
obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-das.o
obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
obj-$(CONFIG_SND_SOC_TEGRA_I2S) += snd-soc-tegra-i2s.o
obj-$(CONFIG_SND_SOC_TEGRA_SPDIF) += snd-soc-tegra-spdif.o
# Tegra machine Support
snd-soc-tegra-wm8903-objs := tegra_wm8903.o

View File

@ -354,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;
@ -389,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);

View File

@ -322,9 +322,11 @@ static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream)
static u64 tegra_dma_mask = DMA_BIT_MASK(32);
static int tegra_pcm_new(struct snd_card *card,
struct snd_soc_dai *dai, struct snd_pcm *pcm)
static int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int ret = 0;
if (!card->dev->dma_mask)

View File

@ -0,0 +1,371 @@
/*
* tegra_spdif.c - Tegra SPDIF driver
*
* Author: Stephen Warren <swarren@nvidia.com>
* Copyright (C) 2011 - NVIDIA, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <mach/iomap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "tegra_spdif.h"
#define DRV_NAME "tegra-spdif"
static inline void tegra_spdif_write(struct tegra_spdif *spdif, u32 reg,
u32 val)
{
__raw_writel(val, spdif->regs + reg);
}
static inline u32 tegra_spdif_read(struct tegra_spdif *spdif, u32 reg)
{
return __raw_readl(spdif->regs + reg);
}
#ifdef CONFIG_DEBUG_FS
static int tegra_spdif_show(struct seq_file *s, void *unused)
{
#define REG(r) { r, #r }
static const struct {
int offset;
const char *name;
} regs[] = {
REG(TEGRA_SPDIF_CTRL),
REG(TEGRA_SPDIF_STATUS),
REG(TEGRA_SPDIF_STROBE_CTRL),
REG(TEGRA_SPDIF_DATA_FIFO_CSR),
REG(TEGRA_SPDIF_CH_STA_RX_A),
REG(TEGRA_SPDIF_CH_STA_RX_B),
REG(TEGRA_SPDIF_CH_STA_RX_C),
REG(TEGRA_SPDIF_CH_STA_RX_D),
REG(TEGRA_SPDIF_CH_STA_RX_E),
REG(TEGRA_SPDIF_CH_STA_RX_F),
REG(TEGRA_SPDIF_CH_STA_TX_A),
REG(TEGRA_SPDIF_CH_STA_TX_B),
REG(TEGRA_SPDIF_CH_STA_TX_C),
REG(TEGRA_SPDIF_CH_STA_TX_D),
REG(TEGRA_SPDIF_CH_STA_TX_E),
REG(TEGRA_SPDIF_CH_STA_TX_F),
};
#undef REG
struct tegra_spdif *spdif = s->private;
int i;
for (i = 0; i < ARRAY_SIZE(regs); i++) {
u32 val = tegra_spdif_read(spdif, regs[i].offset);
seq_printf(s, "%s = %08x\n", regs[i].name, val);
}
return 0;
}
static int tegra_spdif_debug_open(struct inode *inode, struct file *file)
{
return single_open(file, tegra_spdif_show, inode->i_private);
}
static const struct file_operations tegra_spdif_debug_fops = {
.open = tegra_spdif_debug_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void tegra_spdif_debug_add(struct tegra_spdif *spdif)
{
spdif->debug = debugfs_create_file(DRV_NAME, S_IRUGO,
snd_soc_debugfs_root, spdif,
&tegra_spdif_debug_fops);
}
static void tegra_spdif_debug_remove(struct tegra_spdif *spdif)
{
if (spdif->debug)
debugfs_remove(spdif->debug);
}
#else
static inline void tegra_spdif_debug_add(struct tegra_spdif *spdif)
{
}
static inline void tegra_spdif_debug_remove(struct tegra_spdif *spdif)
{
}
#endif
static int tegra_spdif_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct device *dev = substream->pcm->card->dev;
struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai);
int ret, srate, spdifclock;
spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_PACK;
spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_BIT_MODE_MASK;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_PACK;
spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_BIT_MODE_16BIT;
break;
default:
return -EINVAL;
}
srate = params_rate(params);
switch (params_rate(params)) {
case 32000:
spdifclock = 4096000;
break;
case 44100:
spdifclock = 5644800;
break;
case 48000:
spdifclock = 6144000;
break;
case 88200:
spdifclock = 11289600;
break;
case 96000:
spdifclock = 12288000;
break;
case 176400:
spdifclock = 22579200;
break;
case 192000:
spdifclock = 24576000;
break;
default:
return -EINVAL;
}
ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
if (ret) {
dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret);
return ret;
}
return 0;
}
static void tegra_spdif_start_playback(struct tegra_spdif *spdif)
{
spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_TX_EN;
tegra_spdif_write(spdif, TEGRA_SPDIF_CTRL, spdif->reg_ctrl);
}
static void tegra_spdif_stop_playback(struct tegra_spdif *spdif)
{
spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_TX_EN;
tegra_spdif_write(spdif, TEGRA_SPDIF_CTRL, spdif->reg_ctrl);
}
static int tegra_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
if (!spdif->clk_refs)
clk_enable(spdif->clk_spdif_out);
spdif->clk_refs++;
tegra_spdif_start_playback(spdif);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
tegra_spdif_stop_playback(spdif);
spdif->clk_refs--;
if (!spdif->clk_refs)
clk_disable(spdif->clk_spdif_out);
break;
default:
return -EINVAL;
}
return 0;
}
static int tegra_spdif_probe(struct snd_soc_dai *dai)
{
struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai);
dai->capture_dma_data = NULL;
dai->playback_dma_data = &spdif->playback_dma_data;
return 0;
}
static struct snd_soc_dai_ops tegra_spdif_dai_ops = {
.hw_params = tegra_spdif_hw_params,
.trigger = tegra_spdif_trigger,
};
struct snd_soc_dai_driver tegra_spdif_dai = {
.name = DRV_NAME,
.probe = tegra_spdif_probe,
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = &tegra_spdif_dai_ops,
};
static __devinit int tegra_spdif_platform_probe(struct platform_device *pdev)
{
struct tegra_spdif *spdif;
struct resource *mem, *memregion, *dmareq;
int ret;
spdif = kzalloc(sizeof(struct tegra_spdif), GFP_KERNEL);
if (!spdif) {
dev_err(&pdev->dev, "Can't allocate tegra_spdif\n");
ret = -ENOMEM;
goto exit;
}
dev_set_drvdata(&pdev->dev, spdif);
spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out");
if (IS_ERR(spdif->clk_spdif_out)) {
pr_err("Can't retrieve spdif clock\n");
ret = PTR_ERR(spdif->clk_spdif_out);
goto err_free;
}
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
dev_err(&pdev->dev, "No memory resource\n");
ret = -ENODEV;
goto err_clk_put;
}
dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!dmareq) {
dev_err(&pdev->dev, "No DMA resource\n");
ret = -ENODEV;
goto err_clk_put;
}
memregion = request_mem_region(mem->start, resource_size(mem),
DRV_NAME);
if (!memregion) {
dev_err(&pdev->dev, "Memory region already claimed\n");
ret = -EBUSY;
goto err_clk_put;
}
spdif->regs = ioremap(mem->start, resource_size(mem));
if (!spdif->regs) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
goto err_release;
}
spdif->playback_dma_data.addr = mem->start + TEGRA_SPDIF_DATA_OUT;
spdif->playback_dma_data.wrap = 4;
spdif->playback_dma_data.width = 32;
spdif->playback_dma_data.req_sel = dmareq->start;
ret = snd_soc_register_dai(&pdev->dev, &tegra_spdif_dai);
if (ret) {
dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
ret = -ENOMEM;
goto err_unmap;
}
tegra_spdif_debug_add(spdif);
return 0;
err_unmap:
iounmap(spdif->regs);
err_release:
release_mem_region(mem->start, resource_size(mem));
err_clk_put:
clk_put(spdif->clk_spdif_out);
err_free:
kfree(spdif);
exit:
return ret;
}
static int __devexit tegra_spdif_platform_remove(struct platform_device *pdev)
{
struct tegra_spdif *spdif = dev_get_drvdata(&pdev->dev);
struct resource *res;
snd_soc_unregister_dai(&pdev->dev);
tegra_spdif_debug_remove(spdif);
iounmap(spdif->regs);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, resource_size(res));
clk_put(spdif->clk_spdif_out);
kfree(spdif);
return 0;
}
static struct platform_driver tegra_spdif_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
.probe = tegra_spdif_platform_probe,
.remove = __devexit_p(tegra_spdif_platform_remove),
};
static int __init snd_tegra_spdif_init(void)
{
return platform_driver_register(&tegra_spdif_driver);
}
module_init(snd_tegra_spdif_init);
static void __exit snd_tegra_spdif_exit(void)
{
platform_driver_unregister(&tegra_spdif_driver);
}
module_exit(snd_tegra_spdif_exit);
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
MODULE_DESCRIPTION("Tegra SPDIF ASoC driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);

View File

@ -0,0 +1,473 @@
/*
* tegra_spdif.h - Definitions for Tegra SPDIF driver
*
* Author: Stephen Warren <swarren@nvidia.com>
* Copyright (C) 2011 - NVIDIA, Inc.
*
* Based on code copyright/by:
* Copyright (c) 2008-2009, NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __TEGRA_SPDIF_H__
#define __TEGRA_SPDIF_H__
#include "tegra_pcm.h"
/* Offsets from TEGRA_SPDIF_BASE */
#define TEGRA_SPDIF_CTRL 0x0
#define TEGRA_SPDIF_STATUS 0x4
#define TEGRA_SPDIF_STROBE_CTRL 0x8
#define TEGRA_SPDIF_DATA_FIFO_CSR 0x0C
#define TEGRA_SPDIF_DATA_OUT 0x40
#define TEGRA_SPDIF_DATA_IN 0x80
#define TEGRA_SPDIF_CH_STA_RX_A 0x100
#define TEGRA_SPDIF_CH_STA_RX_B 0x104
#define TEGRA_SPDIF_CH_STA_RX_C 0x108
#define TEGRA_SPDIF_CH_STA_RX_D 0x10C
#define TEGRA_SPDIF_CH_STA_RX_E 0x110
#define TEGRA_SPDIF_CH_STA_RX_F 0x114
#define TEGRA_SPDIF_CH_STA_TX_A 0x140
#define TEGRA_SPDIF_CH_STA_TX_B 0x144
#define TEGRA_SPDIF_CH_STA_TX_C 0x148
#define TEGRA_SPDIF_CH_STA_TX_D 0x14C
#define TEGRA_SPDIF_CH_STA_TX_E 0x150
#define TEGRA_SPDIF_CH_STA_TX_F 0x154
#define TEGRA_SPDIF_USR_STA_RX_A 0x180
#define TEGRA_SPDIF_USR_DAT_TX_A 0x1C0
/* Fields in TEGRA_SPDIF_CTRL */
/* Start capturing from 0=right, 1=left channel */
#define TEGRA_SPDIF_CTRL_CAP_LC (1 << 30)
/* SPDIF receiver(RX) enable */
#define TEGRA_SPDIF_CTRL_RX_EN (1 << 29)
/* SPDIF Transmitter(TX) enable */
#define TEGRA_SPDIF_CTRL_TX_EN (1 << 28)
/* Transmit Channel status */
#define TEGRA_SPDIF_CTRL_TC_EN (1 << 27)
/* Transmit user Data */
#define TEGRA_SPDIF_CTRL_TU_EN (1 << 26)
/* Interrupt on transmit error */
#define TEGRA_SPDIF_CTRL_IE_TXE (1 << 25)
/* Interrupt on receive error */
#define TEGRA_SPDIF_CTRL_IE_RXE (1 << 24)
/* Interrupt on invalid preamble */
#define TEGRA_SPDIF_CTRL_IE_P (1 << 23)
/* Interrupt on "B" preamble */
#define TEGRA_SPDIF_CTRL_IE_B (1 << 22)
/* Interrupt when block of channel status received */
#define TEGRA_SPDIF_CTRL_IE_C (1 << 21)
/* Interrupt when a valid information unit (IU) is received */
#define TEGRA_SPDIF_CTRL_IE_U (1 << 20)
/* Interrupt when RX user FIFO attention level is reached */
#define TEGRA_SPDIF_CTRL_QE_RU (1 << 19)
/* Interrupt when TX user FIFO attention level is reached */
#define TEGRA_SPDIF_CTRL_QE_TU (1 << 18)
/* Interrupt when RX data FIFO attention level is reached */
#define TEGRA_SPDIF_CTRL_QE_RX (1 << 17)
/* Interrupt when TX data FIFO attention level is reached */
#define TEGRA_SPDIF_CTRL_QE_TX (1 << 16)
/* Loopback test mode enable */
#define TEGRA_SPDIF_CTRL_LBK_EN (1 << 15)
/*
* Pack data mode:
* 0 = Single data (16 bit needs to be padded to match the
* interface data bit size).
* 1 = Packeted left/right channel data into a single word.
*/
#define TEGRA_SPDIF_CTRL_PACK (1 << 14)
/*
* 00 = 16bit data
* 01 = 20bit data
* 10 = 24bit data
* 11 = raw data
*/
#define TEGRA_SPDIF_BIT_MODE_16BIT 0
#define TEGRA_SPDIF_BIT_MODE_20BIT 1
#define TEGRA_SPDIF_BIT_MODE_24BIT 2
#define TEGRA_SPDIF_BIT_MODE_RAW 3
#define TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT 12
#define TEGRA_SPDIF_CTRL_BIT_MODE_MASK (3 << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
#define TEGRA_SPDIF_CTRL_BIT_MODE_16BIT (TEGRA_SPDIF_BIT_MODE_16BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
#define TEGRA_SPDIF_CTRL_BIT_MODE_20BIT (TEGRA_SPDIF_BIT_MODE_20BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
#define TEGRA_SPDIF_CTRL_BIT_MODE_24BIT (TEGRA_SPDIF_BIT_MODE_24BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
#define TEGRA_SPDIF_CTRL_BIT_MODE_RAW (TEGRA_SPDIF_BIT_MODE_RAW << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT)
/* Fields in TEGRA_SPDIF_STATUS */
/*
* Note: IS_P, IS_B, IS_C, and IS_U are sticky bits. Software must
* write a 1 to the corresponding bit location to clear the status.
*/
/*
* Receiver(RX) shifter is busy receiving data.
* This bit is asserted when the receiver first locked onto the
* preamble of the data stream after RX_EN is asserted. This bit is
* deasserted when either,
* (a) the end of a frame is reached after RX_EN is deeasserted, or
* (b) the SPDIF data stream becomes inactive.
*/
#define TEGRA_SPDIF_STATUS_RX_BSY (1 << 29)
/*
* Transmitter(TX) shifter is busy transmitting data.
* This bit is asserted when TX_EN is asserted.
* This bit is deasserted when the end of a frame is reached after
* TX_EN is deasserted.
*/
#define TEGRA_SPDIF_STATUS_TX_BSY (1 << 28)
/*
* TX is busy shifting out channel status.
* This bit is asserted when both TX_EN and TC_EN are asserted and
* data from CH_STA_TX_A register is loaded into the internal shifter.
* This bit is deasserted when either,
* (a) the end of a frame is reached after TX_EN is deasserted, or
* (b) CH_STA_TX_F register is loaded into the internal shifter.
*/
#define TEGRA_SPDIF_STATUS_TC_BSY (1 << 27)
/*
* TX User data FIFO busy.
* This bit is asserted when TX_EN and TXU_EN are asserted and
* there's data in the TX user FIFO. This bit is deassert when either,
* (a) the end of a frame is reached after TX_EN is deasserted, or
* (b) there's no data left in the TX user FIFO.
*/
#define TEGRA_SPDIF_STATUS_TU_BSY (1 << 26)
/* TX FIFO Underrun error status */
#define TEGRA_SPDIF_STATUS_TX_ERR (1 << 25)
/* RX FIFO Overrun error status */
#define TEGRA_SPDIF_STATUS_RX_ERR (1 << 24)
/* Preamble status: 0=Preamble OK, 1=bad/missing preamble */
#define TEGRA_SPDIF_STATUS_IS_P (1 << 23)
/* B-preamble detection status: 0=not detected, 1=B-preamble detected */
#define TEGRA_SPDIF_STATUS_IS_B (1 << 22)
/*
* RX channel block data receive status:
* 0=entire block not recieved yet.
* 1=received entire block of channel status,
*/
#define TEGRA_SPDIF_STATUS_IS_C (1 << 21)
/* RX User Data Valid flag: 1=valid IU detected, 0 = no IU detected. */
#define TEGRA_SPDIF_STATUS_IS_U (1 << 20)
/*
* RX User FIFO Status:
* 1=attention level reached, 0=attention level not reached.
*/
#define TEGRA_SPDIF_STATUS_QS_RU (1 << 19)
/*
* TX User FIFO Status:
* 1=attention level reached, 0=attention level not reached.
*/
#define TEGRA_SPDIF_STATUS_QS_TU (1 << 18)
/*
* RX Data FIFO Status:
* 1=attention level reached, 0=attention level not reached.
*/
#define TEGRA_SPDIF_STATUS_QS_RX (1 << 17)
/*
* TX Data FIFO Status:
* 1=attention level reached, 0=attention level not reached.
*/
#define TEGRA_SPDIF_STATUS_QS_TX (1 << 16)
/* Fields in TEGRA_SPDIF_STROBE_CTRL */
/*
* Indicates the approximate number of detected SPDIFIN clocks within a
* bi-phase period.
*/
#define TEGRA_SPDIF_STROBE_CTRL_PERIOD_SHIFT 16
#define TEGRA_SPDIF_STROBE_CTRL_PERIOD_MASK (0xff << TEGRA_SPDIF_STROBE_CTRL_PERIOD_SHIFT)
/* Data strobe mode: 0=Auto-locked 1=Manual locked */
#define TEGRA_SPDIF_STROBE_CTRL_STROBE (1 << 15)
/*
* Manual data strobe time within the bi-phase clock period (in terms of
* the number of over-sampling clocks).
*/
#define TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT 8
#define TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_MASK (0x1f << TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT)
/*
* Manual SPDIFIN bi-phase clock period (in terms of the number of
* over-sampling clocks).
*/
#define TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT 0
#define TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_MASK (0x3f << TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT)
/* Fields in SPDIF_DATA_FIFO_CSR */
/* Clear Receiver User FIFO (RX USR.FIFO) */
#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_CLR (1 << 31)
#define TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT 0
#define TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS 1
#define TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS 2
#define TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS 3
/* RU FIFO attention level */
#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT 29
#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_MASK \
(0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU1_WORD_FULL \
(TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU2_WORD_FULL \
(TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU3_WORD_FULL \
(TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU4_WORD_FULL \
(TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT)
/* Number of RX USR.FIFO levels with valid data. */
#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT 24
#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_MASK (0x1f << TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT)
/* Clear Transmitter User FIFO (TX USR.FIFO) */
#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_CLR (1 << 23)
/* TU FIFO attention level */
#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT 21
#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_MASK \
(0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU1_WORD_FULL \
(TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU2_WORD_FULL \
(TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU3_WORD_FULL \
(TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU4_WORD_FULL \
(TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT)
/* Number of TX USR.FIFO levels that could be filled. */
#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT 16
#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT)
/* Clear Receiver Data FIFO (RX DATA.FIFO) */
#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_CLR (1 << 15)
#define TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT 0
#define TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS 1
#define TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS 2
#define TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS 3
/* RU FIFO attention level */
#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT 13
#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_MASK \
(0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU1_WORD_FULL \
(TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU4_WORD_FULL \
(TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU8_WORD_FULL \
(TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU12_WORD_FULL \
(TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT)
/* Number of RX DATA.FIFO levels with valid data. */
#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT 8
#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_MASK (0x1f << TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT)
/* Clear Transmitter Data FIFO (TX DATA.FIFO) */
#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_CLR (1 << 7)
/* TU FIFO attention level */
#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT 5
#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK \
(0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU1_WORD_FULL \
(TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL \
(TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU8_WORD_FULL \
(TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU12_WORD_FULL \
(TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT)
/* Number of TX DATA.FIFO levels that could be filled. */
#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT 0
#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT)
/* Fields in TEGRA_SPDIF_DATA_OUT */
/*
* This register has 5 different formats:
* 16-bit (BIT_MODE=00, PACK=0)
* 20-bit (BIT_MODE=01, PACK=0)
* 24-bit (BIT_MODE=10, PACK=0)
* raw (BIT_MODE=11, PACK=0)
* 16-bit packed (BIT_MODE=00, PACK=1)
*/
#define TEGRA_SPDIF_DATA_OUT_DATA_16_SHIFT 0
#define TEGRA_SPDIF_DATA_OUT_DATA_16_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_SHIFT)
#define TEGRA_SPDIF_DATA_OUT_DATA_20_SHIFT 0
#define TEGRA_SPDIF_DATA_OUT_DATA_20_MASK (0xfffff << TEGRA_SPDIF_DATA_OUT_DATA_20_SHIFT)
#define TEGRA_SPDIF_DATA_OUT_DATA_24_SHIFT 0
#define TEGRA_SPDIF_DATA_OUT_DATA_24_MASK (0xffffff << TEGRA_SPDIF_DATA_OUT_DATA_24_SHIFT)
#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_P (1 << 31)
#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_C (1 << 30)
#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_U (1 << 29)
#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_V (1 << 28)
#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT 8
#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_MASK (0xfffff << TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT)
#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT 4
#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_MASK (0xf << TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT)
#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT 0
#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT)
#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT 16
#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT)
#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT 0
#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT)
/* Fields in TEGRA_SPDIF_DATA_IN */
/*
* This register has 5 different formats:
* 16-bit (BIT_MODE=00, PACK=0)
* 20-bit (BIT_MODE=01, PACK=0)
* 24-bit (BIT_MODE=10, PACK=0)
* raw (BIT_MODE=11, PACK=0)
* 16-bit packed (BIT_MODE=00, PACK=1)
*
* Bits 31:24 are common to all modes except 16-bit packed
*/
#define TEGRA_SPDIF_DATA_IN_DATA_P (1 << 31)
#define TEGRA_SPDIF_DATA_IN_DATA_C (1 << 30)
#define TEGRA_SPDIF_DATA_IN_DATA_U (1 << 29)
#define TEGRA_SPDIF_DATA_IN_DATA_V (1 << 28)
#define TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT 24
#define TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT)
#define TEGRA_SPDIF_DATA_IN_DATA_16_SHIFT 0
#define TEGRA_SPDIF_DATA_IN_DATA_16_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_SHIFT)
#define TEGRA_SPDIF_DATA_IN_DATA_20_SHIFT 0
#define TEGRA_SPDIF_DATA_IN_DATA_20_MASK (0xfffff << TEGRA_SPDIF_DATA_IN_DATA_20_SHIFT)
#define TEGRA_SPDIF_DATA_IN_DATA_24_SHIFT 0
#define TEGRA_SPDIF_DATA_IN_DATA_24_MASK (0xffffff << TEGRA_SPDIF_DATA_IN_DATA_24_SHIFT)
#define TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT 8
#define TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_MASK (0xfffff << TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT)
#define TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT 4
#define TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT)
#define TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT 0
#define TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT)
#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT 16
#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT)
#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT 0
#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT)
/* Fields in TEGRA_SPDIF_CH_STA_RX_A */
/* Fields in TEGRA_SPDIF_CH_STA_RX_B */
/* Fields in TEGRA_SPDIF_CH_STA_RX_C */
/* Fields in TEGRA_SPDIF_CH_STA_RX_D */
/* Fields in TEGRA_SPDIF_CH_STA_RX_E */
/* Fields in TEGRA_SPDIF_CH_STA_RX_F */
/*
* The 6-word receive channel data page buffer holds a block (192 frames) of
* channel status information. The order of receive is from LSB to MSB
* bit, and from CH_STA_RX_A to CH_STA_RX_F then back to CH_STA_RX_A.
*/
/* Fields in TEGRA_SPDIF_CH_STA_TX_A */
/* Fields in TEGRA_SPDIF_CH_STA_TX_B */
/* Fields in TEGRA_SPDIF_CH_STA_TX_C */
/* Fields in TEGRA_SPDIF_CH_STA_TX_D */
/* Fields in TEGRA_SPDIF_CH_STA_TX_E */
/* Fields in TEGRA_SPDIF_CH_STA_TX_F */
/*
* The 6-word transmit channel data page buffer holds a block (192 frames) of
* channel status information. The order of transmission is from LSB to MSB
* bit, and from CH_STA_TX_A to CH_STA_TX_F then back to CH_STA_TX_A.
*/
/* Fields in TEGRA_SPDIF_USR_STA_RX_A */
/*
* This 4-word deep FIFO receives user FIFO field information. The order of
* receive is from LSB to MSB bit.
*/
/* Fields in TEGRA_SPDIF_USR_DAT_TX_A */
/*
* This 4-word deep FIFO transmits user FIFO field information. The order of
* transmission is from LSB to MSB bit.
*/
struct tegra_spdif {
struct clk *clk_spdif_out;
int clk_refs;
struct tegra_pcm_dma_params capture_dma_data;
struct tegra_pcm_dma_params playback_dma_data;
void __iomem *regs;
struct dentry *debug;
u32 reg_ctrl;
};
#endif

View File

@ -267,7 +267,7 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
}
machine->gpio_requested |= GPIO_HP_MUTE;
gpio_direction_output(pdata->gpio_hp_mute, 0);
gpio_direction_output(pdata->gpio_hp_mute, 1);
}
if (gpio_is_valid(pdata->gpio_int_mic_en)) {

View File

@ -288,9 +288,10 @@ static void txx9aclc_pcm_free_dma_buffers(struct snd_pcm *pcm)
snd_pcm_lib_preallocate_free_for_all(pcm);
}
static int txx9aclc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
struct platform_device *pdev = to_platform_device(dai->platform->dev);
struct txx9aclc_soc_device *dev;
struct resource *r;