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