@@ -1093,6 +1093,7 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv,
break;
rsnd_parse_connect_ssi(rdai, playback, capture);
+ rsnd_parse_connect_ssiu(rdai, playback, capture);
rsnd_parse_connect_src(rdai, playback, capture);
rsnd_parse_connect_ctu(rdai, playback, capture);
rsnd_parse_connect_mix(rdai, playback, capture);
@@ -219,7 +219,7 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
int i = 0;
for_each_child_of_node(of_node, np) {
- if (i == rsnd_mod_id(mod) && (!chan))
+ if (i == rsnd_mod_id_raw(mod) && (!chan))
chan = of_dma_request_slave_channel(np, name);
i++;
}
@@ -345,14 +345,16 @@ static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
+ struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io);
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
const u8 *entry = NULL;
int id = 255;
int size = 0;
- if (mod == ssi) {
- int busif = rsnd_ssi_get_busif(io);
+ if ((mod == ssi) ||
+ (mod == ssiu)) {
+ int busif = rsnd_mod_id_sub(ssiu);
entry = gen2_id_table_ssiu;
size = ARRAY_SIZE(gen2_id_table_ssiu);
@@ -532,13 +534,14 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io,
struct device *dev = rsnd_priv_to_dev(priv);
phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI);
phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU);
- int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod);
+ int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod) ||
+ !!(rsnd_io_to_mod_ssiu(io) == mod);
int use_src = !!rsnd_io_to_mod_src(io);
int use_cmd = !!rsnd_io_to_mod_dvc(io) ||
!!rsnd_io_to_mod_mix(io) ||
!!rsnd_io_to_mod_ctu(io);
int id = rsnd_mod_id(mod);
- int busif = rsnd_ssi_get_busif(io);
+ int busif = rsnd_mod_id_sub(rsnd_io_to_mod_ssiu(io));
struct dma_addr {
dma_addr_t out_addr;
dma_addr_t in_addr;
@@ -622,7 +625,7 @@ static void rsnd_dma_of_path(struct rsnd_mod *this,
struct rsnd_mod **mod_from,
struct rsnd_mod **mod_to)
{
- struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
+ struct rsnd_mod *ssi;
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io);
struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
@@ -633,6 +636,28 @@ static void rsnd_dma_of_path(struct rsnd_mod *this,
struct device *dev = rsnd_priv_to_dev(priv);
int nr, i, idx;
+ /*
+ * It should use "rcar_sound,ssiu" on DT.
+ * But, we need to keep compatibility for old version.
+ *
+ * If it has "rcar_sound.ssiu", it will be used.
+ * If not, "rcar_sound.ssi" will be used.
+ * see
+ * rsnd_ssiu_dma_req()
+ * rsnd_ssi_dma_req()
+ */
+ if (rsnd_ssiu_of_node(priv)) {
+ struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io);
+
+ /* use SSIU */
+ ssi = ssiu;
+ if (this == rsnd_io_to_mod_ssi(io))
+ this = ssiu;
+ } else {
+ /* keep compatible, use SSI */
+ ssi = rsnd_io_to_mod_ssi(io);
+ }
+
if (!ssi)
return;
@@ -441,6 +441,7 @@ int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io);
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, node)
#define RSND_NODE_DAI "rcar_sound,dai"
#define RSND_NODE_SSI "rcar_sound,ssi"
+#define RSND_NODE_SSIU "rcar_sound,ssiu"
#define RSND_NODE_SRC "rcar_sound,src"
#define RSND_NODE_CTU "rcar_sound,ctu"
#define RSND_NODE_MIX "rcar_sound,mix"
@@ -715,7 +716,6 @@ void rsnd_ssi_remove(struct rsnd_priv *priv);
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
-int rsnd_ssi_get_busif(struct rsnd_dai_stream *io);
u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io);
#define RSND_SSI_HDMI_PORT0 0xf0
@@ -743,6 +743,10 @@ int rsnd_ssiu_attach(struct rsnd_dai_stream *io,
struct rsnd_mod *mod);
int rsnd_ssiu_probe(struct rsnd_priv *priv);
void rsnd_ssiu_remove(struct rsnd_priv *priv);
+void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai,
+ struct device_node *playback,
+ struct device_node *capture);
+#define rsnd_ssiu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SSIU)
/*
* R-Car SRC
@@ -150,11 +150,6 @@ int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
return use_busif;
}
-int rsnd_ssi_get_busif(struct rsnd_dai_stream *io)
-{
- return 0; /* BUSIF0 only for now */
-}
-
static void rsnd_ssi_status_clear(struct rsnd_mod *mod)
{
rsnd_mod_write(mod, SSISR, 0);
@@ -764,7 +759,7 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
{
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- int ret;
+ int ret = 0;
/*
* SSIP/SSIU/IRQ are not needed on
@@ -778,10 +773,6 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
* see rsnd_ssi_pcm_new()
*/
- ret = rsnd_ssiu_attach(io, mod);
- if (ret < 0)
- return ret;
-
/*
* SSI might be called again as PIO fallback
* It is easy to manual handling for IRQ request/free
@@ -976,6 +967,17 @@ static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io,
int is_play = rsnd_io_is_play(io);
char *name;
+ /*
+ * It should use "rcar_sound,ssiu" on DT.
+ * But, we need to keep compatibility for old version.
+ *
+ * If it has "rcar_sound.ssiu", it will be used.
+ * If not, "rcar_sound.ssi" will be used.
+ * see
+ * rsnd_ssiu_dma_req()
+ * rsnd_dma_of_path()
+ */
+
if (rsnd_ssi_use_busif(io))
name = is_play ? "rxu" : "txu";
else
@@ -12,6 +12,8 @@ struct rsnd_ssiu {
struct rsnd_mod mod;
u32 busif_status[8]; /* for BUSIF0 - BUSIF7 */
unsigned int usrcnt;
+ int id;
+ int id_sub;
};
#define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr)
@@ -22,12 +24,29 @@ struct rsnd_ssiu {
((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i)); \
i++)
+/*
+ * SSI Gen2 Gen3
+ * 0 BUSIF0-3 BUSIF0-7
+ * 1 BUSIF0-3 BUSIF0-7
+ * 2 BUSIF0-3 BUSIF0-7
+ * 3 BUSIF0 BUSIF0-7
+ * 4 BUSIF0 BUSIF0-7
+ * 5 BUSIF0 BUSIF0
+ * 6 BUSIF0 BUSIF0
+ * 7 BUSIF0 BUSIF0
+ * 8 BUSIF0 BUSIF0
+ * 9 BUSIF0-3 BUSIF0-7
+ * total 22 52
+ */
+static const int gen2_id[] = { 0, 4, 8, 12, 13, 14, 15, 16, 17, 18 };
+static const int gen3_id[] = { 0, 8, 16, 24, 32, 40, 41, 42, 43, 44 };
+
static u32 *rsnd_ssiu_get_status(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
enum rsnd_mod_type type)
{
struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
- int busif = rsnd_ssi_get_busif(io);
+ int busif = rsnd_mod_id_sub(mod);
return &ssiu->busif_status[busif];
}
@@ -158,7 +177,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
if (rsnd_ssi_use_busif(io)) {
int id = rsnd_mod_id(mod);
- int busif = rsnd_ssi_get_busif(io);
+ int busif = rsnd_mod_id_sub(mod);
/*
* FIXME
@@ -255,7 +274,7 @@ static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- int busif = rsnd_ssi_get_busif(io);
+ int busif = rsnd_mod_id_sub(mod);
if (!rsnd_ssi_use_busif(io))
return 0;
@@ -273,7 +292,7 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
struct rsnd_priv *priv)
{
struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
- int busif = rsnd_ssi_get_busif(io);
+ int busif = rsnd_mod_id_sub(mod);
if (!rsnd_ssi_use_busif(io))
return 0;
@@ -289,8 +308,49 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
return 0;
}
+static int rsnd_ssiu_id(struct rsnd_mod *mod)
+{
+ struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
+
+ /* see rsnd_ssiu_probe() */
+ return ssiu->id;
+}
+
+static int rsnd_ssiu_id_sub(struct rsnd_mod *mod)
+{
+ struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod);
+
+ /* see rsnd_ssiu_probe() */
+ return ssiu->id_sub;
+}
+
+static struct dma_chan *rsnd_ssiu_dma_req(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ int is_play = rsnd_io_is_play(io);
+ char *name;
+
+ /*
+ * It should use "rcar_sound,ssiu" on DT.
+ * But, we need to keep compatibility for old version.
+ *
+ * If it has "rcar_sound.ssiu", it will be used.
+ * If not, "rcar_sound.ssi" will be used.
+ * see
+ * rsnd_ssi_dma_req()
+ * rsnd_dma_of_path()
+ */
+
+ name = is_play ? "rx" : "tx";
+
+ return rsnd_dma_request_channel(rsnd_ssiu_of_node(priv),
+ mod, name);
+}
+
static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = {
.name = SSIU_NAME,
+ .dma_req = rsnd_ssiu_dma_req,
.init = rsnd_ssiu_init_gen2,
.start = rsnd_ssiu_start_gen2,
.stop = rsnd_ssiu_stop_gen2,
@@ -305,26 +365,83 @@ static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id)
return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id);
}
-int rsnd_ssiu_attach(struct rsnd_dai_stream *io,
- struct rsnd_mod *ssi_mod)
+static void rsnd_parse_connect_ssiu_compatible(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io)
+{
+ struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+ struct rsnd_mod *mod;
+ struct rsnd_ssiu *ssiu;
+ int i;
+
+ if (!ssi_mod)
+ return;
+
+ /* select BUSIF0 */
+ for_each_rsnd_ssiu(ssiu, priv, i) {
+ mod = rsnd_mod_get(ssiu);
+
+ if ((rsnd_mod_id(ssi_mod) == rsnd_ssiu_id(mod)) &&
+ (rsnd_mod_id_sub(mod) == 0))
+ rsnd_dai_connect(mod, io, mod->type);
+ }
+}
+
+void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai,
+ struct device_node *playback,
+ struct device_node *capture)
{
- struct rsnd_priv *priv = rsnd_io_to_priv(io);
- struct rsnd_mod *mod = rsnd_ssiu_mod_get(priv, rsnd_mod_id(ssi_mod));
+ struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+ struct device_node *node = rsnd_ssiu_of_node(priv);
+ struct device_node *np;
+ struct rsnd_mod *mod;
+ struct rsnd_dai_stream *io_p = &rdai->playback;
+ struct rsnd_dai_stream *io_c = &rdai->capture;
+ int i;
- rsnd_mod_confirm_ssi(ssi_mod);
+ /* use rcar_sound,ssiu if exist */
+ if (node) {
+ i = 0;
+ for_each_child_of_node(node, np) {
+ mod = rsnd_ssiu_mod_get(priv, i);
+ if (np == playback)
+ rsnd_dai_connect(mod, io_p, mod->type);
+ if (np == capture)
+ rsnd_dai_connect(mod, io_c, mod->type);
+ i++;
+ }
- return rsnd_dai_connect(mod, io, mod->type);
+ of_node_put(node);
+ }
+
+ /* Keep DT compatibility */
+ if (!rsnd_io_to_mod_ssiu(io_p))
+ rsnd_parse_connect_ssiu_compatible(priv, io_p);
+ if (!rsnd_io_to_mod_ssiu(io_c))
+ rsnd_parse_connect_ssiu_compatible(priv, io_c);
}
int rsnd_ssiu_probe(struct rsnd_priv *priv)
{
struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *node;
struct rsnd_ssiu *ssiu;
struct rsnd_mod_ops *ops;
+ const int *list = NULL;
int i, nr, ret;
- /* same number to SSI */
- nr = priv->ssi_nr;
+ /*
+ * Keep DT compatibility.
+ * if it has "rcar_sound,ssiu", use it.
+ * if not, use "rcar_sound,ssi"
+ * see
+ * rsnd_ssiu_bufsif_to_id()
+ */
+ node = rsnd_ssiu_of_node(priv);
+ if (node)
+ nr = of_get_child_count(node);
+ else
+ nr = priv->ssi_nr;
+
ssiu = devm_kcalloc(dev, nr, sizeof(*ssiu), GFP_KERNEL);
if (!ssiu)
return -ENOMEM;
@@ -337,7 +454,44 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv)
else
ops = &rsnd_ssiu_ops_gen2;
+ /* Keep compatibility */
+ nr = 0;
+ if ((node) &&
+ (ops == &rsnd_ssiu_ops_gen2)) {
+ ops->id = rsnd_ssiu_id;
+ ops->id_sub = rsnd_ssiu_id_sub;
+
+ if (rsnd_is_gen2(priv)) {
+ list = gen2_id;
+ nr = ARRAY_SIZE(gen2_id);
+ } else if (rsnd_is_gen3(priv)) {
+ list = gen3_id;
+ nr = ARRAY_SIZE(gen3_id);
+ } else {
+ dev_err(dev, "unknown SSIU\n");
+ return -ENODEV;
+ }
+ }
+
for_each_rsnd_ssiu(ssiu, priv, i) {
+ if (node) {
+ int j;
+
+ /*
+ * see
+ * rsnd_ssiu_get_id()
+ * rsnd_ssiu_get_id_sub()
+ */
+ for (j = 0; j < nr; j++) {
+ if (list[j] > i)
+ break;
+ ssiu->id = j;
+ ssiu->id_sub = i - list[ssiu->id];
+ }
+ } else {
+ ssiu->id = i;
+ }
+
ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
ops, NULL, RSND_MOD_SSIU, i);
if (ret)