mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
3.7 KiB
182 lines
3.7 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
// |
|
// Renesas R-Car CMD support |
|
// |
|
// Copyright (C) 2015 Renesas Solutions Corp. |
|
// Kuninori Morimoto <[email protected]> |
|
|
|
#include "rsnd.h" |
|
|
|
struct rsnd_cmd { |
|
struct rsnd_mod mod; |
|
}; |
|
|
|
#define CMD_NAME "cmd" |
|
|
|
#define rsnd_cmd_nr(priv) ((priv)->cmd_nr) |
|
#define for_each_rsnd_cmd(pos, priv, i) \ |
|
for ((i) = 0; \ |
|
((i) < rsnd_cmd_nr(priv)) && \ |
|
((pos) = (struct rsnd_cmd *)(priv)->cmd + i); \ |
|
i++) |
|
|
|
static int rsnd_cmd_init(struct rsnd_mod *mod, |
|
struct rsnd_dai_stream *io, |
|
struct rsnd_priv *priv) |
|
{ |
|
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); |
|
struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); |
|
struct device *dev = rsnd_priv_to_dev(priv); |
|
u32 data; |
|
static const u32 path[] = { |
|
[1] = 1 << 0, |
|
[5] = 1 << 8, |
|
[6] = 1 << 12, |
|
[9] = 1 << 15, |
|
}; |
|
|
|
if (!mix && !dvc) |
|
return 0; |
|
|
|
if (ARRAY_SIZE(path) < rsnd_mod_id(mod) + 1) |
|
return -ENXIO; |
|
|
|
if (mix) { |
|
struct rsnd_dai *rdai; |
|
struct rsnd_mod *src; |
|
struct rsnd_dai_stream *tio; |
|
int i; |
|
|
|
/* |
|
* it is assuming that integrater is well understanding about |
|
* data path. Here doesn't check impossible connection, |
|
* like src2 + src5 |
|
*/ |
|
data = 0; |
|
for_each_rsnd_dai(rdai, priv, i) { |
|
tio = &rdai->playback; |
|
src = rsnd_io_to_mod_src(tio); |
|
if (mix == rsnd_io_to_mod_mix(tio)) |
|
data |= path[rsnd_mod_id(src)]; |
|
|
|
tio = &rdai->capture; |
|
src = rsnd_io_to_mod_src(tio); |
|
if (mix == rsnd_io_to_mod_mix(tio)) |
|
data |= path[rsnd_mod_id(src)]; |
|
} |
|
|
|
} else { |
|
struct rsnd_mod *src = rsnd_io_to_mod_src(io); |
|
|
|
static const u8 cmd_case[] = { |
|
[0] = 0x3, |
|
[1] = 0x3, |
|
[2] = 0x4, |
|
[3] = 0x1, |
|
[4] = 0x2, |
|
[5] = 0x4, |
|
[6] = 0x1, |
|
[9] = 0x2, |
|
}; |
|
|
|
if (unlikely(!src)) |
|
return -EIO; |
|
|
|
data = path[rsnd_mod_id(src)] | |
|
cmd_case[rsnd_mod_id(src)] << 16; |
|
} |
|
|
|
dev_dbg(dev, "ctu/mix path = 0x%08x\n", data); |
|
|
|
rsnd_mod_write(mod, CMD_ROUTE_SLCT, data); |
|
rsnd_mod_write(mod, CMD_BUSIF_MODE, rsnd_get_busif_shift(io, mod) | 1); |
|
rsnd_mod_write(mod, CMD_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); |
|
|
|
rsnd_adg_set_cmd_timsel_gen2(mod, io); |
|
|
|
return 0; |
|
} |
|
|
|
static int rsnd_cmd_start(struct rsnd_mod *mod, |
|
struct rsnd_dai_stream *io, |
|
struct rsnd_priv *priv) |
|
{ |
|
rsnd_mod_write(mod, CMD_CTRL, 0x10); |
|
|
|
return 0; |
|
} |
|
|
|
static int rsnd_cmd_stop(struct rsnd_mod *mod, |
|
struct rsnd_dai_stream *io, |
|
struct rsnd_priv *priv) |
|
{ |
|
rsnd_mod_write(mod, CMD_CTRL, 0); |
|
|
|
return 0; |
|
} |
|
|
|
static struct rsnd_mod_ops rsnd_cmd_ops = { |
|
.name = CMD_NAME, |
|
.init = rsnd_cmd_init, |
|
.start = rsnd_cmd_start, |
|
.stop = rsnd_cmd_stop, |
|
.get_status = rsnd_mod_get_status, |
|
}; |
|
|
|
static struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id) |
|
{ |
|
if (WARN_ON(id < 0 || id >= rsnd_cmd_nr(priv))) |
|
id = 0; |
|
|
|
return rsnd_mod_get((struct rsnd_cmd *)(priv->cmd) + id); |
|
} |
|
int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id) |
|
{ |
|
struct rsnd_priv *priv = rsnd_io_to_priv(io); |
|
struct rsnd_mod *mod = rsnd_cmd_mod_get(priv, id); |
|
|
|
return rsnd_dai_connect(mod, io, mod->type); |
|
} |
|
|
|
int rsnd_cmd_probe(struct rsnd_priv *priv) |
|
{ |
|
struct device *dev = rsnd_priv_to_dev(priv); |
|
struct rsnd_cmd *cmd; |
|
int i, nr, ret; |
|
|
|
/* This driver doesn't support Gen1 at this point */ |
|
if (rsnd_is_gen1(priv)) |
|
return 0; |
|
|
|
/* same number as DVC */ |
|
nr = priv->dvc_nr; |
|
if (!nr) |
|
return 0; |
|
|
|
cmd = devm_kcalloc(dev, nr, sizeof(*cmd), GFP_KERNEL); |
|
if (!cmd) |
|
return -ENOMEM; |
|
|
|
priv->cmd_nr = nr; |
|
priv->cmd = cmd; |
|
|
|
for_each_rsnd_cmd(cmd, priv, i) { |
|
ret = rsnd_mod_init(priv, rsnd_mod_get(cmd), |
|
&rsnd_cmd_ops, NULL, |
|
RSND_MOD_CMD, i); |
|
if (ret) |
|
return ret; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
void rsnd_cmd_remove(struct rsnd_priv *priv) |
|
{ |
|
struct rsnd_cmd *cmd; |
|
int i; |
|
|
|
for_each_rsnd_cmd(cmd, priv, i) { |
|
rsnd_mod_quit(rsnd_mod_get(cmd)); |
|
} |
|
}
|
|
|