xref: /openbmc/linux/sound/soc/sh/rcar/mix.c (revision 9b9c2cd4)
1 /*
2  * mix.c
3  *
4  * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10 #include "rsnd.h"
11 
12 #define MIX_NAME_SIZE	16
13 #define MIX_NAME "mix"
14 
15 struct rsnd_mix {
16 	struct rsnd_mix_platform_info *info; /* rcar_snd.h */
17 	struct rsnd_mod mod;
18 };
19 
20 #define rsnd_mix_nr(priv) ((priv)->mix_nr)
21 #define for_each_rsnd_mix(pos, priv, i)					\
22 	for ((i) = 0;							\
23 	     ((i) < rsnd_mix_nr(priv)) &&				\
24 		     ((pos) = (struct rsnd_mix *)(priv)->mix + i);	\
25 	     i++)
26 
27 
28 static void rsnd_mix_soft_reset(struct rsnd_mod *mod)
29 {
30 	rsnd_mod_write(mod, MIX_SWRSR, 0);
31 	rsnd_mod_write(mod, MIX_SWRSR, 1);
32 }
33 
34 #define rsnd_mix_initialize_lock(mod)	__rsnd_mix_initialize_lock(mod, 1)
35 #define rsnd_mix_initialize_unlock(mod)	__rsnd_mix_initialize_lock(mod, 0)
36 static void __rsnd_mix_initialize_lock(struct rsnd_mod *mod, u32 enable)
37 {
38 	rsnd_mod_write(mod, MIX_MIXIR, enable);
39 }
40 
41 static void rsnd_mix_volume_update(struct rsnd_dai_stream *io,
42 				  struct rsnd_mod *mod)
43 {
44 
45 	/* Disable MIX dB setting */
46 	rsnd_mod_write(mod, MIX_MDBER, 0);
47 
48 	rsnd_mod_write(mod, MIX_MDBAR, 0);
49 	rsnd_mod_write(mod, MIX_MDBBR, 0);
50 	rsnd_mod_write(mod, MIX_MDBCR, 0);
51 	rsnd_mod_write(mod, MIX_MDBDR, 0);
52 
53 	/* Enable MIX dB setting */
54 	rsnd_mod_write(mod, MIX_MDBER, 1);
55 }
56 
57 static int rsnd_mix_init(struct rsnd_mod *mod,
58 			 struct rsnd_dai_stream *io,
59 			 struct rsnd_priv *priv)
60 {
61 	rsnd_mod_power_on(mod);
62 
63 	rsnd_mix_soft_reset(mod);
64 
65 	rsnd_mix_initialize_lock(mod);
66 
67 	rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
68 
69 	rsnd_path_parse(priv, io);
70 
71 	/* volume step */
72 	rsnd_mod_write(mod, MIX_MIXMR, 0);
73 	rsnd_mod_write(mod, MIX_MVPDR, 0);
74 
75 	rsnd_mix_volume_update(io, mod);
76 
77 	rsnd_mix_initialize_unlock(mod);
78 
79 	return 0;
80 }
81 
82 static int rsnd_mix_quit(struct rsnd_mod *mod,
83 			 struct rsnd_dai_stream *io,
84 			 struct rsnd_priv *priv)
85 {
86 	rsnd_mod_power_off(mod);
87 
88 	return 0;
89 }
90 
91 static struct rsnd_mod_ops rsnd_mix_ops = {
92 	.name		= MIX_NAME,
93 	.init		= rsnd_mix_init,
94 	.quit		= rsnd_mix_quit,
95 };
96 
97 struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id)
98 {
99 	if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv)))
100 		id = 0;
101 
102 	return rsnd_mod_get((struct rsnd_mix *)(priv->mix) + id);
103 }
104 
105 static void rsnd_of_parse_mix(struct platform_device *pdev,
106 			      const struct rsnd_of_data *of_data,
107 			      struct rsnd_priv *priv)
108 {
109 	struct device_node *node;
110 	struct rsnd_mix_platform_info *mix_info;
111 	struct rcar_snd_info *info = rsnd_priv_to_info(priv);
112 	struct device *dev = &pdev->dev;
113 	int nr;
114 
115 	if (!of_data)
116 		return;
117 
118 	node = of_get_child_by_name(dev->of_node, "rcar_sound,mix");
119 	if (!node)
120 		return;
121 
122 	nr = of_get_child_count(node);
123 	if (!nr)
124 		goto rsnd_of_parse_mix_end;
125 
126 	mix_info = devm_kzalloc(dev,
127 				sizeof(struct rsnd_mix_platform_info) * nr,
128 				GFP_KERNEL);
129 	if (!mix_info) {
130 		dev_err(dev, "mix info allocation error\n");
131 		goto rsnd_of_parse_mix_end;
132 	}
133 
134 	info->mix_info		= mix_info;
135 	info->mix_info_nr	= nr;
136 
137 rsnd_of_parse_mix_end:
138 	of_node_put(node);
139 
140 }
141 
142 int rsnd_mix_probe(struct platform_device *pdev,
143 		   const struct rsnd_of_data *of_data,
144 		   struct rsnd_priv *priv)
145 {
146 	struct rcar_snd_info *info = rsnd_priv_to_info(priv);
147 	struct device *dev = rsnd_priv_to_dev(priv);
148 	struct rsnd_mix *mix;
149 	struct clk *clk;
150 	char name[MIX_NAME_SIZE];
151 	int i, nr, ret;
152 
153 	/* This driver doesn't support Gen1 at this point */
154 	if (rsnd_is_gen1(priv))
155 		return 0;
156 
157 	rsnd_of_parse_mix(pdev, of_data, priv);
158 
159 	nr = info->mix_info_nr;
160 	if (!nr)
161 		return 0;
162 
163 	mix	= devm_kzalloc(dev, sizeof(*mix) * nr, GFP_KERNEL);
164 	if (!mix)
165 		return -ENOMEM;
166 
167 	priv->mix_nr	= nr;
168 	priv->mix	= mix;
169 
170 	for_each_rsnd_mix(mix, priv, i) {
171 		snprintf(name, MIX_NAME_SIZE, "%s.%d",
172 			 MIX_NAME, i);
173 
174 		clk = devm_clk_get(dev, name);
175 		if (IS_ERR(clk))
176 			return PTR_ERR(clk);
177 
178 		mix->info = &info->mix_info[i];
179 
180 		ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
181 				    clk, RSND_MOD_MIX, i);
182 		if (ret)
183 			return ret;
184 	}
185 
186 	return 0;
187 }
188 
189 void rsnd_mix_remove(struct platform_device *pdev,
190 		     struct rsnd_priv *priv)
191 {
192 	struct rsnd_mix *mix;
193 	int i;
194 
195 	for_each_rsnd_mix(mix, priv, i) {
196 		rsnd_mod_quit(rsnd_mod_get(mix));
197 	}
198 }
199