Merge branch 'topic/asoc' into for-linus
[safe/jmp/linux-2.6] / sound / soc / sh / fsi.c
index 8dc966f..3396a0d 100644 (file)
 #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 */
 #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)