aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>2013-07-22 00:36:57 -0400
committerMark Brown <broonie@linaro.org>2013-07-28 14:34:09 -0400
commitae5c322303fff50b93d60e34c6563f1264a5941b (patch)
treeec37f50997c26bd8601502a640060fa66694ab61
parentdfc9403b7c1f566bb099a12c58aee20589e390f1 (diff)
ASoC: add Renesas R-Car SSI feature
Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuit is different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2) (Actually, there are many difference in Generation1 chips) As 1st protype, this patch adds SSI feature on this driver. But, it is PIO sound playback support only at this point. The DMA transfer, and capture feature will be supported in the future Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Signed-off-by: Mark Brown <broonie@linaro.org>
-rw-r--r--include/sound/rcar_snd.h23
-rw-r--r--sound/soc/sh/rcar/Makefile2
-rw-r--r--sound/soc/sh/rcar/core.c5
-rw-r--r--sound/soc/sh/rcar/gen.c24
-rw-r--r--sound/soc/sh/rcar/rsnd.h23
-rw-r--r--sound/soc/sh/rcar/ssi.c588
6 files changed, 661 insertions, 4 deletions
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h
index 6babd6f7b537..99d8dd029906 100644
--- a/include/sound/rcar_snd.h
+++ b/include/sound/rcar_snd.h
@@ -16,11 +16,30 @@
16 16
17#define RSND_GEN1_SRU 0 17#define RSND_GEN1_SRU 0
18#define RSND_GEN1_ADG 1 18#define RSND_GEN1_ADG 1
19#define RSND_GEN1_SSI 2
19 20
20#define RSND_GEN2_SRU 0 21#define RSND_GEN2_SRU 0
21#define RSND_GEN2_ADG 1 22#define RSND_GEN2_ADG 1
23#define RSND_GEN2_SSIU 2
24#define RSND_GEN2_SSI 3
22 25
23#define RSND_BASE_MAX 2 26#define RSND_BASE_MAX 4
27
28/*
29 * flags
30 *
31 * 0xA0000000
32 *
33 * A : clock sharing settings
34 */
35#define RSND_SSI_CLK_PIN_SHARE (1 << 31)
36#define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */
37#define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */
38
39struct rsnd_ssi_platform_info {
40 int pio_irq;
41 u32 flags;
42};
24 43
25struct rsnd_scu_platform_info { 44struct rsnd_scu_platform_info {
26 u32 flags; 45 u32 flags;
@@ -43,6 +62,8 @@ struct rsnd_dai_platform_info {
43 62
44struct rcar_snd_info { 63struct rcar_snd_info {
45 u32 flags; 64 u32 flags;
65 struct rsnd_ssi_platform_info *ssi_info;
66 int ssi_info_nr;
46 struct rsnd_scu_platform_info *scu_info; 67 struct rsnd_scu_platform_info *scu_info;
47 int scu_info_nr; 68 int scu_info_nr;
48 struct rsnd_dai_platform_info *dai_info; 69 struct rsnd_dai_platform_info *dai_info;
diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile
index c11280cffcfe..0ff492df7929 100644
--- a/sound/soc/sh/rcar/Makefile
+++ b/sound/soc/sh/rcar/Makefile
@@ -1,2 +1,2 @@
1snd-soc-rcar-objs := core.o gen.o scu.o adg.o 1snd-soc-rcar-objs := core.o gen.o scu.o adg.o ssi.o
2obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file 2obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index e588d8a8ae40..9a5469d3f352 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -639,6 +639,10 @@ static int rsnd_probe(struct platform_device *pdev)
639 if (ret < 0) 639 if (ret < 0)
640 return ret; 640 return ret;
641 641
642 ret = rsnd_ssi_probe(pdev, info, priv);
643 if (ret < 0)
644 return ret;
645
642 /* 646 /*
643 * asoc register 647 * asoc register
644 */ 648 */
@@ -677,6 +681,7 @@ static int rsnd_remove(struct platform_device *pdev)
677 /* 681 /*
678 * remove each module 682 * remove each module
679 */ 683 */
684 rsnd_ssi_remove(pdev, priv);
680 rsnd_adg_remove(pdev, priv); 685 rsnd_adg_remove(pdev, priv);
681 rsnd_scu_remove(pdev, priv); 686 rsnd_scu_remove(pdev, priv);
682 rsnd_dai_remove(pdev, priv); 687 rsnd_dai_remove(pdev, priv);
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index ed21a136354f..5e4ae0da4352 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -72,6 +72,12 @@ static int rsnd_gen1_path_init(struct rsnd_priv *priv,
72 else 72 else
73 id = info->ssi_id_capture; 73 id = info->ssi_id_capture;
74 74
75 /* SSI */
76 mod = rsnd_ssi_mod_get(priv, id);
77 ret = rsnd_dai_connect(rdai, mod, io);
78 if (ret < 0)
79 return ret;
80
75 /* SCU */ 81 /* SCU */
76 mod = rsnd_scu_mod_get(priv, id); 82 mod = rsnd_scu_mod_get(priv, id);
77 ret = rsnd_dai_connect(rdai, mod, io); 83 ret = rsnd_dai_connect(rdai, mod, io);
@@ -120,6 +126,12 @@ static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen)
120 RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18); 126 RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18);
121 RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c); 127 RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c);
122 RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20); 128 RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20);
129
130 RSND_GEN1_REG_MAP(gen, SSI, SSICR, 0x40, 0x00);
131 RSND_GEN1_REG_MAP(gen, SSI, SSISR, 0x40, 0x04);
132 RSND_GEN1_REG_MAP(gen, SSI, SSITDR, 0x40, 0x08);
133 RSND_GEN1_REG_MAP(gen, SSI, SSIRDR, 0x40, 0x0c);
134 RSND_GEN1_REG_MAP(gen, SSI, SSIWSR, 0x40, 0x20);
123} 135}
124 136
125static int rsnd_gen1_probe(struct platform_device *pdev, 137static int rsnd_gen1_probe(struct platform_device *pdev,
@@ -130,14 +142,17 @@ static int rsnd_gen1_probe(struct platform_device *pdev,
130 struct rsnd_gen *gen = rsnd_priv_to_gen(priv); 142 struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
131 struct resource *sru_res; 143 struct resource *sru_res;
132 struct resource *adg_res; 144 struct resource *adg_res;
145 struct resource *ssi_res;
133 146
134 /* 147 /*
135 * map address 148 * map address
136 */ 149 */
137 sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU); 150 sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU);
138 adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG); 151 adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG);
152 ssi_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SSI);
139 if (!sru_res || 153 if (!sru_res ||
140 !adg_res) { 154 !adg_res ||
155 !ssi_res) {
141 dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n"); 156 dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n");
142 return -ENODEV; 157 return -ENODEV;
143 } 158 }
@@ -146,8 +161,10 @@ static int rsnd_gen1_probe(struct platform_device *pdev,
146 161
147 gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res); 162 gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res);
148 gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res); 163 gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res);
164 gen->base[RSND_GEN1_SSI] = devm_ioremap_resource(dev, ssi_res);
149 if (!gen->base[RSND_GEN1_SRU] || 165 if (!gen->base[RSND_GEN1_SRU] ||
150 !gen->base[RSND_GEN1_ADG]) { 166 !gen->base[RSND_GEN1_ADG] ||
167 !gen->base[RSND_GEN1_SSI]) {
151 dev_err(dev, "SRU/SSI/ADG ioremap failed\n"); 168 dev_err(dev, "SRU/SSI/ADG ioremap failed\n");
152 return -ENODEV; 169 return -ENODEV;
153 } 170 }
@@ -159,8 +176,11 @@ static int rsnd_gen1_probe(struct platform_device *pdev,
159 gen->base[RSND_GEN1_SRU]); 176 gen->base[RSND_GEN1_SRU]);
160 dev_dbg(dev, "ADG : %08x => %p\n", adg_res->start, 177 dev_dbg(dev, "ADG : %08x => %p\n", adg_res->start,
161 gen->base[RSND_GEN1_ADG]); 178 gen->base[RSND_GEN1_ADG]);
179 dev_dbg(dev, "SSI : %08x => %p\n", ssi_res->start,
180 gen->base[RSND_GEN1_SSI]);
162 181
163 return 0; 182 return 0;
183
164} 184}
165 185
166static void rsnd_gen1_remove(struct platform_device *pdev, 186static void rsnd_gen1_remove(struct platform_device *pdev,
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 344fd59cb7fd..0e7727cc41db 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -43,6 +43,13 @@ enum rsnd_reg {
43 RSND_REG_AUDIO_CLK_SEL4, 43 RSND_REG_AUDIO_CLK_SEL4,
44 RSND_REG_AUDIO_CLK_SEL5, 44 RSND_REG_AUDIO_CLK_SEL5,
45 45
46 /* SSI */
47 RSND_REG_SSICR,
48 RSND_REG_SSISR,
49 RSND_REG_SSITDR,
50 RSND_REG_SSIRDR,
51 RSND_REG_SSIWSR,
52
46 RSND_REG_MAX, 53 RSND_REG_MAX,
47}; 54};
48 55
@@ -151,6 +158,7 @@ int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod,
151 struct rsnd_dai_stream *io); 158 struct rsnd_dai_stream *io);
152int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); 159int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io);
153#define rsnd_dai_get_platform_info(rdai) ((rdai)->info) 160#define rsnd_dai_get_platform_info(rdai) ((rdai)->info)
161#define rsnd_io_to_runtime(io) ((io)->substream->runtime)
154 162
155void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); 163void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
156int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); 164int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
@@ -210,6 +218,11 @@ struct rsnd_priv {
210 void *adg; 218 void *adg;
211 219
212 /* 220 /*
221 * below value will be filled on rsnd_ssi_probe()
222 */
223 void *ssiu;
224
225 /*
213 * below value will be filled on rsnd_dai_probe() 226 * below value will be filled on rsnd_dai_probe()
214 */ 227 */
215 struct snd_soc_dai_driver *daidrv; 228 struct snd_soc_dai_driver *daidrv;
@@ -232,4 +245,14 @@ void rsnd_scu_remove(struct platform_device *pdev,
232struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id); 245struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id);
233#define rsnd_scu_nr(priv) ((priv)->scu_nr) 246#define rsnd_scu_nr(priv) ((priv)->scu_nr)
234 247
248/*
249 * R-Car SSI
250 */
251int rsnd_ssi_probe(struct platform_device *pdev,
252 struct rcar_snd_info *info,
253 struct rsnd_priv *priv);
254void rsnd_ssi_remove(struct platform_device *pdev,
255 struct rsnd_priv *priv);
256struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
257
235#endif 258#endif
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
new file mode 100644
index 000000000000..061ac7e88309
--- /dev/null
+++ b/sound/soc/sh/rcar/ssi.c
@@ -0,0 +1,588 @@
1/*
2 * Renesas R-Car SSIU/SSI support
3 *
4 * Copyright (C) 2013 Renesas Solutions Corp.
5 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
6 *
7 * Based on fsi.c
8 * Kuninori Morimoto <morimoto.kuninori@renesas.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 */
14#include <linux/delay.h>
15#include "rsnd.h"
16#define RSND_SSI_NAME_SIZE 16
17
18/*
19 * SSICR
20 */
21#define FORCE (1 << 31) /* Fixed */
22#define UIEN (1 << 27) /* Underflow Interrupt Enable */
23#define OIEN (1 << 26) /* Overflow Interrupt Enable */
24#define IIEN (1 << 25) /* Idle Mode Interrupt Enable */
25#define DIEN (1 << 24) /* Data Interrupt Enable */
26
27#define DWL_8 (0 << 19) /* Data Word Length */
28#define DWL_16 (1 << 19) /* Data Word Length */
29#define DWL_18 (2 << 19) /* Data Word Length */
30#define DWL_20 (3 << 19) /* Data Word Length */
31#define DWL_22 (4 << 19) /* Data Word Length */
32#define DWL_24 (5 << 19) /* Data Word Length */
33#define DWL_32 (6 << 19) /* Data Word Length */
34
35#define SWL_32 (3 << 16) /* R/W System Word Length */
36#define SCKD (1 << 15) /* Serial Bit Clock Direction */
37#define SWSD (1 << 14) /* Serial WS Direction */
38#define SCKP (1 << 13) /* Serial Bit Clock Polarity */
39#define SWSP (1 << 12) /* Serial WS Polarity */
40#define SDTA (1 << 10) /* Serial Data Alignment */
41#define DEL (1 << 8) /* Serial Data Delay */
42#define CKDV(v) (v << 4) /* Serial Clock Division Ratio */
43#define TRMD (1 << 1) /* Transmit/Receive Mode Select */
44#define EN (1 << 0) /* SSI Module Enable */
45
46/*
47 * SSISR
48 */
49#define UIRQ (1 << 27) /* Underflow Error Interrupt Status */
50#define OIRQ (1 << 26) /* Overflow Error Interrupt Status */
51#define IIRQ (1 << 25) /* Idle Mode Interrupt Status */
52#define DIRQ (1 << 24) /* Data Interrupt Status Flag */
53
54struct rsnd_ssi {
55 struct clk *clk;
56 struct rsnd_ssi_platform_info *info; /* rcar_snd.h */
57 struct rsnd_ssi *parent;
58 struct rsnd_mod mod;
59
60 struct rsnd_dai *rdai;
61 struct rsnd_dai_stream *io;
62 u32 cr_own;
63 u32 cr_clk;
64 u32 cr_etc;
65 int err;
66 unsigned int usrcnt;
67 unsigned int rate;
68};
69
70struct rsnd_ssiu {
71 u32 ssi_mode0;
72 u32 ssi_mode1;
73
74 int ssi_nr;
75 struct rsnd_ssi *ssi;
76};
77
78#define for_each_rsnd_ssi(pos, priv, i) \
79 for (i = 0; \
80 (i < rsnd_ssi_nr(priv)) && \
81 ((pos) = ((struct rsnd_ssiu *)((priv)->ssiu))->ssi + i); \
82 i++)
83
84#define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr)
85#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
86#define rsnd_ssi_is_pio(ssi) ((ssi)->info->pio_irq > 0)
87#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
88#define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master)
89#define rsnd_ssi_mode_flags(p) ((p)->info->flags)
90#define rsnd_ssi_to_ssiu(ssi)\
91 (((struct rsnd_ssiu *)((ssi) - rsnd_mod_id(&(ssi)->mod))) - 1)
92
93static void rsnd_ssi_mode_init(struct rsnd_priv *priv,
94 struct rsnd_ssiu *ssiu)
95{
96 struct rsnd_ssi *ssi;
97 u32 flags;
98 u32 val;
99 int i;
100
101 /*
102 * SSI_MODE0
103 */
104 ssiu->ssi_mode0 = 0;
105 for_each_rsnd_ssi(ssi, priv, i)
106 ssiu->ssi_mode0 |= (1 << i);
107
108 /*
109 * SSI_MODE1
110 */
111#define ssi_parent_set(p, sync, adg, ext) \
112 do { \
113 ssi->parent = ssiu->ssi + p; \
114 if (flags & RSND_SSI_CLK_FROM_ADG) \
115 val = adg; \
116 else \
117 val = ext; \
118 if (flags & RSND_SSI_SYNC) \
119 val |= sync; \
120 } while (0)
121
122 ssiu->ssi_mode1 = 0;
123 for_each_rsnd_ssi(ssi, priv, i) {
124 flags = rsnd_ssi_mode_flags(ssi);
125
126 if (!(flags & RSND_SSI_CLK_PIN_SHARE))
127 continue;
128
129 val = 0;
130 switch (i) {
131 case 1:
132 ssi_parent_set(0, (1 << 4), (0x2 << 0), (0x1 << 0));
133 break;
134 case 2:
135 ssi_parent_set(0, (1 << 4), (0x2 << 2), (0x1 << 2));
136 break;
137 case 4:
138 ssi_parent_set(3, (1 << 20), (0x2 << 16), (0x1 << 16));
139 break;
140 case 8:
141 ssi_parent_set(7, 0, 0, 0);
142 break;
143 }
144
145 ssiu->ssi_mode1 |= val;
146 }
147}
148
149static void rsnd_ssi_mode_set(struct rsnd_ssi *ssi)
150{
151 struct rsnd_ssiu *ssiu = rsnd_ssi_to_ssiu(ssi);
152
153 rsnd_mod_write(&ssi->mod, SSI_MODE0, ssiu->ssi_mode0);
154 rsnd_mod_write(&ssi->mod, SSI_MODE1, ssiu->ssi_mode1);
155}
156
157static void rsnd_ssi_status_check(struct rsnd_mod *mod,
158 u32 bit)
159{
160 struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
161 struct device *dev = rsnd_priv_to_dev(priv);
162 u32 status;
163 int i;
164
165 for (i = 0; i < 1024; i++) {
166 status = rsnd_mod_read(mod, SSISR);
167 if (status & bit)
168 return;
169
170 udelay(50);
171 }
172
173 dev_warn(dev, "status check failed\n");
174}
175
176static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
177 unsigned int rate)
178{
179 struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
180 struct device *dev = rsnd_priv_to_dev(priv);
181 int i, j, ret;
182 int adg_clk_div_table[] = {
183 1, 6, /* see adg.c */
184 };
185 int ssi_clk_mul_table[] = {
186 1, 2, 4, 8, 16, 6, 12,
187 };
188 unsigned int main_rate;
189
190 /*
191 * Find best clock, and try to start ADG
192 */
193 for (i = 0; i < ARRAY_SIZE(adg_clk_div_table); i++) {
194 for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
195
196 /*
197 * this driver is assuming that
198 * system word is 64fs (= 2 x 32bit)
199 * see rsnd_ssi_start()
200 */
201 main_rate = rate / adg_clk_div_table[i]
202 * 32 * 2 * ssi_clk_mul_table[j];
203
204 ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate);
205 if (0 == ret) {
206 ssi->rate = rate;
207 ssi->cr_clk = FORCE | SWL_32 |
208 SCKD | SWSD | CKDV(j);
209
210 dev_dbg(dev, "ssi%d outputs %u Hz\n",
211 rsnd_mod_id(&ssi->mod), rate);
212
213 return 0;
214 }
215 }
216 }
217
218 dev_err(dev, "unsupported clock rate\n");
219 return -EIO;
220}
221
222static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi)
223{
224 ssi->rate = 0;
225 ssi->cr_clk = 0;
226 rsnd_adg_ssi_clk_stop(&ssi->mod);
227}
228
229static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
230 struct rsnd_dai *rdai,
231 struct rsnd_dai_stream *io)
232{
233 struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
234 struct device *dev = rsnd_priv_to_dev(priv);
235 u32 cr;
236
237 if (0 == ssi->usrcnt) {
238 clk_enable(ssi->clk);
239
240 if (rsnd_rdai_is_clk_master(rdai)) {
241 struct snd_pcm_runtime *runtime;
242
243 runtime = rsnd_io_to_runtime(io);
244
245 if (rsnd_ssi_clk_from_parent(ssi))
246 rsnd_ssi_hw_start(ssi->parent, rdai, io);
247 else
248 rsnd_ssi_master_clk_start(ssi, runtime->rate);
249 }
250 }
251
252 cr = ssi->cr_own |
253 ssi->cr_clk |
254 ssi->cr_etc |
255 EN;
256
257 rsnd_mod_write(&ssi->mod, SSICR, cr);
258
259 ssi->usrcnt++;
260
261 dev_dbg(dev, "ssi%d hw started\n", rsnd_mod_id(&ssi->mod));
262}
263
264static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
265 struct rsnd_dai *rdai)
266{
267 struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
268 struct device *dev = rsnd_priv_to_dev(priv);
269 u32 cr;
270
271 if (0 == ssi->usrcnt) /* stop might be called without start */
272 return;
273
274 ssi->usrcnt--;
275
276 if (0 == ssi->usrcnt) {
277 /*
278 * disable all IRQ,
279 * and, wait all data was sent
280 */
281 cr = ssi->cr_own |
282 ssi->cr_clk;
283
284 rsnd_mod_write(&ssi->mod, SSICR, cr | EN);
285 rsnd_ssi_status_check(&ssi->mod, DIRQ);
286
287 /*
288 * disable SSI,
289 * and, wait idle state
290 */
291 rsnd_mod_write(&ssi->mod, SSICR, cr); /* disabled all */
292 rsnd_ssi_status_check(&ssi->mod, IIRQ);
293
294 if (rsnd_rdai_is_clk_master(rdai)) {
295 if (rsnd_ssi_clk_from_parent(ssi))
296 rsnd_ssi_hw_stop(ssi->parent, rdai);
297 else
298 rsnd_ssi_master_clk_stop(ssi);
299 }
300
301 clk_disable(ssi->clk);
302 }
303
304 dev_dbg(dev, "ssi%d hw stopped\n", rsnd_mod_id(&ssi->mod));
305}
306
307/*
308 * SSI mod common functions
309 */
310static int rsnd_ssi_init(struct rsnd_mod *mod,
311 struct rsnd_dai *rdai,
312 struct rsnd_dai_stream *io)
313{
314 struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
315 struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
316 struct device *dev = rsnd_priv_to_dev(priv);
317 struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
318 u32 cr;
319
320 cr = FORCE;
321
322 /*
323 * always use 32bit system word for easy clock calculation.
324 * see also rsnd_ssi_master_clk_enable()
325 */
326 cr |= SWL_32;
327
328 /*
329 * init clock settings for SSICR
330 */
331 switch (runtime->sample_bits) {
332 case 16:
333 cr |= DWL_16;
334 break;
335 case 32:
336 cr |= DWL_24;
337 break;
338 default:
339 return -EIO;
340 }
341
342 if (rdai->bit_clk_inv)
343 cr |= SCKP;
344 if (rdai->frm_clk_inv)
345 cr |= SWSP;
346 if (rdai->data_alignment)
347 cr |= SDTA;
348 if (rdai->sys_delay)
349 cr |= DEL;
350 if (rsnd_dai_is_play(rdai, io))
351 cr |= TRMD;
352
353 /*
354 * set ssi parameter
355 */
356 ssi->rdai = rdai;
357 ssi->io = io;
358 ssi->cr_own = cr;
359 ssi->err = -1; /* ignore 1st error */
360
361 rsnd_ssi_mode_set(ssi);
362
363 dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
364
365 return 0;
366}
367
368static int rsnd_ssi_quit(struct rsnd_mod *mod,
369 struct rsnd_dai *rdai,
370 struct rsnd_dai_stream *io)
371{
372 struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
373 struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
374 struct device *dev = rsnd_priv_to_dev(priv);
375
376 dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
377
378 if (ssi->err > 0)
379 dev_warn(dev, "ssi under/over flow err = %d\n", ssi->err);
380
381 ssi->rdai = NULL;
382 ssi->io = NULL;
383 ssi->cr_own = 0;
384 ssi->err = 0;
385
386 return 0;
387}
388
389static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
390{
391 /* under/over flow error */
392 if (status & (UIRQ | OIRQ)) {
393 ssi->err++;
394
395 /* clear error status */
396 rsnd_mod_write(&ssi->mod, SSISR, 0);
397 }
398}
399
400/*
401 * SSI PIO
402 */
403static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
404{
405 struct rsnd_ssi *ssi = data;
406 struct rsnd_dai_stream *io = ssi->io;
407 u32 status = rsnd_mod_read(&ssi->mod, SSISR);
408 irqreturn_t ret = IRQ_NONE;
409
410 if (io && (status & DIRQ)) {
411 struct rsnd_dai *rdai = ssi->rdai;
412 struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
413 u32 *buf = (u32 *)(runtime->dma_area +
414 rsnd_dai_pointer_offset(io, 0));
415
416 rsnd_ssi_record_error(ssi, status);
417
418 /*
419 * 8/16/32 data can be assesse to TDR/RDR register
420 * directly as 32bit data
421 * see rsnd_ssi_init()
422 */
423 if (rsnd_dai_is_play(rdai, io))
424 rsnd_mod_write(&ssi->mod, SSITDR, *buf);
425 else
426 *buf = rsnd_mod_read(&ssi->mod, SSIRDR);
427
428 rsnd_dai_pointer_update(io, sizeof(*buf));
429
430 ret = IRQ_HANDLED;
431 }
432
433 return ret;
434}
435
436static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
437 struct rsnd_dai *rdai,
438 struct rsnd_dai_stream *io)
439{
440 struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
441 struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
442 struct device *dev = rsnd_priv_to_dev(priv);
443
444 /* enable PIO IRQ */
445 ssi->cr_etc = UIEN | OIEN | DIEN;
446
447 rsnd_ssi_hw_start(ssi, rdai, io);
448
449 dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
450
451 return 0;
452}
453
454static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
455 struct rsnd_dai *rdai,
456 struct rsnd_dai_stream *io)
457{
458 struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
459 struct device *dev = rsnd_priv_to_dev(priv);
460 struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
461
462 dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
463
464 ssi->cr_etc = 0;
465
466 rsnd_ssi_hw_stop(ssi, rdai);
467
468 return 0;
469}
470
471static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
472 .name = "ssi (pio)",
473 .init = rsnd_ssi_init,
474 .quit = rsnd_ssi_quit,
475 .start = rsnd_ssi_pio_start,
476 .stop = rsnd_ssi_pio_stop,
477};
478
479/*
480 * Non SSI
481 */
482static int rsnd_ssi_non(struct rsnd_mod *mod,
483 struct rsnd_dai *rdai,
484 struct rsnd_dai_stream *io)
485{
486 struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
487 struct device *dev = rsnd_priv_to_dev(priv);
488
489 dev_dbg(dev, "%s\n", __func__);
490
491 return 0;
492}
493
494static struct rsnd_mod_ops rsnd_ssi_non_ops = {
495 .name = "ssi (non)",
496 .init = rsnd_ssi_non,
497 .quit = rsnd_ssi_non,
498 .start = rsnd_ssi_non,
499 .stop = rsnd_ssi_non,
500};
501
502/*
503 * ssi mod function
504 */
505struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
506{
507 BUG_ON(id < 0 || id >= rsnd_ssi_nr(priv));
508
509 return &(((struct rsnd_ssiu *)(priv->ssiu))->ssi + id)->mod;
510}
511
512int rsnd_ssi_probe(struct platform_device *pdev,
513 struct rcar_snd_info *info,
514 struct rsnd_priv *priv)
515{
516 struct rsnd_ssi_platform_info *pinfo;
517 struct device *dev = rsnd_priv_to_dev(priv);
518 struct rsnd_mod_ops *ops;
519 struct clk *clk;
520 struct rsnd_ssiu *ssiu;
521 struct rsnd_ssi *ssi;
522 char name[RSND_SSI_NAME_SIZE];
523 int i, nr, ret;
524
525 /*
526 * init SSI
527 */
528 nr = info->ssi_info_nr;
529 ssiu = devm_kzalloc(dev, sizeof(*ssiu) + (sizeof(*ssi) * nr),
530 GFP_KERNEL);
531 if (!ssiu) {
532 dev_err(dev, "SSI allocate failed\n");
533 return -ENOMEM;
534 }
535
536 priv->ssiu = ssiu;
537 ssiu->ssi = (struct rsnd_ssi *)(ssiu + 1);
538 ssiu->ssi_nr = nr;
539
540 for_each_rsnd_ssi(ssi, priv, i) {
541 pinfo = &info->ssi_info[i];
542
543 snprintf(name, RSND_SSI_NAME_SIZE, "ssi.%d", i);
544
545 clk = clk_get(dev, name);
546 if (IS_ERR(clk))
547 return PTR_ERR(clk);
548
549 ssi->info = pinfo;
550 ssi->clk = clk;
551
552 ops = &rsnd_ssi_non_ops;
553
554 /*
555 * SSI PIO case
556 */
557 if (rsnd_ssi_is_pio(ssi)) {
558 ret = devm_request_irq(dev, pinfo->pio_irq,
559 &rsnd_ssi_pio_interrupt,
560 IRQF_SHARED,
561 dev_name(dev), ssi);
562 if (ret) {
563 dev_err(dev, "SSI request interrupt failed\n");
564 return ret;
565 }
566
567 ops = &rsnd_ssi_pio_ops;
568 }
569
570 rsnd_mod_init(priv, &ssi->mod, ops, i);
571 }
572
573 rsnd_ssi_mode_init(priv, ssiu);
574
575 dev_dbg(dev, "ssi probed\n");
576
577 return 0;
578}
579
580void rsnd_ssi_remove(struct platform_device *pdev,
581 struct rsnd_priv *priv)
582{
583 struct rsnd_ssi *ssi;
584 int i;
585
586 for_each_rsnd_ssi(ssi, priv, i)
587 clk_put(ssi->clk);
588}