~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/sh/rcar/ssiu.c

Version: ~ [ linux-5.10-rc5 ] ~ [ linux-5.9.10 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.79 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.159 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.208 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.245 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.245 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 //
  3 // Renesas R-Car SSIU support
  4 //
  5 // Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
  6 
  7 #include "rsnd.h"
  8 
  9 #define SSIU_NAME "ssiu"
 10 
 11 struct rsnd_ssiu {
 12         struct rsnd_mod mod;
 13         u32 busif_status[8]; /* for BUSIF0 - BUSIF7 */
 14         unsigned int usrcnt;
 15         int id;
 16         int id_sub;
 17 };
 18 
 19 /* SSI_MODE */
 20 #define TDM_EXT         (1 << 0)
 21 #define TDM_SPLIT       (1 << 8)
 22 
 23 #define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr)
 24 #define rsnd_mod_to_ssiu(_mod) container_of((_mod), struct rsnd_ssiu, mod)
 25 #define for_each_rsnd_ssiu(pos, priv, i)                                \
 26         for (i = 0;                                                     \
 27              (i < rsnd_ssiu_nr(priv)) &&                                \
 28                      ((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i));  \
 29              i++)
 30 
 31 /*
 32  *      SSI     Gen2            Gen3
 33  *      0       BUSIF0-3        BUSIF0-7
 34  *      1       BUSIF0-3        BUSIF0-7
 35  *      2       BUSIF0-3        BUSIF0-7
 36  *      3       BUSIF0          BUSIF0-7
 37  *      4       BUSIF0          BUSIF0-7
 38  *      5       BUSIF0          BUSIF0
 39  *      6       BUSIF0          BUSIF0
 40  *      7       BUSIF0          BUSIF0
 41  *      8       BUSIF0          BUSIF0
 42  *      9       BUSIF0-3        BUSIF0-7
 43  *      total   22              52
 44  */
 45 static const int gen2_id[] = { 0, 4,  8, 12, 13, 14, 15, 16, 17, 18 };
 46 static const int gen3_id[] = { 0, 8, 16, 24, 32, 40, 41, 42, 43, 44 };
 47 
 48 static u32 *rsnd_ssiu_get_status(struct rsnd_mod *mod,
 49                                  struct rsnd_dai_stream *io,
 50                                  enum rsnd_mod_type type)
 51 {
 52         struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
 53         int busif = rsnd_mod_id_sub(mod);
 54 
 55         return &ssiu->busif_status[busif];
 56 }
 57 
 58 static int rsnd_ssiu_init(struct rsnd_mod *mod,
 59                           struct rsnd_dai_stream *io,
 60                           struct rsnd_priv *priv)
 61 {
 62         struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
 63         u32 ssis = rsnd_ssi_multi_slaves_runtime(io);
 64         int use_busif = rsnd_ssi_use_busif(io);
 65         int id = rsnd_mod_id(mod);
 66         int is_clk_master = rsnd_rdai_is_clk_master(rdai);
 67         u32 val1, val2;
 68         int i;
 69 
 70         /* clear status */
 71         switch (id) {
 72         case 0:
 73         case 1:
 74         case 2:
 75         case 3:
 76         case 4:
 77                 for (i = 0; i < 4; i++)
 78                         rsnd_mod_write(mod, SSI_SYS_STATUS(i * 2), 0xf << (id * 4));
 79                 break;
 80         case 9:
 81                 for (i = 0; i < 4; i++)
 82                         rsnd_mod_write(mod, SSI_SYS_STATUS((i * 2) + 1), 0xf << 4);
 83                 break;
 84         }
 85 
 86         /*
 87          * SSI_MODE0
 88          */
 89         rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id);
 90 
 91         /*
 92          * SSI_MODE1 / SSI_MODE2
 93          *
 94          * FIXME
 95          * sharing/multi with SSI0 are mainly supported
 96          */
 97         val1 = rsnd_mod_read(mod, SSI_MODE1);
 98         val2 = rsnd_mod_read(mod, SSI_MODE2);
 99         if (rsnd_ssi_is_pin_sharing(io)) {
100 
101                 ssis |= (1 << id);
102 
103         } else if (ssis) {
104                 /*
105                  * Multi SSI
106                  *
107                  * set synchronized bit here
108                  */
109 
110                 /* SSI4 is synchronized with SSI3 */
111                 if (ssis & (1 << 4))
112                         val1 |= (1 << 20);
113                 /* SSI012 are synchronized */
114                 if (ssis == 0x0006)
115                         val1 |= (1 << 4);
116                 /* SSI0129 are synchronized */
117                 if (ssis == 0x0206)
118                         val2 |= (1 << 4);
119         }
120 
121         /* SSI1 is sharing pin with SSI0 */
122         if (ssis & (1 << 1))
123                 val1 |= is_clk_master ? 0x2 : 0x1;
124 
125         /* SSI2 is sharing pin with SSI0 */
126         if (ssis & (1 << 2))
127                 val1 |= is_clk_master ? 0x2 << 2 :
128                                         0x1 << 2;
129         /* SSI4 is sharing pin with SSI3 */
130         if (ssis & (1 << 4))
131                 val1 |= is_clk_master ? 0x2 << 16 :
132                                         0x1 << 16;
133         /* SSI9 is sharing pin with SSI0 */
134         if (ssis & (1 << 9))
135                 val2 |= is_clk_master ? 0x2 : 0x1;
136 
137         rsnd_mod_bset(mod, SSI_MODE1, 0x0013001f, val1);
138         rsnd_mod_bset(mod, SSI_MODE2, 0x00000017, val2);
139 
140         return 0;
141 }
142 
143 static struct rsnd_mod_ops rsnd_ssiu_ops_gen1 = {
144         .name           = SSIU_NAME,
145         .init           = rsnd_ssiu_init,
146         .get_status     = rsnd_ssiu_get_status,
147 };
148 
149 static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
150                                struct rsnd_dai_stream *io,
151                                struct rsnd_priv *priv)
152 {
153         struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
154         u32 has_hdmi0 = rsnd_flags_has(io, RSND_STREAM_HDMI0);
155         u32 has_hdmi1 = rsnd_flags_has(io, RSND_STREAM_HDMI1);
156         int ret;
157         u32 mode = 0;
158 
159         ret = rsnd_ssiu_init(mod, io, priv);
160         if (ret < 0)
161                 return ret;
162 
163         ssiu->usrcnt++;
164 
165         /*
166          * TDM Extend/Split Mode
167          * see
168          *      rsnd_ssi_config_init()
169          */
170         if (rsnd_runtime_is_tdm(io))
171                 mode = TDM_EXT;
172         else if (rsnd_runtime_is_tdm_split(io))
173                 mode = TDM_SPLIT;
174 
175         rsnd_mod_write(mod, SSI_MODE, mode);
176 
177         if (rsnd_ssi_use_busif(io)) {
178                 int id = rsnd_mod_id(mod);
179                 int busif = rsnd_mod_id_sub(mod);
180                 enum rsnd_reg adinr_reg, mode_reg, dalign_reg;
181 
182                 if ((id == 9) && (busif >= 4)) {
183                         adinr_reg = SSI9_BUSIF_ADINR(busif);
184                         mode_reg = SSI9_BUSIF_MODE(busif);
185                         dalign_reg = SSI9_BUSIF_DALIGN(busif);
186                 } else {
187                         adinr_reg = SSI_BUSIF_ADINR(busif);
188                         mode_reg = SSI_BUSIF_MODE(busif);
189                         dalign_reg = SSI_BUSIF_DALIGN(busif);
190                 }
191 
192                 rsnd_mod_write(mod, adinr_reg,
193                                rsnd_get_adinr_bit(mod, io) |
194                                (rsnd_io_is_play(io) ?
195                                 rsnd_runtime_channel_after_ctu(io) :
196                                 rsnd_runtime_channel_original(io)));
197                 rsnd_mod_write(mod, mode_reg,
198                                rsnd_get_busif_shift(io, mod) | 1);
199                 rsnd_mod_write(mod, dalign_reg,
200                                rsnd_get_dalign(mod, io));
201         }
202 
203         if (has_hdmi0 || has_hdmi1) {
204                 enum rsnd_mod_type rsnd_ssi_array[] = {
205                         RSND_MOD_SSIM1,
206                         RSND_MOD_SSIM2,
207                         RSND_MOD_SSIM3,
208                 };
209                 struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
210                 struct rsnd_mod *pos;
211                 u32 val;
212                 int i, shift;
213 
214                 i = rsnd_mod_id(ssi_mod);
215 
216                 /* output all same SSI as default */
217                 val =   i << 16 |
218                         i << 20 |
219                         i << 24 |
220                         i << 28 |
221                         i;
222 
223                 for_each_rsnd_mod_array(i, pos, io, rsnd_ssi_array) {
224                         shift   = (i * 4) + 16;
225                         val     = (val & ~(0xF << shift)) |
226                                 rsnd_mod_id(pos) << shift;
227                 }
228 
229                 if (has_hdmi0)
230                         rsnd_mod_write(mod, HDMI0_SEL, val);
231                 if (has_hdmi1)
232                         rsnd_mod_write(mod, HDMI1_SEL, val);
233         }
234 
235         return 0;
236 }
237 
238 static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
239                                 struct rsnd_dai_stream *io,
240                                 struct rsnd_priv *priv)
241 {
242         int busif = rsnd_mod_id_sub(mod);
243 
244         if (!rsnd_ssi_use_busif(io))
245                 return 0;
246 
247         rsnd_mod_bset(mod, SSI_CTRL, 1 << (busif * 4), 1 << (busif * 4));
248 
249         if (rsnd_ssi_multi_slaves_runtime(io))
250                 rsnd_mod_write(mod, SSI_CONTROL, 0x1);
251 
252         return 0;
253 }
254 
255 static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
256                                struct rsnd_dai_stream *io,
257                                struct rsnd_priv *priv)
258 {
259         struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
260         int busif = rsnd_mod_id_sub(mod);
261 
262         if (!rsnd_ssi_use_busif(io))
263                 return 0;
264 
265         rsnd_mod_bset(mod, SSI_CTRL, 1 << (busif * 4), 0);
266 
267         if (--ssiu->usrcnt)
268                 return 0;
269 
270         if (rsnd_ssi_multi_slaves_runtime(io))
271                 rsnd_mod_write(mod, SSI_CONTROL, 0);
272 
273         return 0;
274 }
275 
276 static int rsnd_ssiu_id(struct rsnd_mod *mod)
277 {
278         struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
279 
280         /* see rsnd_ssiu_probe() */
281         return ssiu->id;
282 }
283 
284 static int rsnd_ssiu_id_sub(struct rsnd_mod *mod)
285 {
286         struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
287 
288         /* see rsnd_ssiu_probe() */
289         return ssiu->id_sub;
290 }
291 
292 static struct dma_chan *rsnd_ssiu_dma_req(struct rsnd_dai_stream *io,
293                                           struct rsnd_mod *mod)
294 {
295         struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
296         int is_play = rsnd_io_is_play(io);
297         char *name;
298 
299         /*
300          * It should use "rcar_sound,ssiu" on DT.
301          * But, we need to keep compatibility for old version.
302          *
303          * If it has "rcar_sound.ssiu", it will be used.
304          * If not, "rcar_sound.ssi" will be used.
305          * see
306          *      rsnd_ssi_dma_req()
307          *      rsnd_dma_of_path()
308          */
309 
310         name = is_play ? "rx" : "tx";
311 
312         return rsnd_dma_request_channel(rsnd_ssiu_of_node(priv),
313                                         mod, name);
314 }
315 
316 static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = {
317         .name           = SSIU_NAME,
318         .dma_req        = rsnd_ssiu_dma_req,
319         .init           = rsnd_ssiu_init_gen2,
320         .start          = rsnd_ssiu_start_gen2,
321         .stop           = rsnd_ssiu_stop_gen2,
322         .get_status     = rsnd_ssiu_get_status,
323 };
324 
325 static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id)
326 {
327         if (WARN_ON(id < 0 || id >= rsnd_ssiu_nr(priv)))
328                 id = 0;
329 
330         return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id);
331 }
332 
333 static void rsnd_parse_connect_ssiu_compatible(struct rsnd_priv *priv,
334                                                struct rsnd_dai_stream *io)
335 {
336         struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
337         struct rsnd_mod *mod;
338         struct rsnd_ssiu *ssiu;
339         int i;
340 
341         if (!ssi_mod)
342                 return;
343 
344         /* select BUSIF0 */
345         for_each_rsnd_ssiu(ssiu, priv, i) {
346                 mod = rsnd_mod_get(ssiu);
347 
348                 if ((rsnd_mod_id(ssi_mod) == rsnd_mod_id(mod)) &&
349                     (rsnd_mod_id_sub(mod) == 0)) {
350                         rsnd_dai_connect(mod, io, mod->type);
351                         return;
352                 }
353         }
354 }
355 
356 void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai,
357                              struct device_node *playback,
358                              struct device_node *capture)
359 {
360         struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
361         struct device_node *node = rsnd_ssiu_of_node(priv);
362         struct device_node *np;
363         struct rsnd_mod *mod;
364         struct rsnd_dai_stream *io_p = &rdai->playback;
365         struct rsnd_dai_stream *io_c = &rdai->capture;
366         int i;
367 
368         /* use rcar_sound,ssiu if exist */
369         if (node) {
370                 i = 0;
371                 for_each_child_of_node(node, np) {
372                         mod = rsnd_ssiu_mod_get(priv, i);
373                         if (np == playback)
374                                 rsnd_dai_connect(mod, io_p, mod->type);
375                         if (np == capture)
376                                 rsnd_dai_connect(mod, io_c, mod->type);
377                         i++;
378                 }
379 
380                 of_node_put(node);
381         }
382 
383         /* Keep DT compatibility */
384         if (!rsnd_io_to_mod_ssiu(io_p))
385                 rsnd_parse_connect_ssiu_compatible(priv, io_p);
386         if (!rsnd_io_to_mod_ssiu(io_c))
387                 rsnd_parse_connect_ssiu_compatible(priv, io_c);
388 }
389 
390 int rsnd_ssiu_probe(struct rsnd_priv *priv)
391 {
392         struct device *dev = rsnd_priv_to_dev(priv);
393         struct device_node *node;
394         struct rsnd_ssiu *ssiu;
395         struct rsnd_mod_ops *ops;
396         const int *list = NULL;
397         int i, nr, ret;
398 
399         /*
400          * Keep DT compatibility.
401          * if it has "rcar_sound,ssiu", use it.
402          * if not, use "rcar_sound,ssi"
403          * see
404          *      rsnd_ssiu_bufsif_to_id()
405          */
406         node = rsnd_ssiu_of_node(priv);
407         if (node)
408                 nr = of_get_child_count(node);
409         else
410                 nr = priv->ssi_nr;
411 
412         ssiu    = devm_kcalloc(dev, nr, sizeof(*ssiu), GFP_KERNEL);
413         if (!ssiu)
414                 return -ENOMEM;
415 
416         priv->ssiu      = ssiu;
417         priv->ssiu_nr   = nr;
418 
419         if (rsnd_is_gen1(priv))
420                 ops = &rsnd_ssiu_ops_gen1;
421         else
422                 ops = &rsnd_ssiu_ops_gen2;
423 
424         /* Keep compatibility */
425         nr = 0;
426         if ((node) &&
427             (ops == &rsnd_ssiu_ops_gen2)) {
428                 ops->id         = rsnd_ssiu_id;
429                 ops->id_sub     = rsnd_ssiu_id_sub;
430 
431                 if (rsnd_is_gen2(priv)) {
432                         list    = gen2_id;
433                         nr      = ARRAY_SIZE(gen2_id);
434                 } else if (rsnd_is_gen3(priv)) {
435                         list    = gen3_id;
436                         nr      = ARRAY_SIZE(gen3_id);
437                 } else {
438                         dev_err(dev, "unknown SSIU\n");
439                         return -ENODEV;
440                 }
441         }
442 
443         for_each_rsnd_ssiu(ssiu, priv, i) {
444                 if (node) {
445                         int j;
446 
447                         /*
448                          * see
449                          *      rsnd_ssiu_get_id()
450                          *      rsnd_ssiu_get_id_sub()
451                          */
452                         for (j = 0; j < nr; j++) {
453                                 if (list[j] > i)
454                                         break;
455                                 ssiu->id        = j;
456                                 ssiu->id_sub    = i - list[ssiu->id];
457                         }
458                 } else {
459                         ssiu->id = i;
460                 }
461 
462                 ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
463                                     ops, NULL, RSND_MOD_SSIU, i);
464                 if (ret)
465                         return ret;
466         }
467 
468         return 0;
469 }
470 
471 void rsnd_ssiu_remove(struct rsnd_priv *priv)
472 {
473         struct rsnd_ssiu *ssiu;
474         int i;
475 
476         for_each_rsnd_ssiu(ssiu, priv, i) {
477                 rsnd_mod_quit(rsnd_mod_get(ssiu));
478         }
479 }
480 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp