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
15struct 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
28static 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)
36static void __rsnd_mix_initialize_lock(struct rsnd_mod *mod, u32 enable)
37{
38	rsnd_mod_write(mod, MIX_MIXIR, enable);
39}
40
41static 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
57static 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
82static 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
91static struct rsnd_mod_ops rsnd_mix_ops = {
92	.name		= MIX_NAME,
93	.init		= rsnd_mix_init,
94	.quit		= rsnd_mix_quit,
95};
96
97struct 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
105static 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
137rsnd_of_parse_mix_end:
138	of_node_put(node);
139
140}
141
142int 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
189void 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