summaryrefslogtreecommitdiffstats
path: root/sound/soc/sh/fsi.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sh/fsi.c')
-rw-r--r--sound/soc/sh/fsi.c227
1 files changed, 144 insertions, 83 deletions
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 42813b8..993abb7 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;
@@ -108,10 +110,9 @@ struct fsi_master {
struct fsi_priv fsia;
struct fsi_priv fsib;
struct sh_fsi_platform_info *info;
+ spinlock_t lock;
};
-static struct fsi_master *master;
-
/************************************************************************
@@ -119,35 +120,35 @@ static struct fsi_master *master;
************************************************************************/
-static int __fsi_reg_write(u32 reg, u32 data)
+static void __fsi_reg_write(u32 reg, u32 data)
{
/* valid data area is 24bit */
data &= 0x00ffffff;
- return ctrl_outl(data, reg);
+ __raw_writel(data, reg);
}
static u32 __fsi_reg_read(u32 reg)
{
- return ctrl_inl(reg);
+ return __raw_readl(reg);
}
-static int __fsi_reg_mask_set(u32 reg, u32 mask, u32 data)
+static void __fsi_reg_mask_set(u32 reg, u32 mask, u32 data)
{
u32 val = __fsi_reg_read(reg);
val &= ~mask;
val |= data & mask;
- return __fsi_reg_write(reg, val);
+ __fsi_reg_write(reg, val);
}
-static int fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data)
+static void fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data)
{
if (reg > REG_END)
- return -1;
+ return;
- return __fsi_reg_write((u32)(fsi->base + reg), data);
+ __fsi_reg_write((u32)(fsi->base + reg), data);
}
static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg)
@@ -158,39 +159,55 @@ static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg)
return __fsi_reg_read((u32)(fsi->base + reg));
}
-static int fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data)
+static void fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data)
{
if (reg > REG_END)
- return -1;
+ return;
- return __fsi_reg_mask_set((u32)(fsi->base + reg), mask, data);
+ __fsi_reg_mask_set((u32)(fsi->base + reg), mask, data);
}
-static int fsi_master_write(u32 reg, u32 data)
+static void fsi_master_write(struct fsi_master *master, u32 reg, u32 data)
{
+ unsigned long flags;
+
if ((reg < MREG_START) ||
(reg > MREG_END))
- return -1;
+ return;
- return __fsi_reg_write((u32)(master->base + reg), data);
+ spin_lock_irqsave(&master->lock, flags);
+ __fsi_reg_write((u32)(master->base + reg), data);
+ spin_unlock_irqrestore(&master->lock, flags);
}
-static u32 fsi_master_read(u32 reg)
+static u32 fsi_master_read(struct fsi_master *master, u32 reg)
{
+ u32 ret;
+ unsigned long flags;
+
if ((reg < MREG_START) ||
(reg > MREG_END))
return 0;
- return __fsi_reg_read((u32)(master->base + reg));
+ spin_lock_irqsave(&master->lock, flags);
+ ret = __fsi_reg_read((u32)(master->base + reg));
+ spin_unlock_irqrestore(&master->lock, flags);
+
+ return ret;
}
-static int fsi_master_mask_set(u32 reg, u32 mask, u32 data)
+static void fsi_master_mask_set(struct fsi_master *master,
+ u32 reg, u32 mask, u32 data)
{
+ unsigned long flags;
+
if ((reg < MREG_START) ||
(reg > MREG_END))
- return -1;
+ return;
- return __fsi_reg_mask_set((u32)(master->base + reg), mask, data);
+ spin_lock_irqsave(&master->lock, flags);
+ __fsi_reg_mask_set((u32)(master->base + reg), mask, data);
+ spin_unlock_irqrestore(&master->lock, flags);
}
/************************************************************************
@@ -200,43 +217,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
- */
+ struct snd_soc_dai *dai = fsi_get_dai(substream);
- if (fsi == &master->fsia)
- return 1;
-
- 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 +323,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,43 +367,46 @@ 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);
}
/* playback interrupt */
-static int fsi_data_push(struct fsi_priv *fsi)
+static int fsi_data_push(struct fsi_priv *fsi, int startup)
{
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, 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 +414,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 +453,44 @@ static int fsi_data_push(struct fsi_priv *fsi)
fsi->byte_offset += send * width;
+ status = fsi_reg_read(fsi, DOFF_ST);
+ if (!startup) {
+ struct snd_soc_dai *dai = fsi_get_dai(substream);
+
+ if (status & ERR_OVER)
+ dev_err(dai->dev, "over run\n");
+ if (status & ERR_UNDER)
+ dev_err(dai->dev, "under run\n");
+ }
+ fsi_reg_write(fsi, DOFF_ST, 0);
+
fsi_irq_enable(fsi, 1);
- if (substream)
+ if (over_period)
snd_pcm_period_elapsed(substream);
return 0;
}
-static int fsi_data_pop(struct fsi_priv *fsi)
+static int fsi_data_pop(struct fsi_priv *fsi, int startup)
{
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, 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 +498,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,9 +536,20 @@ static int fsi_data_pop(struct fsi_priv *fsi)
fsi->byte_offset += fifo_fill * width;
+ status = fsi_reg_read(fsi, DIFF_ST);
+ if (!startup) {
+ struct snd_soc_dai *dai = fsi_get_dai(substream);
+
+ if (status & ERR_OVER)
+ dev_err(dai->dev, "over run\n");
+ if (status & ERR_UNDER)
+ dev_err(dai->dev, "under run\n");
+ }
+ fsi_reg_write(fsi, DIFF_ST, 0);
+
fsi_irq_enable(fsi, 0);
- if (substream)
+ if (over_period)
snd_pcm_period_elapsed(substream);
return 0;
@@ -517,23 +557,24 @@ static int fsi_data_pop(struct fsi_priv *fsi)
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);
+ fsi_data_push(&master->fsia, 0);
if (int_st & INT_B_OUT)
- fsi_data_push(&master->fsib);
+ fsi_data_push(&master->fsib, 0);
if (int_st & INT_A_IN)
- fsi_data_pop(&master->fsia);
+ fsi_data_pop(&master->fsia, 0);
if (int_st & INT_B_IN)
- fsi_data_pop(&master->fsib);
+ fsi_data_pop(&master->fsib, 0);
- fsi_master_write(INT_ST, 0x0000000);
+ fsi_master_write(master, INT_ST, 0x0000000);
return IRQ_HANDLED;
}
@@ -548,7 +589,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 +708,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 +720,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;
@@ -689,7 +730,7 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
fsi_stream_push(fsi, substream,
frames_to_bytes(runtime, runtime->buffer_size),
frames_to_bytes(runtime, runtime->period_size));
- ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi);
+ ret = is_play ? fsi_data_push(fsi, 1) : fsi_data_pop(fsi, 1);
break;
case SNDRV_PCM_TRIGGER_STOP:
fsi_irq_disable(fsi, is_play);
@@ -760,7 +801,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 +911,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 +946,20 @@ 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;
+ spin_lock_init(&master->lock);
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 +989,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 +1002,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;
}
OpenPOWER on IntegriCloud