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_secondaries_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;
 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                        int shift = (i * 4) + 20;
 225
 226                        val     = (val & ~(0xF << shift)) |
 227                                rsnd_mod_id(pos) << shift;
 228                }
 229
 230                if (has_hdmi0)
 231                        rsnd_mod_write(mod, HDMI0_SEL, val);
 232                if (has_hdmi1)
 233                        rsnd_mod_write(mod, HDMI1_SEL, val);
 234        }
 235
 236        return 0;
 237}
 238
 239static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
 240                                struct rsnd_dai_stream *io,
 241                                struct rsnd_priv *priv)
 242{
 243        int busif = rsnd_mod_id_sub(mod);
 244
 245        if (!rsnd_ssi_use_busif(io))
 246                return 0;
 247
 248        rsnd_mod_bset(mod, SSI_CTRL, 1 << (busif * 4), 1 << (busif * 4));
 249
 250        if (rsnd_ssi_multi_secondaries_runtime(io))
 251                rsnd_mod_write(mod, SSI_CONTROL, 0x1);
 252
 253        return 0;
 254}
 255
 256static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
 257                               struct rsnd_dai_stream *io,
 258                               struct rsnd_priv *priv)
 259{
 260        struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
 261        int busif = rsnd_mod_id_sub(mod);
 262
 263        if (!rsnd_ssi_use_busif(io))
 264                return 0;
 265
 266        rsnd_mod_bset(mod, SSI_CTRL, 1 << (busif * 4), 0);
 267
 268        if (--ssiu->usrcnt)
 269                return 0;
 270
 271        if (rsnd_ssi_multi_secondaries_runtime(io))
 272                rsnd_mod_write(mod, SSI_CONTROL, 0);
 273
 274        return 0;
 275}
 276
 277static int rsnd_ssiu_id(struct rsnd_mod *mod)
 278{
 279        struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
 280
 281        /* see rsnd_ssiu_probe() */
 282        return ssiu->id;
 283}
 284
 285static int rsnd_ssiu_id_sub(struct rsnd_mod *mod)
 286{
 287        struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
 288
 289        /* see rsnd_ssiu_probe() */
 290        return ssiu->id_sub;
 291}
 292
 293static struct dma_chan *rsnd_ssiu_dma_req(struct rsnd_dai_stream *io,
 294                                          struct rsnd_mod *mod)
 295{
 296        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
 297        int is_play = rsnd_io_is_play(io);
 298        char *name;
 299
 300        /*
 301         * It should use "rcar_sound,ssiu" on DT.
 302         * But, we need to keep compatibility for old version.
 303         *
 304         * If it has "rcar_sound.ssiu", it will be used.
 305         * If not, "rcar_sound.ssi" will be used.
 306         * see
 307         *      rsnd_ssi_dma_req()
 308         *      rsnd_dma_of_path()
 309         */
 310
 311        name = is_play ? "rx" : "tx";
 312
 313        return rsnd_dma_request_channel(rsnd_ssiu_of_node(priv),
 314                                        mod, name);
 315}
 316
 317static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = {
 318        .name           = SSIU_NAME,
 319        .dma_req        = rsnd_ssiu_dma_req,
 320        .init           = rsnd_ssiu_init_gen2,
 321        .start          = rsnd_ssiu_start_gen2,
 322        .stop           = rsnd_ssiu_stop_gen2,
 323        .get_status     = rsnd_ssiu_get_status,
 324};
 325
 326static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id)
 327{
 328        if (WARN_ON(id < 0 || id >= rsnd_ssiu_nr(priv)))
 329                id = 0;
 330
 331        return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id);
 332}
 333
 334static void rsnd_parse_connect_ssiu_compatible(struct rsnd_priv *priv,
 335                                               struct rsnd_dai_stream *io)
 336{
 337        struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
 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                struct rsnd_mod *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 rsnd_dai_stream *io_p = &rdai->playback;
 363        struct rsnd_dai_stream *io_c = &rdai->capture;
 364
 365        /* use rcar_sound,ssiu if exist */
 366        if (node) {
 367                struct device_node *np;
 368                int i = 0;
 369
 370                for_each_child_of_node(node, np) {
 371                        struct rsnd_mod *mod = rsnd_ssiu_mod_get(priv, i);
 372
 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;
 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                int ret;
 445
 446                if (node) {
 447                        int j;
 448
 449                        /*
 450                         * see
 451                         *      rsnd_ssiu_get_id()
 452                         *      rsnd_ssiu_get_id_sub()
 453                         */
 454                        for (j = 0; j < nr; j++) {
 455                                if (list[j] > i)
 456                                        break;
 457                                ssiu->id        = j;
 458                                ssiu->id_sub    = i - list[ssiu->id];
 459                        }
 460                } else {
 461                        ssiu->id = i;
 462                }
 463
 464                ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
 465                                    ops, NULL, RSND_MOD_SSIU, i);
 466                if (ret)
 467                        return ret;
 468        }
 469
 470        return 0;
 471}
 472
 473void rsnd_ssiu_remove(struct rsnd_priv *priv)
 474{
 475        struct rsnd_ssiu *ssiu;
 476        int i;
 477
 478        for_each_rsnd_ssiu(ssiu, priv, i) {
 479                rsnd_mod_quit(rsnd_mod_get(ssiu));
 480        }
 481}
 482