diff options
-rw-r--r-- | include/sound/rcar_snd.h | 23 | ||||
-rw-r--r-- | sound/soc/sh/rcar/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/sh/rcar/core.c | 5 | ||||
-rw-r--r-- | sound/soc/sh/rcar/gen.c | 24 | ||||
-rw-r--r-- | sound/soc/sh/rcar/rsnd.h | 23 | ||||
-rw-r--r-- | sound/soc/sh/rcar/ssi.c | 588 |
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 | |||
39 | struct rsnd_ssi_platform_info { | ||
40 | int pio_irq; | ||
41 | u32 flags; | ||
42 | }; | ||
24 | 43 | ||
25 | struct rsnd_scu_platform_info { | 44 | struct rsnd_scu_platform_info { |
26 | u32 flags; | 45 | u32 flags; |
@@ -43,6 +62,8 @@ struct rsnd_dai_platform_info { | |||
43 | 62 | ||
44 | struct rcar_snd_info { | 63 | struct 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 @@ | |||
1 | snd-soc-rcar-objs := core.o gen.o scu.o adg.o | 1 | snd-soc-rcar-objs := core.o gen.o scu.o adg.o ssi.o |
2 | obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file | 2 | obj-$(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 | ||
125 | static int rsnd_gen1_probe(struct platform_device *pdev, | 137 | static 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 | ||
166 | static void rsnd_gen1_remove(struct platform_device *pdev, | 186 | static 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); |
152 | int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); | 159 | int 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 | ||
155 | void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); | 163 | void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); |
156 | int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); | 164 | int 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, | |||
232 | struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id); | 245 | struct 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 | */ | ||
251 | int rsnd_ssi_probe(struct platform_device *pdev, | ||
252 | struct rcar_snd_info *info, | ||
253 | struct rsnd_priv *priv); | ||
254 | void rsnd_ssi_remove(struct platform_device *pdev, | ||
255 | struct rsnd_priv *priv); | ||
256 | struct 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 | |||
54 | struct 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 | |||
70 | struct 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 | |||
93 | static 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 | |||
149 | static 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 | |||
157 | static 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 | |||
176 | static 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 | |||
222 | static 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 | |||
229 | static 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 | |||
264 | static 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 | */ | ||
310 | static 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 | |||
368 | static 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 | |||
389 | static 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 | */ | ||
403 | static 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 | |||
436 | static 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 | |||
454 | static 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 | |||
471 | static 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 | */ | ||
482 | static 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 | |||
494 | static 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 | */ | ||
505 | struct 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 | |||
512 | int 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 | |||
580 | void 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 | } | ||