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