Commit e320bc42 authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'for-3.1' of...

Merge branch 'for-3.1' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6 into topic/asoc
parents f187700c b5d5f59b
...@@ -447,6 +447,7 @@ struct snd_soc_dapm_widget { ...@@ -447,6 +447,7 @@ struct snd_soc_dapm_widget {
char *name; /* widget name */ char *name; /* widget name */
char *sname; /* stream name */ char *sname; /* stream name */
struct snd_soc_codec *codec; struct snd_soc_codec *codec;
struct snd_soc_platform *platform;
struct list_head list; struct list_head list;
struct snd_soc_dapm_context *dapm; struct snd_soc_dapm_context *dapm;
...@@ -510,6 +511,7 @@ struct snd_soc_dapm_context { ...@@ -510,6 +511,7 @@ struct snd_soc_dapm_context {
struct device *dev; /* from parent - for debug */ struct device *dev; /* from parent - for debug */
struct snd_soc_codec *codec; /* parent codec */ struct snd_soc_codec *codec; /* parent codec */
struct snd_soc_platform *platform; /* parent platform */
struct snd_soc_card *card; /* parent card */ struct snd_soc_card *card; /* parent card */
/* used during DAPM updates */ /* used during DAPM updates */
......
...@@ -368,6 +368,8 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, ...@@ -368,6 +368,8 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
const char *prefix); const char *prefix);
int snd_soc_add_controls(struct snd_soc_codec *codec, int snd_soc_add_controls(struct snd_soc_codec *codec,
const struct snd_kcontrol_new *controls, int num_controls); 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, int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo); struct snd_ctl_elem_info *uinfo);
int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,
...@@ -649,6 +651,14 @@ struct snd_soc_platform_driver { ...@@ -649,6 +651,14 @@ struct snd_soc_platform_driver {
int (*pcm_new)(struct snd_soc_pcm_runtime *); int (*pcm_new)(struct snd_soc_pcm_runtime *);
void (*pcm_free)(struct snd_pcm *); 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. * For platform caused delay reporting.
* Optional. * Optional.
...@@ -680,6 +690,8 @@ struct snd_soc_platform { ...@@ -680,6 +690,8 @@ struct snd_soc_platform {
struct snd_soc_card *card; struct snd_soc_card *card;
struct list_head list; struct list_head list;
struct list_head card_list; struct list_head card_list;
struct snd_soc_dapm_context dapm;
}; };
struct snd_soc_dai_link { struct snd_soc_dai_link {
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
struct snd_soc_jack; struct snd_soc_jack;
struct snd_soc_codec; struct snd_soc_codec;
struct snd_soc_platform;
struct snd_soc_card; struct snd_soc_card;
struct snd_soc_dapm_widget; struct snd_soc_dapm_widget;
...@@ -59,6 +60,50 @@ DEFINE_EVENT(snd_soc_reg, snd_soc_reg_read, ...@@ -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, DECLARE_EVENT_CLASS(snd_soc_card,
TP_PROTO(struct snd_soc_card *card, int val), TP_PROTO(struct snd_soc_card *card, int val),
......
...@@ -357,7 +357,7 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ...@@ -357,7 +357,7 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
default: default:
return -EINVAL; return -EINVAL;
} }
snd_soc_update_bits(codec, PW_MGMT2, MS, data); snd_soc_update_bits(codec, PW_MGMT2, MS | MCKO | PMPLL, data);
snd_soc_update_bits(codec, MD_CTL1, BCKO_MASK, bcko); snd_soc_update_bits(codec, MD_CTL1, BCKO_MASK, bcko);
/* format type */ /* format type */
......
...@@ -175,6 +175,7 @@ static const struct snd_kcontrol_new wm8731_input_mux_controls = ...@@ -175,6 +175,7 @@ static const struct snd_kcontrol_new wm8731_input_mux_controls =
SOC_DAPM_ENUM("Input Select", wm8731_insel_enum); SOC_DAPM_ENUM("Input Select", wm8731_insel_enum);
static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("ACTIVE",WM8731_ACTIVE, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("OSC", WM8731_PWR, 5, 1, NULL, 0), SND_SOC_DAPM_SUPPLY("OSC", WM8731_PWR, 5, 1, NULL, 0),
SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1,
&wm8731_output_mixer_controls[0], &wm8731_output_mixer_controls[0],
...@@ -204,6 +205,8 @@ static int wm8731_check_osc(struct snd_soc_dapm_widget *source, ...@@ -204,6 +205,8 @@ static int wm8731_check_osc(struct snd_soc_dapm_widget *source,
static const struct snd_soc_dapm_route wm8731_intercon[] = { static const struct snd_soc_dapm_route wm8731_intercon[] = {
{"DAC", NULL, "OSC", wm8731_check_osc}, {"DAC", NULL, "OSC", wm8731_check_osc},
{"ADC", NULL, "OSC", wm8731_check_osc}, {"ADC", NULL, "OSC", wm8731_check_osc},
{"DAC", NULL, "ACTIVE"},
{"ADC", NULL, "ACTIVE"},
/* output mixer */ /* output mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"}, {"Output Mixer", "Line Bypass Switch", "Line Input"},
...@@ -315,29 +318,6 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream, ...@@ -315,29 +318,6 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
return 0; return 0;
} }
static int wm8731_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
/* set active */
snd_soc_write(codec, WM8731_ACTIVE, 0x0001);
return 0;
}
static void wm8731_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
/* deactivate */
if (!codec->active) {
udelay(50);
snd_soc_write(codec, WM8731_ACTIVE, 0x0);
}
}
static int wm8731_mute(struct snd_soc_dai *dai, int mute) static int wm8731_mute(struct snd_soc_dai *dai, int mute)
{ {
struct snd_soc_codec *codec = dai->codec; struct snd_soc_codec *codec = dai->codec;
...@@ -480,7 +460,6 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, ...@@ -480,7 +460,6 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
snd_soc_write(codec, WM8731_PWR, reg | 0x0040); snd_soc_write(codec, WM8731_PWR, reg | 0x0040);
break; break;
case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_OFF:
snd_soc_write(codec, WM8731_ACTIVE, 0x0);
snd_soc_write(codec, WM8731_PWR, 0xffff); snd_soc_write(codec, WM8731_PWR, 0xffff);
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
wm8731->supplies); wm8731->supplies);
...@@ -496,9 +475,7 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, ...@@ -496,9 +475,7 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
SNDRV_PCM_FMTBIT_S24_LE) SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_ops wm8731_dai_ops = { static struct snd_soc_dai_ops wm8731_dai_ops = {
.prepare = wm8731_pcm_prepare,
.hw_params = wm8731_hw_params, .hw_params = wm8731_hw_params,
.shutdown = wm8731_shutdown,
.digital_mute = wm8731_mute, .digital_mute = wm8731_mute,
.set_sysclk = wm8731_set_dai_sysclk, .set_sysclk = wm8731_set_dai_sysclk,
.set_fmt = wm8731_set_dai_fmt, .set_fmt = wm8731_set_dai_fmt,
......
...@@ -88,7 +88,6 @@ static u64 pxa2xx_pcm_dmamask = DMA_BIT_MASK(32); ...@@ -88,7 +88,6 @@ static u64 pxa2xx_pcm_dmamask = DMA_BIT_MASK(32);
static int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd) static int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd)
{ {
struct snd_card *card = rtd->card->snd_card; struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm; struct snd_pcm *pcm = rtd->pcm;
int ret = 0; int ret = 0;
......
...@@ -993,10 +993,15 @@ static int soc_probe_platform(struct snd_soc_card *card, ...@@ -993,10 +993,15 @@ static int soc_probe_platform(struct snd_soc_card *card,
const struct snd_soc_platform_driver *driver = platform->driver; const struct snd_soc_platform_driver *driver = platform->driver;
platform->card = card; platform->card = card;
platform->dapm.card = card;
if (!try_module_get(platform->dev->driver->owner)) if (!try_module_get(platform->dev->driver->owner))
return -ENODEV; return -ENODEV;
if (driver->dapm_widgets)
snd_soc_dapm_new_controls(&platform->dapm,
driver->dapm_widgets, driver->num_dapm_widgets);
if (driver->probe) { if (driver->probe) {
ret = driver->probe(platform); ret = driver->probe(platform);
if (ret < 0) { if (ret < 0) {
...@@ -1007,9 +1012,17 @@ static int soc_probe_platform(struct snd_soc_card *card, ...@@ -1007,9 +1012,17 @@ static int soc_probe_platform(struct snd_soc_card *card,
} }
} }
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 */ /* mark platform as probed and add to card platform list */
platform->probed = 1; platform->probed = 1;
list_add(&platform->card_list, &card->platform_dev_list); list_add(&platform->card_list, &card->platform_dev_list);
list_add(&platform->dapm.list, &card->dapm_list);
return 0; return 0;
...@@ -1652,6 +1665,7 @@ int snd_soc_platform_read(struct snd_soc_platform *platform, ...@@ -1652,6 +1665,7 @@ int snd_soc_platform_read(struct snd_soc_platform *platform,
ret = platform->driver->read(platform, reg); ret = platform->driver->read(platform, reg);
dev_dbg(platform->dev, "read %x => %x\n", reg, ret); dev_dbg(platform->dev, "read %x => %x\n", reg, ret);
trace_snd_soc_preg_read(platform, reg, ret);
return ret; return ret;
} }
...@@ -1666,6 +1680,7 @@ int snd_soc_platform_write(struct snd_soc_platform *platform, ...@@ -1666,6 +1680,7 @@ int snd_soc_platform_write(struct snd_soc_platform *platform,
} }
dev_dbg(platform->dev, "write %x = %x\n", reg, val); 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); return platform->driver->write(platform, reg, val);
} }
EXPORT_SYMBOL_GPL(snd_soc_platform_write); EXPORT_SYMBOL_GPL(snd_soc_platform_write);
...@@ -1948,6 +1963,36 @@ int snd_soc_add_controls(struct snd_soc_codec *codec, ...@@ -1948,6 +1963,36 @@ int snd_soc_add_controls(struct snd_soc_codec *codec,
} }
EXPORT_SYMBOL_GPL(snd_soc_add_controls); 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 * snd_soc_info_enum_double - enumerated double mixer info callback
* @kcontrol: mixer control * @kcontrol: mixer control
...@@ -3092,6 +3137,8 @@ int snd_soc_register_platform(struct device *dev, ...@@ -3092,6 +3137,8 @@ int snd_soc_register_platform(struct device *dev,
platform->dev = dev; platform->dev = dev;
platform->driver = platform_drv; platform->driver = platform_drv;
platform->dapm.dev = dev;
platform->dapm.platform = platform;
mutex_lock(&client_mutex); mutex_lock(&client_mutex);
list_add(&platform->list, &platform_list); list_add(&platform->list, &platform_list);
......
...@@ -128,14 +128,22 @@ static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg) ...@@ -128,14 +128,22 @@ static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg)
{ {
if (w->codec) if (w->codec)
return snd_soc_read(w->codec, reg); return snd_soc_read(w->codec, reg);
return 0; 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) static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg, int val)
{ {
if (w->codec) if (w->codec)
return snd_soc_write(w->codec, reg, val); return snd_soc_write(w->codec, reg, val);
return 0; 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, static int soc_widget_update_bits(struct snd_soc_dapm_widget *w,
...@@ -2495,6 +2503,7 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, ...@@ -2495,6 +2503,7 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
dapm->n_widgets++; dapm->n_widgets++;
w->dapm = dapm; w->dapm = dapm;
w->codec = dapm->codec; w->codec = dapm->codec;
w->platform = dapm->platform;
INIT_LIST_HEAD(&w->sources); INIT_LIST_HEAD(&w->sources);
INIT_LIST_HEAD(&w->sinks); INIT_LIST_HEAD(&w->sinks);
INIT_LIST_HEAD(&w->list); INIT_LIST_HEAD(&w->list);
......
...@@ -12,6 +12,15 @@ config SND_SOC_TEGRA_I2S ...@@ -12,6 +12,15 @@ config SND_SOC_TEGRA_I2S
Tegra I2S interface. You will also need to select the individual Tegra I2S interface. You will also need to select the individual
machine drivers to support below. 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 config MACH_HAS_SND_SOC_TEGRA_WM8903
bool bool
help help
......
...@@ -2,12 +2,14 @@ ...@@ -2,12 +2,14 @@
snd-soc-tegra-das-objs := tegra_das.o snd-soc-tegra-das-objs := tegra_das.o
snd-soc-tegra-pcm-objs := tegra_pcm.o snd-soc-tegra-pcm-objs := tegra_pcm.o
snd-soc-tegra-i2s-objs := tegra_i2s.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 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-utils.o
obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-das.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) += snd-soc-tegra-pcm.o
obj-$(CONFIG_SND_SOC_TEGRA_I2S) += snd-soc-tegra-i2s.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 # Tegra machine Support
snd-soc-tegra-wm8903-objs := tegra_wm8903.o snd-soc-tegra-wm8903-objs := tegra_wm8903.o
......
/*
* 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);
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment