aboutsummaryrefslogtreecommitdiff
path: root/sound/soc/sh
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sh')
-rw-r--r--sound/soc/sh/Kconfig8
-rw-r--r--sound/soc/sh/Makefile2
-rw-r--r--sound/soc/sh/fsi-da7210.c83
-rw-r--r--sound/soc/sh/fsi.c164
4 files changed, 195 insertions, 62 deletions
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index 9e697658655..8072a6d1c4d 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -47,4 +47,12 @@ config SND_FSI_AK4642
This option enables generic sound support for the
FSI - AK4642 unit
+config SND_FSI_DA7210
+ bool "FSI-DA7210 sound support"
+ depends on SND_SOC_SH4_FSI
+ select SND_SOC_DA7210
+ help
+ This option enables generic sound support for the
+ FSI - DA7210 unit
+
endmenu
diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile
index a6997872f24..1d0ec0af74b 100644
--- a/sound/soc/sh/Makefile
+++ b/sound/soc/sh/Makefile
@@ -13,6 +13,8 @@ obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o
## boards
snd-soc-sh7760-ac97-objs := sh7760-ac97.o
snd-soc-fsi-ak4642-objs := fsi-ak4642.o
+snd-soc-fsi-da7210-objs := fsi-da7210.o
obj-$(CONFIG_SND_SH7760_AC97) += snd-soc-sh7760-ac97.o
obj-$(CONFIG_SND_FSI_AK4642) += snd-soc-fsi-ak4642.o
+obj-$(CONFIG_SND_FSI_DA7210) += snd-soc-fsi-da7210.o
diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c
new file mode 100644
index 00000000000..33b4d177f46
--- /dev/null
+++ b/sound/soc/sh/fsi-da7210.c
@@ -0,0 +1,83 @@
+/*
+ * fsi-da7210.c
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.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/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/i2c.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/sh_fsi.h>
+#include "../codecs/da7210.h"
+
+static int fsi_da7210_init(struct snd_soc_codec *codec)
+{
+ return snd_soc_dai_set_fmt(&da7210_dai,
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+}
+
+static struct snd_soc_dai_link fsi_da7210_dai = {
+ .name = "DA7210",
+ .stream_name = "DA7210",
+ .cpu_dai = &fsi_soc_dai[1], /* FSI B */
+ .codec_dai = &da7210_dai,
+ .init = fsi_da7210_init,
+};
+
+static struct snd_soc_card fsi_soc_card = {
+ .name = "FSI",
+ .platform = &fsi_soc_platform,
+ .dai_link = &fsi_da7210_dai,
+ .num_links = 1,
+};
+
+static struct snd_soc_device fsi_da7210_snd_devdata = {
+ .card = &fsi_soc_card,
+ .codec_dev = &soc_codec_dev_da7210,
+};
+
+static struct platform_device *fsi_da7210_snd_device;
+
+static int __init fsi_da7210_sound_init(void)
+{
+ int ret;
+
+ fsi_da7210_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!fsi_da7210_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(fsi_da7210_snd_device, &fsi_da7210_snd_devdata);
+ fsi_da7210_snd_devdata.dev = &fsi_da7210_snd_device->dev;
+ ret = platform_device_add(fsi_da7210_snd_device);
+ if (ret)
+ platform_device_put(fsi_da7210_snd_device);
+
+ return ret;
+}
+
+static void __exit fsi_da7210_sound_exit(void)
+{
+ platform_device_unregister(fsi_da7210_snd_device);
+}
+
+module_init(fsi_da7210_sound_init);
+module_exit(fsi_da7210_sound_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("ALSA SoC FSI DA2710");
+MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 42813b80838..5f9f2693f4e 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -67,6 +67,7 @@
/* DOFF_ST */
#define ERR_OVER 0x00000010
#define ERR_UNDER 0x00000001
+#define ST_ERR (ERR_OVER | ERR_UNDER)
/* CLK_RST */
#define B_CLK 0x00000010
@@ -92,6 +93,7 @@
struct fsi_priv {
void __iomem *base;
struct snd_pcm_substream *substream;
+ struct fsi_master *master;
int fifo_max;
int chan;
@@ -110,8 +112,6 @@ struct fsi_master {
struct sh_fsi_platform_info *info;
};
-static struct fsi_master *master;
-
/************************************************************************
@@ -166,7 +166,7 @@ static int fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data)
return __fsi_reg_mask_set((u32)(fsi->base + reg), mask, data);
}
-static int fsi_master_write(u32 reg, u32 data)
+static int fsi_master_write(struct fsi_master *master, u32 reg, u32 data)
{
if ((reg < MREG_START) ||
(reg > MREG_END))
@@ -175,7 +175,7 @@ static int fsi_master_write(u32 reg, u32 data)
return __fsi_reg_write((u32)(master->base + reg), data);
}
-static u32 fsi_master_read(u32 reg)
+static u32 fsi_master_read(struct fsi_master *master, u32 reg)
{
if ((reg < MREG_START) ||
(reg > MREG_END))
@@ -184,7 +184,8 @@ static u32 fsi_master_read(u32 reg)
return __fsi_reg_read((u32)(master->base + reg));
}
-static int fsi_master_mask_set(u32 reg, u32 mask, u32 data)
+static int fsi_master_mask_set(struct fsi_master *master,
+ u32 reg, u32 mask, u32 data)
{
if ((reg < MREG_START) ||
(reg > MREG_END))
@@ -200,43 +201,35 @@ static int fsi_master_mask_set(u32 reg, u32 mask, u32 data)
************************************************************************/
-static struct fsi_priv *fsi_get(struct snd_pcm_substream *substream)
+static struct fsi_master *fsi_get_master(struct fsi_priv *fsi)
{
- struct snd_soc_pcm_runtime *rtd;
- struct fsi_priv *fsi = NULL;
+ return fsi->master;
+}
- if (!substream || !master)
- return NULL;
+static int fsi_is_port_a(struct fsi_priv *fsi)
+{
+ return fsi->master->base == fsi->base;
+}
- rtd = substream->private_data;
- switch (rtd->dai->cpu_dai->id) {
- case 0:
- fsi = &master->fsia;
- break;
- case 1:
- fsi = &master->fsib;
- break;
- }
+static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *machine = rtd->dai;
- return fsi;
+ return machine->cpu_dai;
}
-static int fsi_is_port_a(struct fsi_priv *fsi)
+static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream)
{
- /* return
- * 1 : port a
- * 0 : port b
- */
-
- if (fsi == &master->fsia)
- return 1;
+ struct snd_soc_dai *dai = fsi_get_dai(substream);
- return 0;
+ return dai->private_data;
}
static u32 fsi_get_info_flags(struct fsi_priv *fsi)
{
int is_porta = fsi_is_port_a(fsi);
+ struct fsi_master *master = fsi_get_master(fsi);
return is_porta ? master->info->porta_flags :
master->info->portb_flags;
@@ -314,27 +307,30 @@ static int fsi_get_fifo_residue(struct fsi_priv *fsi, int is_play)
static void fsi_irq_enable(struct fsi_priv *fsi, int is_play)
{
u32 data = fsi_port_ab_io_bit(fsi, is_play);
+ struct fsi_master *master = fsi_get_master(fsi);
- fsi_master_mask_set(IMSK, data, data);
- fsi_master_mask_set(IEMSK, data, data);
+ fsi_master_mask_set(master, IMSK, data, data);
+ fsi_master_mask_set(master, IEMSK, data, data);
}
static void fsi_irq_disable(struct fsi_priv *fsi, int is_play)
{
u32 data = fsi_port_ab_io_bit(fsi, is_play);
+ struct fsi_master *master = fsi_get_master(fsi);
- fsi_master_mask_set(IMSK, data, 0);
- fsi_master_mask_set(IEMSK, data, 0);
+ fsi_master_mask_set(master, IMSK, data, 0);
+ fsi_master_mask_set(master, IEMSK, data, 0);
}
static void fsi_clk_ctrl(struct fsi_priv *fsi, int enable)
{
u32 val = fsi_is_port_a(fsi) ? (1 << 0) : (1 << 4);
+ struct fsi_master *master = fsi_get_master(fsi);
if (enable)
- fsi_master_mask_set(CLK_RST, val, val);
+ fsi_master_mask_set(master, CLK_RST, val, val);
else
- fsi_master_mask_set(CLK_RST, val, 0);
+ fsi_master_mask_set(master, CLK_RST, val, 0);
}
static void fsi_irq_init(struct fsi_priv *fsi, int is_play)
@@ -355,23 +351,23 @@ static void fsi_irq_init(struct fsi_priv *fsi, int is_play)
fsi_reg_mask_set(fsi, ctrl, FIFO_CLR, FIFO_CLR);
/* clear interrupt factor */
- fsi_master_mask_set(INT_ST, data, 0);
+ fsi_master_mask_set(fsi_get_master(fsi), INT_ST, data, 0);
}
-static void fsi_soft_all_reset(void)
+static void fsi_soft_all_reset(struct fsi_master *master)
{
- u32 status = fsi_master_read(SOFT_RST);
+ u32 status = fsi_master_read(master, SOFT_RST);
/* port AB reset */
status &= 0x000000ff;
- fsi_master_write(SOFT_RST, status);
+ fsi_master_write(master, SOFT_RST, status);
mdelay(10);
/* soft reset */
status &= 0x000000f0;
- fsi_master_write(SOFT_RST, status);
+ fsi_master_write(master, SOFT_RST, status);
status |= 0x00000001;
- fsi_master_write(SOFT_RST, status);
+ fsi_master_write(master, SOFT_RST, status);
mdelay(10);
}
@@ -380,18 +376,21 @@ static int fsi_data_push(struct fsi_priv *fsi)
{
struct snd_pcm_runtime *runtime;
struct snd_pcm_substream *substream = NULL;
+ u32 status;
int send;
int fifo_free;
int width;
u8 *start;
- int i;
+ int i, ret, over_period;
if (!fsi ||
!fsi->substream ||
!fsi->substream->runtime)
return -EINVAL;
- runtime = fsi->substream->runtime;
+ over_period = 0;
+ substream = fsi->substream;
+ runtime = substream->runtime;
/* FSI FIFO has limit.
* So, this driver can not send periods data at a time
@@ -399,7 +398,7 @@ static int fsi_data_push(struct fsi_priv *fsi)
if (fsi->byte_offset >=
fsi->period_len * (fsi->periods + 1)) {
- substream = fsi->substream;
+ over_period = 1;
fsi->periods = (fsi->periods + 1) % runtime->periods;
if (0 == fsi->periods)
@@ -438,30 +437,42 @@ static int fsi_data_push(struct fsi_priv *fsi)
fsi->byte_offset += send * width;
+ ret = 0;
+ status = fsi_reg_read(fsi, DOFF_ST);
+ if (status & ERR_OVER) {
+ struct snd_soc_dai *dai = fsi_get_dai(substream);
+ dev_err(dai->dev, "over run error\n");
+ fsi_reg_write(fsi, DOFF_ST, status & ~ST_ERR);
+ ret = -EIO;
+ }
+
fsi_irq_enable(fsi, 1);
- if (substream)
+ if (over_period)
snd_pcm_period_elapsed(substream);
- return 0;
+ return ret;
}
static int fsi_data_pop(struct fsi_priv *fsi)
{
struct snd_pcm_runtime *runtime;
struct snd_pcm_substream *substream = NULL;
+ u32 status;
int free;
int fifo_fill;
int width;
u8 *start;
- int i;
+ int i, ret, over_period;
if (!fsi ||
!fsi->substream ||
!fsi->substream->runtime)
return -EINVAL;
- runtime = fsi->substream->runtime;
+ over_period = 0;
+ substream = fsi->substream;
+ runtime = substream->runtime;
/* FSI FIFO has limit.
* So, this driver can not send periods data at a time
@@ -469,7 +480,7 @@ static int fsi_data_pop(struct fsi_priv *fsi)
if (fsi->byte_offset >=
fsi->period_len * (fsi->periods + 1)) {
- substream = fsi->substream;
+ over_period = 1;
fsi->periods = (fsi->periods + 1) % runtime->periods;
if (0 == fsi->periods)
@@ -507,22 +518,32 @@ static int fsi_data_pop(struct fsi_priv *fsi)
fsi->byte_offset += fifo_fill * width;
+ ret = 0;
+ status = fsi_reg_read(fsi, DIFF_ST);
+ if (status & ERR_UNDER) {
+ struct snd_soc_dai *dai = fsi_get_dai(substream);
+ dev_err(dai->dev, "under run error\n");
+ fsi_reg_write(fsi, DIFF_ST, status & ~ST_ERR);
+ ret = -EIO;
+ }
+
fsi_irq_enable(fsi, 0);
- if (substream)
+ if (over_period)
snd_pcm_period_elapsed(substream);
- return 0;
+ return ret;
}
static irqreturn_t fsi_interrupt(int irq, void *data)
{
- u32 status = fsi_master_read(SOFT_RST) & ~0x00000010;
- u32 int_st = fsi_master_read(INT_ST);
+ struct fsi_master *master = data;
+ u32 status = fsi_master_read(master, SOFT_RST) & ~0x00000010;
+ u32 int_st = fsi_master_read(master, INT_ST);
/* clear irq status */
- fsi_master_write(SOFT_RST, status);
- fsi_master_write(SOFT_RST, status | 0x00000010);
+ fsi_master_write(master, SOFT_RST, status);
+ fsi_master_write(master, SOFT_RST, status | 0x00000010);
if (int_st & INT_A_OUT)
fsi_data_push(&master->fsia);
@@ -533,7 +554,7 @@ static irqreturn_t fsi_interrupt(int irq, void *data)
if (int_st & INT_B_IN)
fsi_data_pop(&master->fsib);
- fsi_master_write(INT_ST, 0x0000000);
+ fsi_master_write(master, INT_ST, 0x0000000);
return IRQ_HANDLED;
}
@@ -548,7 +569,7 @@ static irqreturn_t fsi_interrupt(int irq, void *data)
static int fsi_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct fsi_priv *fsi = fsi_get(substream);
+ struct fsi_priv *fsi = fsi_get_priv(substream);
const char *msg;
u32 flags = fsi_get_info_flags(fsi);
u32 fmt;
@@ -667,7 +688,7 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct fsi_priv *fsi = fsi_get(substream);
+ struct fsi_priv *fsi = fsi_get_priv(substream);
int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
fsi_irq_disable(fsi, is_play);
@@ -679,7 +700,7 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct fsi_priv *fsi = fsi_get(substream);
+ struct fsi_priv *fsi = fsi_get_priv(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
int ret = 0;
@@ -760,7 +781,7 @@ 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(substream);
+ struct fsi_priv *fsi = fsi_get_priv(substream);
long location;
location = (fsi->byte_offset - 1);
@@ -870,10 +891,16 @@ EXPORT_SYMBOL_GPL(fsi_soc_platform);
************************************************************************/
static int fsi_probe(struct platform_device *pdev)
{
+ struct fsi_master *master;
struct resource *res;
unsigned int irq;
int ret;
+ if (0 != pdev->id) {
+ dev_err(&pdev->dev, "current fsi support id 0 only now\n");
+ return -ENODEV;
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
if (!res || (int)irq <= 0) {
@@ -899,15 +926,19 @@ static int fsi_probe(struct platform_device *pdev)
master->irq = irq;
master->info = pdev->dev.platform_data;
master->fsia.base = master->base;
+ master->fsia.master = master;
master->fsib.base = master->base + 0x40;
+ master->fsib.master = master;
pm_runtime_enable(&pdev->dev);
pm_runtime_resume(&pdev->dev);
fsi_soc_dai[0].dev = &pdev->dev;
+ fsi_soc_dai[0].private_data = &master->fsia;
fsi_soc_dai[1].dev = &pdev->dev;
+ fsi_soc_dai[1].private_data = &master->fsib;
- fsi_soft_all_reset();
+ fsi_soft_all_reset(master);
ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, "fsi", master);
if (ret) {
@@ -937,6 +968,10 @@ exit:
static int fsi_remove(struct platform_device *pdev)
{
+ struct fsi_master *master;
+
+ master = fsi_get_master(fsi_soc_dai[0].private_data);
+
snd_soc_unregister_dais(fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai));
snd_soc_unregister_platform(&fsi_soc_platform);
@@ -946,7 +981,12 @@ static int fsi_remove(struct platform_device *pdev)
iounmap(master->base);
kfree(master);
- master = NULL;
+
+ fsi_soc_dai[0].dev = NULL;
+ fsi_soc_dai[0].private_data = NULL;
+ fsi_soc_dai[1].dev = NULL;
+ fsi_soc_dai[1].private_data = NULL;
+
return 0;
}