summaryrefslogtreecommitdiff
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.c257
1 files changed, 193 insertions, 64 deletions
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index ec4acac49ebd..58c6bec642de 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -12,21 +12,12 @@
* published by the Free Software Foundation.
*/
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
#include <linux/delay.h>
-#include <linux/list.h>
#include <linux/pm_runtime.h>
#include <linux/io.h>
#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/initval.h>
#include <sound/soc.h>
-#include <sound/pcm_params.h>
#include <sound/sh_fsi.h>
-#include <asm/atomic.h>
#define DO_FMT 0x0000
#define DOFF_CTL 0x0004
@@ -39,9 +30,11 @@
#define DIDT 0x0020
#define DODT 0x0024
#define MUTE_ST 0x0028
-#define REG_END MUTE_ST
-
+#define OUT_SEL 0x0030
+#define REG_END OUT_SEL
+#define A_MST_CTLR 0x0180
+#define B_MST_CTLR 0x01A0
#define CPU_INT_ST 0x01F4
#define CPU_IEMSK 0x01F8
#define CPU_IMSK 0x01FC
@@ -52,18 +45,18 @@
#define CLK_RST 0x0210
#define SOFT_RST 0x0214
#define FIFO_SZ 0x0218
-#define MREG_START CPU_INT_ST
+#define MREG_START A_MST_CTLR
#define MREG_END FIFO_SZ
/* DO_FMT */
/* DI_FMT */
-#define CR_FMT(param) ((param) << 4)
-# define CR_MONO 0x0
-# define CR_MONO_D 0x1
-# define CR_PCM 0x2
-# define CR_I2S 0x3
-# define CR_TDM 0x4
-# define CR_TDM_D 0x5
+#define CR_MONO (0x0 << 4)
+#define CR_MONO_D (0x1 << 4)
+#define CR_PCM (0x2 << 4)
+#define CR_I2S (0x3 << 4)
+#define CR_TDM (0x4 << 4)
+#define CR_TDM_D (0x5 << 4)
+#define CR_SPDIF 0x00100120
/* DOFF_CTL */
/* DIFF_CTL */
@@ -75,6 +68,14 @@
#define ERR_UNDER 0x00000001
#define ST_ERR (ERR_OVER | ERR_UNDER)
+/* CKG1 */
+#define ACKMD_MASK 0x00007000
+#define BPFMD_MASK 0x00000700
+
+/* A/B MST_CTLR */
+#define BP (1 << 4) /* Fix the signal of Biphase output */
+#define SE (1 << 0) /* Fix the master clock */
+
/* CLK_RST */
#define B_CLK 0x00000010
#define A_CLK 0x00000001
@@ -119,9 +120,13 @@ struct fsi_priv {
int period_len;
int buffer_len;
int periods;
+
+ u32 mst_ctrl;
};
-struct fsi_regs {
+struct fsi_core {
+ int ver;
+
u32 int_st;
u32 iemsk;
u32 imsk;
@@ -132,7 +137,7 @@ struct fsi_master {
int irq;
struct fsi_priv fsia;
struct fsi_priv fsib;
- struct fsi_regs *regs;
+ struct fsi_core *core;
struct sh_fsi_platform_info *info;
spinlock_t lock;
};
@@ -169,24 +174,30 @@ static void __fsi_reg_mask_set(u32 reg, u32 mask, u32 data)
static void fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data)
{
- if (reg > REG_END)
+ if (reg > REG_END) {
+ pr_err("fsi: register access err (%s)\n", __func__);
return;
+ }
__fsi_reg_write((u32)(fsi->base + reg), data);
}
static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg)
{
- if (reg > REG_END)
+ if (reg > REG_END) {
+ pr_err("fsi: register access err (%s)\n", __func__);
return 0;
+ }
return __fsi_reg_read((u32)(fsi->base + reg));
}
static void fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data)
{
- if (reg > REG_END)
+ if (reg > REG_END) {
+ pr_err("fsi: register access err (%s)\n", __func__);
return;
+ }
__fsi_reg_mask_set((u32)(fsi->base + reg), mask, data);
}
@@ -196,8 +207,10 @@ static void fsi_master_write(struct fsi_master *master, u32 reg, u32 data)
unsigned long flags;
if ((reg < MREG_START) ||
- (reg > MREG_END))
+ (reg > MREG_END)) {
+ pr_err("fsi: register access err (%s)\n", __func__);
return;
+ }
spin_lock_irqsave(&master->lock, flags);
__fsi_reg_write((u32)(master->base + reg), data);
@@ -210,8 +223,10 @@ static u32 fsi_master_read(struct fsi_master *master, u32 reg)
unsigned long flags;
if ((reg < MREG_START) ||
- (reg > MREG_END))
+ (reg > MREG_END)) {
+ pr_err("fsi: register access err (%s)\n", __func__);
return 0;
+ }
spin_lock_irqsave(&master->lock, flags);
ret = __fsi_reg_read((u32)(master->base + reg));
@@ -226,8 +241,10 @@ static void fsi_master_mask_set(struct fsi_master *master,
unsigned long flags;
if ((reg < MREG_START) ||
- (reg > MREG_END))
+ (reg > MREG_END)) {
+ pr_err("fsi: register access err (%s)\n", __func__);
return;
+ }
spin_lock_irqsave(&master->lock, flags);
__fsi_reg_mask_set((u32)(master->base + reg), mask, data);
@@ -349,8 +366,8 @@ 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(master, master->regs->imsk, data, data);
- fsi_master_mask_set(master, master->regs->iemsk, data, data);
+ fsi_master_mask_set(master, master->core->imsk, data, data);
+ fsi_master_mask_set(master, master->core->iemsk, data, data);
}
static void fsi_irq_disable(struct fsi_priv *fsi, int is_play)
@@ -358,18 +375,18 @@ 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(master, master->regs->imsk, data, 0);
- fsi_master_mask_set(master, master->regs->iemsk, data, 0);
+ fsi_master_mask_set(master, master->core->imsk, data, 0);
+ fsi_master_mask_set(master, master->core->iemsk, data, 0);
}
static u32 fsi_irq_get_status(struct fsi_master *master)
{
- return fsi_master_read(master, master->regs->int_st);
+ return fsi_master_read(master, master->core->int_st);
}
static void fsi_irq_clear_all_status(struct fsi_master *master)
{
- fsi_master_write(master, master->regs->int_st, 0x0000000);
+ fsi_master_write(master, master->core->int_st, 0);
}
static void fsi_irq_clear_status(struct fsi_priv *fsi)
@@ -381,7 +398,30 @@ static void fsi_irq_clear_status(struct fsi_priv *fsi)
data |= fsi_port_ab_io_bit(fsi, 1);
/* clear interrupt factor */
- fsi_master_mask_set(master, master->regs->int_st, data, 0);
+ fsi_master_mask_set(master, master->core->int_st, data, 0);
+}
+
+/************************************************************************
+
+
+ SPDIF master clock function
+
+These functions are used later FSI2
+************************************************************************/
+static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable)
+{
+ struct fsi_master *master = fsi_get_master(fsi);
+ u32 val = BP | SE;
+
+ if (master->core->ver < 2) {
+ pr_err("fsi: register access err (%s)\n", __func__);
+ return;
+ }
+
+ if (enable)
+ fsi_master_mask_set(master, fsi->mst_ctrl, val, val);
+ else
+ fsi_master_mask_set(master, fsi->mst_ctrl, val, 0);
}
/************************************************************************
@@ -662,8 +702,8 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsi_priv *fsi = fsi_get_priv(substream);
- const char *msg;
u32 flags = fsi_get_info_flags(fsi);
+ struct fsi_master *master = fsi_get_master(fsi);
u32 fmt;
u32 reg;
u32 data;
@@ -700,36 +740,40 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
fmt = is_play ? SH_FSI_GET_OFMT(flags) : SH_FSI_GET_IFMT(flags);
switch (fmt) {
case SH_FSI_FMT_MONO:
- msg = "MONO";
- data = CR_FMT(CR_MONO);
+ data = CR_MONO;
fsi->chan = 1;
break;
case SH_FSI_FMT_MONO_DELAY:
- msg = "MONO Delay";
- data = CR_FMT(CR_MONO_D);
+ data = CR_MONO_D;
fsi->chan = 1;
break;
case SH_FSI_FMT_PCM:
- msg = "PCM";
- data = CR_FMT(CR_PCM);
+ data = CR_PCM;
fsi->chan = 2;
break;
case SH_FSI_FMT_I2S:
- msg = "I2S";
- data = CR_FMT(CR_I2S);
+ data = CR_I2S;
fsi->chan = 2;
break;
case SH_FSI_FMT_TDM:
- msg = "TDM";
fsi->chan = is_play ?
SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags);
- data = CR_FMT(CR_TDM) | (fsi->chan - 1);
+ data = CR_TDM | (fsi->chan - 1);
break;
case SH_FSI_FMT_TDM_DELAY:
- msg = "TDM Delay";
fsi->chan = is_play ?
SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags);
- data = CR_FMT(CR_TDM_D) | (fsi->chan - 1);
+ data = CR_TDM_D | (fsi->chan - 1);
+ break;
+ case SH_FSI_FMT_SPDIF:
+ if (master->core->ver < 2) {
+ dev_err(dai->dev, "This FSI can not use SPDIF\n");
+ return -EINVAL;
+ }
+ data = CR_SPDIF;
+ fsi->chan = 2;
+ fsi_spdif_clk_ctrl(fsi, 1);
+ fsi_reg_mask_set(fsi, OUT_SEL, 0x0010, 0x0010);
break;
default:
dev_err(dai->dev, "unknown format.\n");
@@ -737,12 +781,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
}
fsi_reg_write(fsi, reg, data);
- /*
- * clear clk reset if master mode
- */
- if (is_master)
- fsi_clk_ctrl(fsi, 1);
-
/* irq clear */
fsi_irq_disable(fsi, is_play);
fsi_irq_clear_status(fsi);
@@ -789,10 +827,93 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
return ret;
}
+static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct fsi_priv *fsi = fsi_get_priv(substream);
+ struct fsi_master *master = fsi_get_master(fsi);
+ int (*set_rate)(int is_porta, int rate) = master->info->set_rate;
+ int fsi_ver = master->core->ver;
+ int is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ int ret;
+
+ /* if slave mode, set_rate is not needed */
+ if (!fsi_is_master_mode(fsi, is_play))
+ return 0;
+
+ /* it is error if no set_rate */
+ if (!set_rate)
+ return -EIO;
+
+ ret = set_rate(fsi_is_port_a(fsi), params_rate(params));
+ 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);
+ fsi_clk_ctrl(fsi, 1);
+ ret = 0;
+ }
+
+ return ret;
+
+}
+
static struct snd_soc_dai_ops fsi_dai_ops = {
.startup = fsi_dai_startup,
.shutdown = fsi_dai_shutdown,
.trigger = fsi_dai_trigger,
+ .hw_params = fsi_dai_hw_params,
};
/************************************************************************
@@ -965,11 +1086,6 @@ static int fsi_probe(struct platform_device *pdev)
unsigned int irq;
int ret;
- if (0 != pdev->id) {
- dev_err(&pdev->dev, "current fsi support id 0 only now\n");
- return -ENODEV;
- }
-
id_entry = pdev->id_entry;
if (!id_entry) {
dev_err(&pdev->dev, "unknown fsi device\n");
@@ -998,14 +1114,21 @@ static int fsi_probe(struct platform_device *pdev)
goto exit_kfree;
}
+ /* master setting */
master->irq = irq;
master->info = pdev->dev.platform_data;
+ master->core = (struct fsi_core *)id_entry->driver_data;
+ spin_lock_init(&master->lock);
+
+ /* FSI A setting */
master->fsia.base = master->base;
master->fsia.master = master;
+ master->fsia.mst_ctrl = A_MST_CTLR;
+
+ /* FSI B setting */
master->fsib.base = master->base + 0x40;
master->fsib.master = master;
- master->regs = (struct fsi_regs *)id_entry->driver_data;
- spin_lock_init(&master->lock);
+ master->fsib.mst_ctrl = B_MST_CTLR;
pm_runtime_enable(&pdev->dev);
pm_runtime_resume(&pdev->dev);
@@ -1085,21 +1208,27 @@ static struct dev_pm_ops fsi_pm_ops = {
.runtime_resume = fsi_runtime_nop,
};
-static struct fsi_regs fsi_regs = {
+static struct fsi_core fsi1_core = {
+ .ver = 1,
+
+ /* Interrupt */
.int_st = INT_ST,
.iemsk = IEMSK,
.imsk = IMSK,
};
-static struct fsi_regs fsi2_regs = {
+static struct fsi_core fsi2_core = {
+ .ver = 2,
+
+ /* Interrupt */
.int_st = CPU_INT_ST,
.iemsk = CPU_IEMSK,
.imsk = CPU_IMSK,
};
static struct platform_device_id fsi_id_table[] = {
- { "sh_fsi", (kernel_ulong_t)&fsi_regs },
- { "sh_fsi2", (kernel_ulong_t)&fsi2_regs },
+ { "sh_fsi", (kernel_ulong_t)&fsi1_core },
+ { "sh_fsi2", (kernel_ulong_t)&fsi2_core },
};
static struct platform_driver fsi_driver = {