linux/sound/soc/sh/rcar/cmd.c
<<
>>
Prefs
   1/*
   2 * Renesas R-Car CMD support
   3 *
   4 * Copyright (C) 2015 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
  13struct rsnd_cmd {
  14        struct rsnd_mod mod;
  15};
  16
  17#define CMD_NAME "cmd"
  18
  19#define rsnd_cmd_nr(priv) ((priv)->cmd_nr)
  20#define for_each_rsnd_cmd(pos, priv, i)                                 \
  21        for ((i) = 0;                                                   \
  22             ((i) < rsnd_cmd_nr(priv)) &&                               \
  23                     ((pos) = (struct rsnd_cmd *)(priv)->cmd + i);      \
  24             i++)
  25
  26static int rsnd_cmd_init(struct rsnd_mod *mod,
  27                         struct rsnd_dai_stream *io,
  28                         struct rsnd_priv *priv)
  29{
  30        struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
  31        struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
  32        struct device *dev = rsnd_priv_to_dev(priv);
  33        u32 data;
  34        static const u32 path[] = {
  35                [1] = 1 << 0,
  36                [5] = 1 << 8,
  37                [6] = 1 << 12,
  38                [9] = 1 << 15,
  39        };
  40
  41        if (!mix && !dvc)
  42                return 0;
  43
  44        if (ARRAY_SIZE(path) < rsnd_mod_id(mod) + 1)
  45                return -ENXIO;
  46
  47        if (mix) {
  48                struct rsnd_dai *rdai;
  49                struct rsnd_mod *src;
  50                struct rsnd_dai_stream *tio;
  51                int i;
  52
  53                /*
  54                 * it is assuming that integrater is well understanding about
  55                 * data path. Here doesn't check impossible connection,
  56                 * like src2 + src5
  57                 */
  58                data = 0;
  59                for_each_rsnd_dai(rdai, priv, i) {
  60                        tio = &rdai->playback;
  61                        src = rsnd_io_to_mod_src(tio);
  62                        if (mix == rsnd_io_to_mod_mix(tio))
  63                                data |= path[rsnd_mod_id(src)];
  64
  65                        tio = &rdai->capture;
  66                        src = rsnd_io_to_mod_src(tio);
  67                        if (mix == rsnd_io_to_mod_mix(tio))
  68                                data |= path[rsnd_mod_id(src)];
  69                }
  70
  71        } else {
  72                struct rsnd_mod *src = rsnd_io_to_mod_src(io);
  73
  74                static const u8 cmd_case[] = {
  75                        [0] = 0x3,
  76                        [1] = 0x3,
  77                        [2] = 0x4,
  78                        [3] = 0x1,
  79                        [4] = 0x2,
  80                        [5] = 0x4,
  81                        [6] = 0x1,
  82                        [9] = 0x2,
  83                };
  84
  85                if (unlikely(!src))
  86                        return -EIO;
  87
  88                data = path[rsnd_mod_id(src)] |
  89                        cmd_case[rsnd_mod_id(src)] << 16;
  90        }
  91
  92        dev_dbg(dev, "ctu/mix path = 0x%08x", data);
  93
  94        rsnd_mod_write(mod, CMD_ROUTE_SLCT, data);
  95        rsnd_mod_write(mod, CMD_BUSIF_MODE, rsnd_get_busif_shift(io, mod) | 1);
  96        rsnd_mod_write(mod, CMD_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
  97
  98        rsnd_adg_set_cmd_timsel_gen2(mod, io);
  99
 100        return 0;
 101}
 102
 103static int rsnd_cmd_start(struct rsnd_mod *mod,
 104                          struct rsnd_dai_stream *io,
 105                          struct rsnd_priv *priv)
 106{
 107        rsnd_mod_write(mod, CMD_CTRL, 0x10);
 108
 109        return 0;
 110}
 111
 112static int rsnd_cmd_stop(struct rsnd_mod *mod,
 113                         struct rsnd_dai_stream *io,
 114                         struct rsnd_priv *priv)
 115{
 116        rsnd_mod_write(mod, CMD_CTRL, 0);
 117
 118        return 0;
 119}
 120
 121static struct rsnd_mod_ops rsnd_cmd_ops = {
 122        .name   = CMD_NAME,
 123        .init   = rsnd_cmd_init,
 124        .start  = rsnd_cmd_start,
 125        .stop   = rsnd_cmd_stop,
 126};
 127
 128int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id)
 129{
 130        struct rsnd_priv *priv = rsnd_io_to_priv(io);
 131        struct rsnd_mod *mod = rsnd_cmd_mod_get(priv, id);
 132
 133        return rsnd_dai_connect(mod, io, mod->type);
 134}
 135
 136struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id)
 137{
 138        if (WARN_ON(id < 0 || id >= rsnd_cmd_nr(priv)))
 139                id = 0;
 140
 141        return rsnd_mod_get((struct rsnd_cmd *)(priv->cmd) + id);
 142}
 143
 144int rsnd_cmd_probe(struct rsnd_priv *priv)
 145{
 146        struct device *dev = rsnd_priv_to_dev(priv);
 147        struct rsnd_cmd *cmd;
 148        int i, nr, ret;
 149
 150        /* This driver doesn't support Gen1 at this point */
 151        if (rsnd_is_gen1(priv))
 152                return 0;
 153
 154        /* same number as DVC */
 155        nr = priv->dvc_nr;
 156        if (!nr)
 157                return 0;
 158
 159        cmd = devm_kzalloc(dev, sizeof(*cmd) * nr, GFP_KERNEL);
 160        if (!cmd)
 161                return -ENOMEM;
 162
 163        priv->cmd_nr    = nr;
 164        priv->cmd       = cmd;
 165
 166        for_each_rsnd_cmd(cmd, priv, i) {
 167                ret = rsnd_mod_init(priv, rsnd_mod_get(cmd),
 168                                    &rsnd_cmd_ops, NULL,
 169                                    rsnd_mod_get_status, RSND_MOD_CMD, i);
 170                if (ret)
 171                        return ret;
 172        }
 173
 174        return 0;
 175}
 176
 177void rsnd_cmd_remove(struct rsnd_priv *priv)
 178{
 179        struct rsnd_cmd *cmd;
 180        int i;
 181
 182        for_each_rsnd_cmd(cmd, priv, i) {
 183                rsnd_mod_quit(rsnd_mod_get(cmd));
 184        }
 185}
 186