linux/sound/soc/sh/rcar/ssiu.c
<<
>>
Prefs
   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
  11struct 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 */
  45static const int gen2_id[] = { 0, 4,  8, 12, 13, 14, 15, 16, 17, 18 };
  46static const int gen3_id[] = { 0, 8, 16, 24, 32, 40, 41, 42, 43, 44 };
  47
  48static 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
  58static 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
 143static 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
 149static 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
 238static 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
 255static 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
 276static 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
 284static 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
 292static 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
 316static 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
 325static 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
 333static 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
 356void 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
 390int 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
 471void 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