diff options
Diffstat (limited to 'sound/soc/sh/fsi.c')
-rw-r--r-- | sound/soc/sh/fsi.c | 191 |
1 files changed, 135 insertions, 56 deletions
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 8dc966f45c36..3396a0db06ba 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -41,14 +41,19 @@ #define MUTE_ST 0x0028 #define REG_END MUTE_ST + +#define CPU_INT_ST 0x01F4 +#define CPU_IEMSK 0x01F8 +#define CPU_IMSK 0x01FC #define INT_ST 0x0200 #define IEMSK 0x0204 #define IMSK 0x0208 #define MUTE 0x020C #define CLK_RST 0x0210 #define SOFT_RST 0x0214 -#define MREG_START INT_ST -#define MREG_END SOFT_RST +#define FIFO_SZ 0x0218 +#define MREG_START CPU_INT_ST +#define MREG_END FIFO_SZ /* DO_FMT */ /* DI_FMT */ @@ -80,6 +85,17 @@ #define INT_A_IN (1 << 4) #define INT_A_OUT (1 << 0) +/* SOFT_RST */ +#define PBSR (1 << 12) /* Port B Software Reset */ +#define PASR (1 << 8) /* Port A Software Reset */ +#define IR (1 << 4) /* Interrupt Reset */ +#define FSISR (1 << 0) /* Software Reset */ + +/* FIFO_SZ */ +#define OUT_SZ_MASK 0x7 +#define BO_SZ_SHIFT 8 +#define AO_SZ_SHIFT 0 + #define FSI_RATES SNDRV_PCM_RATE_8000_96000 #define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) @@ -105,11 +121,18 @@ struct fsi_priv { int periods; }; +struct fsi_regs { + u32 int_st; + u32 iemsk; + u32 imsk; +}; + struct fsi_master { void __iomem *base; int irq; struct fsi_priv fsia; struct fsi_priv fsib; + struct fsi_regs *regs; struct sh_fsi_platform_info *info; spinlock_t lock; }; @@ -317,7 +340,7 @@ static int fsi_get_fifo_residue(struct fsi_priv *fsi, int is_play) /************************************************************************ - ctrl function + irq function ************************************************************************/ @@ -326,8 +349,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, IMSK, data, data); - fsi_master_mask_set(master, IEMSK, data, data); + fsi_master_mask_set(master, master->regs->imsk, data, data); + fsi_master_mask_set(master, master->regs->iemsk, data, data); } static void fsi_irq_disable(struct fsi_priv *fsi, int is_play) @@ -335,10 +358,39 @@ 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, IMSK, data, 0); - fsi_master_mask_set(master, IEMSK, data, 0); + fsi_master_mask_set(master, master->regs->imsk, data, 0); + fsi_master_mask_set(master, master->regs->iemsk, data, 0); +} + +static u32 fsi_irq_get_status(struct fsi_master *master) +{ + return fsi_master_read(master, master->regs->int_st); +} + +static void fsi_irq_clear_all_status(struct fsi_master *master) +{ + fsi_master_write(master, master->regs->int_st, 0x0000000); } +static void fsi_irq_clear_status(struct fsi_priv *fsi) +{ + u32 data = 0; + struct fsi_master *master = fsi_get_master(fsi); + + data |= fsi_port_ab_io_bit(fsi, 0); + data |= fsi_port_ab_io_bit(fsi, 1); + + /* clear interrupt factor */ + fsi_master_mask_set(master, master->regs->int_st, data, 0); +} + +/************************************************************************ + + + ctrl function + + +************************************************************************/ static void fsi_clk_ctrl(struct fsi_priv *fsi, int enable) { u32 val = fsi_is_port_a(fsi) ? (1 << 0) : (1 << 4); @@ -350,41 +402,61 @@ static void fsi_clk_ctrl(struct fsi_priv *fsi, int enable) fsi_master_mask_set(master, CLK_RST, val, 0); } -static void fsi_irq_init(struct fsi_priv *fsi, int is_play) +static void fsi_fifo_init(struct fsi_priv *fsi, + int is_play, + struct snd_soc_dai *dai) { - u32 data; - u32 ctrl; + struct fsi_master *master = fsi_get_master(fsi); + u32 ctrl, shift, i; - data = fsi_port_ab_io_bit(fsi, is_play); - ctrl = is_play ? DOFF_CTL : DIFF_CTL; + /* get on-chip RAM capacity */ + shift = fsi_master_read(master, FIFO_SZ); + shift >>= fsi_is_port_a(fsi) ? AO_SZ_SHIFT : BO_SZ_SHIFT; + shift &= OUT_SZ_MASK; + fsi->fifo_max = 256 << shift; + dev_dbg(dai->dev, "fifo = %d words\n", fsi->fifo_max); - /* set IMSK */ - fsi_irq_disable(fsi, is_play); + /* + * The maximum number of sample data varies depending + * on the number of channels selected for the format. + * + * FIFOs are used in 4-channel units in 3-channel mode + * and in 8-channel units in 5- to 7-channel mode + * meaning that more FIFOs than the required size of DPRAM + * are used. + * + * ex) if 256 words of DP-RAM is connected + * 1 channel: 256 (256 x 1 = 256) + * 2 channels: 128 (128 x 2 = 256) + * 3 channels: 64 ( 64 x 3 = 192) + * 4 channels: 64 ( 64 x 4 = 256) + * 5 channels: 32 ( 32 x 5 = 160) + * 6 channels: 32 ( 32 x 6 = 192) + * 7 channels: 32 ( 32 x 7 = 224) + * 8 channels: 32 ( 32 x 8 = 256) + */ + for (i = 1; i < fsi->chan; i <<= 1) + fsi->fifo_max >>= 1; + dev_dbg(dai->dev, "%d channel %d store\n", fsi->chan, fsi->fifo_max); + + ctrl = is_play ? DOFF_CTL : DIFF_CTL; /* set interrupt generation factor */ fsi_reg_write(fsi, ctrl, IRQ_HALF); /* clear FIFO */ fsi_reg_mask_set(fsi, ctrl, FIFO_CLR, FIFO_CLR); - - /* clear interrupt factor */ - fsi_master_mask_set(fsi_get_master(fsi), INT_ST, data, 0); } static void fsi_soft_all_reset(struct fsi_master *master) { - u32 status = fsi_master_read(master, SOFT_RST); - /* port AB reset */ - status &= 0x000000ff; - fsi_master_write(master, SOFT_RST, status); + fsi_master_mask_set(master, SOFT_RST, PASR | PBSR, 0); mdelay(10); /* soft reset */ - status &= 0x000000f0; - fsi_master_write(master, SOFT_RST, status); - status |= 0x00000001; - fsi_master_write(master, SOFT_RST, status); + fsi_master_mask_set(master, SOFT_RST, FSISR, 0); + fsi_master_mask_set(master, SOFT_RST, FSISR, FSISR); mdelay(10); } @@ -559,12 +631,11 @@ static int fsi_data_pop(struct fsi_priv *fsi, int startup) static irqreturn_t fsi_interrupt(int irq, void *data) { struct fsi_master *master = data; - u32 status = fsi_master_read(master, SOFT_RST) & ~0x00000010; - u32 int_st = fsi_master_read(master, INT_ST); + u32 int_st = fsi_irq_get_status(master); /* clear irq status */ - fsi_master_write(master, SOFT_RST, status); - fsi_master_write(master, SOFT_RST, status | 0x00000010); + fsi_master_mask_set(master, SOFT_RST, IR, 0); + fsi_master_mask_set(master, SOFT_RST, IR, IR); if (int_st & INT_A_OUT) fsi_data_push(&master->fsia, 0); @@ -575,7 +646,7 @@ static irqreturn_t fsi_interrupt(int irq, void *data) if (int_st & INT_B_IN) fsi_data_pop(&master->fsib, 0); - fsi_master_write(master, INT_ST, 0x0000000); + fsi_irq_clear_all_status(master); return IRQ_HANDLED; } @@ -669,29 +740,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, dev_err(dai->dev, "unknown format.\n"); return -EINVAL; } - - switch (fsi->chan) { - case 1: - fsi->fifo_max = 256; - break; - case 2: - fsi->fifo_max = 128; - break; - case 3: - case 4: - fsi->fifo_max = 64; - break; - case 5: - case 6: - case 7: - case 8: - fsi->fifo_max = 32; - break; - default: - dev_err(dai->dev, "channel size error.\n"); - return -EINVAL; - } - fsi_reg_write(fsi, reg, data); /* @@ -700,8 +748,12 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, if (is_master) fsi_clk_ctrl(fsi, 1); - /* irq setting */ - fsi_irq_init(fsi, is_play); + /* irq clear */ + fsi_irq_disable(fsi, is_play); + fsi_irq_clear_status(fsi); + + /* fifo init */ + fsi_fifo_init(fsi, is_play, dai); return ret; } @@ -913,6 +965,7 @@ EXPORT_SYMBOL_GPL(fsi_soc_platform); static int fsi_probe(struct platform_device *pdev) { struct fsi_master *master; + const struct platform_device_id *id_entry; struct resource *res; unsigned int irq; int ret; @@ -922,6 +975,12 @@ static int fsi_probe(struct platform_device *pdev) return -ENODEV; } + id_entry = pdev->id_entry; + if (!id_entry) { + dev_err(&pdev->dev, "unknown fsi device\n"); + return -ENODEV; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); if (!res || (int)irq <= 0) { @@ -950,6 +1009,7 @@ static int fsi_probe(struct platform_device *pdev) master->fsia.master = master; master->fsib.base = master->base + 0x40; master->fsib.master = master; + master->regs = (struct fsi_regs *)id_entry->driver_data; spin_lock_init(&master->lock); pm_runtime_enable(&pdev->dev); @@ -962,7 +1022,8 @@ static int fsi_probe(struct platform_device *pdev) fsi_soft_all_reset(master); - ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, "fsi", master); + ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, + id_entry->name, master); if (ret) { dev_err(&pdev->dev, "irq request err\n"); goto exit_iounmap; @@ -1029,6 +1090,23 @@ static struct dev_pm_ops fsi_pm_ops = { .runtime_resume = fsi_runtime_nop, }; +static struct fsi_regs fsi_regs = { + .int_st = INT_ST, + .iemsk = IEMSK, + .imsk = IMSK, +}; + +static struct fsi_regs fsi2_regs = { + .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 }, +}; + static struct platform_driver fsi_driver = { .driver = { .name = "sh_fsi", @@ -1036,6 +1114,7 @@ static struct platform_driver fsi_driver = { }, .probe = fsi_probe, .remove = fsi_remove, + .id_table = fsi_id_table, }; static int __init fsi_mobile_init(void) |