Commit | Line | Data |
---|---|---|
07539c1d KM |
1 | /* |
2 | * Renesas R-Car SCU support | |
3 | * | |
4 | * Copyright (C) 2013 Renesas Solutions Corp. | |
5 | * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | */ | |
11 | #include "rsnd.h" | |
12 | ||
13 | struct rsnd_scu { | |
14 | struct rsnd_scu_platform_info *info; /* rcar_snd.h */ | |
15 | struct rsnd_mod mod; | |
16 | }; | |
17 | ||
374a5281 KM |
18 | #define rsnd_scu_mode_flags(p) ((p)->info->flags) |
19 | ||
20 | /* | |
21 | * ADINR | |
22 | */ | |
23 | #define OTBL_24 (0 << 16) | |
24 | #define OTBL_22 (2 << 16) | |
25 | #define OTBL_20 (4 << 16) | |
26 | #define OTBL_18 (6 << 16) | |
27 | #define OTBL_16 (8 << 16) | |
28 | ||
29 | ||
07539c1d KM |
30 | #define rsnd_mod_to_scu(_mod) \ |
31 | container_of((_mod), struct rsnd_scu, mod) | |
32 | ||
33 | #define for_each_rsnd_scu(pos, priv, i) \ | |
34 | for ((i) = 0; \ | |
35 | ((i) < rsnd_scu_nr(priv)) && \ | |
36 | ((pos) = (struct rsnd_scu *)(priv)->scu + i); \ | |
37 | i++) | |
38 | ||
2582718c KM |
39 | /* Gen1 only */ |
40 | static int rsnd_src_set_route_if_gen1(struct rsnd_priv *priv, | |
374a5281 KM |
41 | struct rsnd_mod *mod, |
42 | struct rsnd_dai *rdai, | |
43 | struct rsnd_dai_stream *io) | |
44 | { | |
45 | struct scu_route_config { | |
46 | u32 mask; | |
47 | int shift; | |
48 | } routes[] = { | |
49 | { 0xF, 0, }, /* 0 */ | |
50 | { 0xF, 4, }, /* 1 */ | |
51 | { 0xF, 8, }, /* 2 */ | |
52 | { 0x7, 12, }, /* 3 */ | |
53 | { 0x7, 16, }, /* 4 */ | |
54 | { 0x7, 20, }, /* 5 */ | |
55 | { 0x7, 24, }, /* 6 */ | |
56 | { 0x3, 28, }, /* 7 */ | |
57 | { 0x3, 30, }, /* 8 */ | |
58 | }; | |
59 | ||
60 | u32 mask; | |
61 | u32 val; | |
62 | int shift; | |
63 | int id; | |
64 | ||
65 | /* | |
66 | * Gen1 only | |
67 | */ | |
68 | if (!rsnd_is_gen1(priv)) | |
69 | return 0; | |
70 | ||
71 | id = rsnd_mod_id(mod); | |
72 | if (id < 0 || id > ARRAY_SIZE(routes)) | |
73 | return -EIO; | |
74 | ||
75 | /* | |
76 | * SRC_ROUTE_SELECT | |
77 | */ | |
78 | val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2; | |
79 | val = val << routes[id].shift; | |
80 | mask = routes[id].mask << routes[id].shift; | |
81 | ||
82 | rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val); | |
83 | ||
84 | /* | |
85 | * SRC_TIMING_SELECT | |
86 | */ | |
87 | shift = (id % 4) * 8; | |
88 | mask = 0x1F << shift; | |
89 | if (8 == id) /* SRU8 is very special */ | |
90 | val = id << shift; | |
91 | else | |
92 | val = (id + 1) << shift; | |
93 | ||
94 | switch (id / 4) { | |
95 | case 0: | |
96 | rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); | |
97 | break; | |
98 | case 1: | |
99 | rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); | |
100 | break; | |
101 | case 2: | |
102 | rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); | |
103 | break; | |
104 | } | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
109 | static int rsnd_scu_set_mode(struct rsnd_priv *priv, | |
110 | struct rsnd_mod *mod, | |
111 | struct rsnd_dai *rdai, | |
112 | struct rsnd_dai_stream *io) | |
113 | { | |
114 | int id = rsnd_mod_id(mod); | |
115 | u32 val; | |
116 | ||
117 | if (rsnd_is_gen1(priv)) { | |
118 | val = (1 << id); | |
690ef81e | 119 | rsnd_mod_bset(mod, SRC_ROUTE_CTRL, val, val); |
374a5281 KM |
120 | } |
121 | ||
122 | return 0; | |
123 | } | |
124 | ||
125 | static int rsnd_scu_set_hpbif(struct rsnd_priv *priv, | |
126 | struct rsnd_mod *mod, | |
127 | struct rsnd_dai *rdai, | |
128 | struct rsnd_dai_stream *io) | |
129 | { | |
130 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | |
131 | u32 adinr = runtime->channels; | |
132 | ||
133 | switch (runtime->sample_bits) { | |
134 | case 16: | |
135 | adinr |= OTBL_16; | |
136 | break; | |
137 | case 32: | |
138 | adinr |= OTBL_24; | |
139 | break; | |
140 | default: | |
141 | return -EIO; | |
142 | } | |
143 | ||
144 | rsnd_mod_write(mod, BUSIF_MODE, 1); | |
690ef81e | 145 | rsnd_mod_write(mod, SRC_ADINR, adinr); |
374a5281 KM |
146 | |
147 | return 0; | |
148 | } | |
149 | ||
cdcfcac9 KM |
150 | bool rsnd_scu_hpbif_is_enable(struct rsnd_mod *mod) |
151 | { | |
152 | struct rsnd_scu *scu = rsnd_mod_to_scu(mod); | |
153 | u32 flags = rsnd_scu_mode_flags(scu); | |
154 | ||
155 | return !!(flags & RSND_SCU_USE_HPBIF); | |
156 | } | |
157 | ||
07539c1d KM |
158 | static int rsnd_scu_start(struct rsnd_mod *mod, |
159 | struct rsnd_dai *rdai, | |
160 | struct rsnd_dai_stream *io) | |
161 | { | |
162 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | |
163 | struct device *dev = rsnd_priv_to_dev(priv); | |
374a5281 KM |
164 | int ret; |
165 | ||
166 | /* | |
34e44475 | 167 | * SCU will be used if it has RSND_SCU_USE_HPBIF flags |
374a5281 | 168 | */ |
cdcfcac9 | 169 | if (!rsnd_scu_hpbif_is_enable(mod)) { |
374a5281 KM |
170 | /* it use PIO transter */ |
171 | dev_dbg(dev, "%s%d is not used\n", | |
172 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | |
173 | ||
174 | return 0; | |
175 | } | |
176 | ||
177 | /* it use DMA transter */ | |
2582718c KM |
178 | |
179 | ret = rsnd_src_set_route_if_gen1(priv, mod, rdai, io); | |
374a5281 KM |
180 | if (ret < 0) |
181 | return ret; | |
182 | ||
183 | ret = rsnd_scu_set_mode(priv, mod, rdai, io); | |
184 | if (ret < 0) | |
185 | return ret; | |
07539c1d | 186 | |
374a5281 KM |
187 | ret = rsnd_scu_set_hpbif(priv, mod, rdai, io); |
188 | if (ret < 0) | |
189 | return ret; | |
190 | ||
191 | dev_dbg(dev, "%s%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); | |
07539c1d KM |
192 | |
193 | return 0; | |
194 | } | |
195 | ||
07539c1d KM |
196 | static struct rsnd_mod_ops rsnd_scu_ops = { |
197 | .name = "scu", | |
07539c1d | 198 | .start = rsnd_scu_start, |
07539c1d KM |
199 | }; |
200 | ||
201 | struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id) | |
202 | { | |
203 | BUG_ON(id < 0 || id >= rsnd_scu_nr(priv)); | |
204 | ||
205 | return &((struct rsnd_scu *)(priv->scu) + id)->mod; | |
206 | } | |
207 | ||
208 | int rsnd_scu_probe(struct platform_device *pdev, | |
209 | struct rcar_snd_info *info, | |
210 | struct rsnd_priv *priv) | |
211 | { | |
212 | struct device *dev = rsnd_priv_to_dev(priv); | |
213 | struct rsnd_scu *scu; | |
214 | int i, nr; | |
215 | ||
216 | /* | |
217 | * init SCU | |
218 | */ | |
219 | nr = info->scu_info_nr; | |
220 | scu = devm_kzalloc(dev, sizeof(*scu) * nr, GFP_KERNEL); | |
221 | if (!scu) { | |
222 | dev_err(dev, "SCU allocate failed\n"); | |
223 | return -ENOMEM; | |
224 | } | |
225 | ||
226 | priv->scu_nr = nr; | |
227 | priv->scu = scu; | |
228 | ||
229 | for_each_rsnd_scu(scu, priv, i) { | |
230 | rsnd_mod_init(priv, &scu->mod, | |
231 | &rsnd_scu_ops, i); | |
232 | scu->info = &info->scu_info[i]; | |
07539c1d | 233 | |
374a5281 KM |
234 | dev_dbg(dev, "SCU%d probed\n", i); |
235 | } | |
07539c1d KM |
236 | dev_dbg(dev, "scu probed\n"); |
237 | ||
238 | return 0; | |
239 | } | |
240 | ||
241 | void rsnd_scu_remove(struct platform_device *pdev, | |
242 | struct rsnd_priv *priv) | |
243 | { | |
244 | } |