]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - sound/soc/sh/rcar/ssi.c
Merge remote-tracking branches 'asoc/topic/txx9', 'asoc/topic/wm8750', 'asoc/topic...
[mirror_ubuntu-zesty-kernel.git] / sound / soc / sh / rcar / ssi.c
index 42d7c7e8226bae23ce71c0acd4faaa6d215b80cf..9e7b627c08e2256dbe09acd063432b7b3b7c1107 100644 (file)
 #define SSI_NAME "ssi"
 
 struct rsnd_ssi {
-       struct clk *clk;
        struct rsnd_ssi_platform_info *info; /* rcar_snd.h */
        struct rsnd_ssi *parent;
        struct rsnd_mod mod;
 
-       struct rsnd_dai *rdai;
        u32 cr_own;
        u32 cr_clk;
        int err;
        unsigned int usrcnt;
-       unsigned int rate;
 };
 
 #define for_each_rsnd_ssi(pos, priv, i)                                        \
@@ -82,7 +79,7 @@ struct rsnd_ssi {
 #define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
 #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
 #define rsnd_dma_to_ssi(dma)  rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
-#define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0)
+#define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
 #define rsnd_ssi_dma_available(ssi) \
        rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod))
 #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
@@ -95,6 +92,9 @@ static int rsnd_ssi_use_busif(struct rsnd_mod *mod)
        struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        int use_busif = 0;
 
+       if (!rsnd_ssi_is_dma_mode(mod))
+               return 0;
+
        if (!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_NO_BUSIF))
                use_busif = 1;
        if (rsnd_io_to_mod_src(io))
@@ -125,7 +125,7 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod,
 static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
                                     struct rsnd_dai_stream *io)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
+       struct rsnd_priv *priv = rsnd_io_to_priv(io);
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
        struct device *dev = rsnd_priv_to_dev(priv);
        int i, j, ret;
@@ -154,7 +154,6 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
 
                        ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate);
                        if (0 == ret) {
-                               ssi->rate       = rate;
                                ssi->cr_clk     = FORCE | SWL_32 |
                                                  SCKD | SWSD | CKDV(j);
 
@@ -173,53 +172,60 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
 
 static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi)
 {
-       ssi->rate = 0;
        ssi->cr_clk = 0;
        rsnd_adg_ssi_clk_stop(&ssi->mod);
 }
 
 static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
-                             struct rsnd_dai *rdai,
                              struct rsnd_dai_stream *io)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
+       struct rsnd_priv *priv = rsnd_io_to_priv(io);
+       struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
        struct device *dev = rsnd_priv_to_dev(priv);
        u32 cr_mode;
        u32 cr;
 
        if (0 == ssi->usrcnt) {
-               clk_prepare_enable(ssi->clk);
+               rsnd_mod_hw_start(&ssi->mod);
 
-               if (rsnd_dai_is_clk_master(rdai)) {
+               if (rsnd_rdai_is_clk_master(rdai)) {
                        if (rsnd_ssi_clk_from_parent(ssi))
-                               rsnd_ssi_hw_start(ssi->parent, rdai, io);
+                               rsnd_ssi_hw_start(ssi->parent, io);
                        else
                                rsnd_ssi_master_clk_start(ssi, io);
                }
        }
 
        cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ?
-               DMEN :                  /* DMA : use DMA */
-               UIEN | OIEN | DIEN;     /* PIO : enable interrupt */
+               DMEN :  /* DMA : enable DMA */
+               DIEN;   /* PIO : enable Data interrupt */
 
 
        cr  =   ssi->cr_own     |
                ssi->cr_clk     |
                cr_mode         |
-               EN;
+               UIEN | OIEN | EN;
 
        rsnd_mod_write(&ssi->mod, SSICR, cr);
 
+       /* enable WS continue */
+       if (rsnd_rdai_is_clk_master(rdai))
+               rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+
+       /* clear error status */
+       rsnd_mod_write(&ssi->mod, SSISR, 0);
+
        ssi->usrcnt++;
 
        dev_dbg(dev, "%s[%d] hw started\n",
                rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
 }
 
-static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
-                            struct rsnd_dai *rdai)
+static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(&ssi->mod);
+       struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
        struct device *dev = rsnd_priv_to_dev(priv);
        u32 cr;
 
@@ -246,14 +252,14 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
                rsnd_mod_write(&ssi->mod, SSICR, cr);   /* disabled all */
                rsnd_ssi_status_check(&ssi->mod, IIRQ);
 
-               if (rsnd_dai_is_clk_master(rdai)) {
+               if (rsnd_rdai_is_clk_master(rdai)) {
                        if (rsnd_ssi_clk_from_parent(ssi))
-                               rsnd_ssi_hw_stop(ssi->parent, rdai);
+                               rsnd_ssi_hw_stop(ssi->parent);
                        else
                                rsnd_ssi_master_clk_stop(ssi);
                }
 
-               clk_disable_unprepare(ssi->clk);
+               rsnd_mod_hw_stop(&ssi->mod);
        }
 
        dev_dbg(dev, "%s[%d] hw stopped\n",
@@ -264,10 +270,11 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
  *     SSI mod common functions
  */
 static int rsnd_ssi_init(struct rsnd_mod *mod,
-                        struct rsnd_dai *rdai)
+                        struct rsnd_priv *priv)
 {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
        u32 cr;
 
@@ -301,13 +308,12 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
                cr |= SDTA;
        if (rdai->sys_delay)
                cr |= DEL;
-       if (rsnd_dai_is_play(rdai, io))
+       if (rsnd_io_is_play(io))
                cr |= TRMD;
 
        /*
         * set ssi parameter
         */
-       ssi->rdai       = rdai;
        ssi->cr_own     = cr;
        ssi->err        = -1; /* ignore 1st error */
 
@@ -315,16 +321,15 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
 }
 
 static int rsnd_ssi_quit(struct rsnd_mod *mod,
-                        struct rsnd_dai *rdai)
+                        struct rsnd_priv *priv)
 {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
 
        if (ssi->err > 0)
-               dev_warn(dev, "ssi under/over flow err = %d\n", ssi->err);
+               dev_warn(dev, "%s[%d] under/over flow err = %d\n",
+                        rsnd_mod_name(mod), rsnd_mod_id(mod), ssi->err);
 
-       ssi->rdai       = NULL;
        ssi->cr_own     = 0;
        ssi->err        = 0;
 
@@ -342,54 +347,99 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
        }
 }
 
-/*
- *             SSI PIO
- */
-static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
+static int rsnd_ssi_start(struct rsnd_mod *mod,
+                         struct rsnd_priv *priv)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+
+       rsnd_src_ssiu_start(mod, rsnd_ssi_use_busif(mod));
+
+       rsnd_ssi_hw_start(ssi, io);
+
+       rsnd_src_ssi_irq_enable(mod);
+
+       return 0;
+}
+
+static int rsnd_ssi_stop(struct rsnd_mod *mod,
+                        struct rsnd_priv *priv)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+       rsnd_src_ssi_irq_disable(mod);
+
+       rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
+
+       rsnd_ssi_hw_stop(ssi);
+
+       rsnd_src_ssiu_stop(mod);
+
+       return 0;
+}
+
+static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
 {
        struct rsnd_ssi *ssi = data;
        struct rsnd_mod *mod = &ssi->mod;
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       int is_dma = rsnd_ssi_is_dma_mode(mod);
        u32 status = rsnd_mod_read(mod, SSISR);
-       irqreturn_t ret = IRQ_NONE;
 
-       if (io && (status & DIRQ)) {
-               struct rsnd_dai *rdai = ssi->rdai;
+       if (!io)
+               return IRQ_NONE;
+
+       /* PIO only */
+       if (!is_dma && (status & DIRQ)) {
                struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
                u32 *buf = (u32 *)(runtime->dma_area +
                                   rsnd_dai_pointer_offset(io, 0));
 
-               rsnd_ssi_record_error(ssi, status);
-
                /*
                 * 8/16/32 data can be assesse to TDR/RDR register
                 * directly as 32bit data
                 * see rsnd_ssi_init()
                 */
-               if (rsnd_dai_is_play(rdai, io))
+               if (rsnd_io_is_play(io))
                        rsnd_mod_write(mod, SSITDR, *buf);
                else
                        *buf = rsnd_mod_read(mod, SSIRDR);
 
                rsnd_dai_pointer_update(io, sizeof(*buf));
+       }
 
-               ret = IRQ_HANDLED;
+       /* PIO / DMA */
+       if (status & (UIRQ | OIRQ)) {
+               struct device *dev = rsnd_priv_to_dev(priv);
+
+               /*
+                * restart SSI
+                */
+               rsnd_ssi_stop(mod, priv);
+               rsnd_ssi_start(mod, priv);
+
+               dev_dbg(dev, "%s[%d] restart\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
        }
 
-       return ret;
+       rsnd_ssi_record_error(ssi, status);
+
+       return IRQ_HANDLED;
 }
 
+/*
+ *             SSI PIO
+ */
 static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai)
+                             struct rsnd_priv *priv)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       int irq = ssi->info->pio_irq;
        int ret;
 
-       ret = devm_request_irq(dev, irq,
-                              rsnd_ssi_pio_interrupt,
+       ret = devm_request_irq(dev, ssi->info->irq,
+                              rsnd_ssi_interrupt,
                               IRQF_SHARED,
                               dev_name(dev), ssi);
        if (ret)
@@ -402,80 +452,67 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
        return ret;
 }
 
-static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai)
-{
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
-
-       rsnd_src_ssiu_start(mod, rdai, 0);
-
-       rsnd_ssi_hw_start(ssi, rdai, io);
-
-       rsnd_src_ssi_irq_enable(mod, rdai);
-
-       return 0;
-}
-
-static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
-                            struct rsnd_dai *rdai)
-{
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-
-       rsnd_src_ssi_irq_disable(mod, rdai);
-
-       rsnd_ssi_hw_stop(ssi, rdai);
-
-       rsnd_src_ssiu_stop(mod, rdai);
-
-       return 0;
-}
-
 static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
        .name   = SSI_NAME,
        .probe  = rsnd_ssi_pio_probe,
        .init   = rsnd_ssi_init,
        .quit   = rsnd_ssi_quit,
-       .start  = rsnd_ssi_pio_start,
-       .stop   = rsnd_ssi_pio_stop,
+       .start  = rsnd_ssi_start,
+       .stop   = rsnd_ssi_stop,
 };
 
 static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
-                         struct rsnd_dai *rdai)
+                             struct rsnd_priv *priv)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
        int dma_id = ssi->info->dma_id;
        int ret;
 
+       ret = devm_request_irq(dev, ssi->info->irq,
+                              rsnd_ssi_interrupt,
+                              IRQF_SHARED,
+                              dev_name(dev), ssi);
+       if (ret)
+               goto rsnd_ssi_dma_probe_fail;
+
        ret = rsnd_dma_init(
                priv, rsnd_mod_to_dma(mod),
                rsnd_info_is_playback(priv, ssi),
                dma_id);
+       if (ret)
+               goto rsnd_ssi_dma_probe_fail;
 
-       if (ret < 0)
-               dev_err(dev, "%s[%d] (DMA) is failed\n",
-                       rsnd_mod_name(mod), rsnd_mod_id(mod));
-       else
-               dev_dbg(dev, "%s[%d] (DMA) is probed\n",
-                       rsnd_mod_name(mod), rsnd_mod_id(mod));
+       dev_dbg(dev, "%s[%d] (DMA) is probed\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+       return ret;
+
+rsnd_ssi_dma_probe_fail:
+       dev_err(dev, "%s[%d] (DMA) is failed\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return ret;
 }
 
 static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
-                              struct rsnd_dai *rdai)
+                              struct rsnd_priv *priv)
 {
-       rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod));
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       int irq = ssi->info->irq;
+
+       rsnd_dma_quit(priv, rsnd_mod_to_dma(mod));
+
+       /* PIO will request IRQ again */
+       devm_free_irq(dev, irq, ssi);
 
        return 0;
 }
 
 static int rsnd_ssi_fallback(struct rsnd_mod *mod,
-                            struct rsnd_dai *rdai)
+                            struct rsnd_priv *priv)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
 
        /*
@@ -494,39 +531,26 @@ static int rsnd_ssi_fallback(struct rsnd_mod *mod,
 }
 
 static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai)
+                             struct rsnd_priv *priv)
 {
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
-       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
-
-       rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
+       struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
 
        rsnd_dma_start(dma);
 
-       rsnd_ssi_hw_start(ssi, ssi->rdai, io);
-
-       /* enable WS continue */
-       if (rsnd_dai_is_clk_master(rdai))
-               rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+       rsnd_ssi_start(mod, priv);
 
        return 0;
 }
 
 static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
-                            struct rsnd_dai *rdai)
+                            struct rsnd_priv *priv)
 {
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
+       struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
 
-       rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
-
-       rsnd_ssi_hw_stop(ssi, rdai);
+       rsnd_ssi_stop(mod, priv);
 
        rsnd_dma_stop(dma);
 
-       rsnd_src_ssiu_stop(mod, rdai);
-
        return 0;
 }
 
@@ -646,7 +670,7 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
                /*
                 * irq
                 */
-               ssi_info->pio_irq = irq_of_parse_and_map(np, 0);
+               ssi_info->irq = irq_of_parse_and_map(np, 0);
 
                /*
                 * DMA
@@ -701,7 +725,6 @@ int rsnd_ssi_probe(struct platform_device *pdev,
                        return PTR_ERR(clk);
 
                ssi->info       = pinfo;
-               ssi->clk        = clk;
 
                ops = &rsnd_ssi_non_ops;
                if (pinfo->dma_id > 0)
@@ -709,7 +732,7 @@ int rsnd_ssi_probe(struct platform_device *pdev,
                else if (rsnd_ssi_pio_available(ssi))
                        ops = &rsnd_ssi_pio_ops;
 
-               rsnd_mod_init(priv, &ssi->mod, ops, RSND_MOD_SSI, i);
+               rsnd_mod_init(&ssi->mod, ops, clk, RSND_MOD_SSI, i);
 
                rsnd_ssi_parent_clk_setup(priv, ssi);
        }