blob: ed21a136354f0a45e7bd18287dfc1d94131409d1 [file] [log] [blame]
Kuninori Morimoto33377442013-07-21 21:36:21 -07001/*
2 * Renesas R-Car Gen1 SRU/SSI support
3 *
4 * Copyright (C) 2013 Renesas Solutions Corp.
5 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11#include "rsnd.h"
12
13struct rsnd_gen_ops {
14 int (*path_init)(struct rsnd_priv *priv,
15 struct rsnd_dai *rdai,
16 struct rsnd_dai_stream *io);
17 int (*path_exit)(struct rsnd_priv *priv,
18 struct rsnd_dai *rdai,
19 struct rsnd_dai_stream *io);
20};
21
22struct rsnd_gen_reg_map {
23 int index; /* -1 : not supported */
24 u32 offset_id; /* offset of ssi0, ssi1, ssi2... */
25 u32 offset_adr; /* offset of SSICR, SSISR, ... */
26};
27
28struct rsnd_gen {
29 void __iomem *base[RSND_BASE_MAX];
30
31 struct rsnd_gen_reg_map reg_map[RSND_REG_MAX];
32 struct rsnd_gen_ops *ops;
33};
34
35#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen)
36
37#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1)
38#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2)
39
40/*
41 * Gen2
42 * will be filled in the future
43 */
44
45/*
46 * Gen1
47 */
Kuninori Morimoto07539c12013-07-21 21:36:35 -070048static int rsnd_gen1_path_init(struct rsnd_priv *priv,
49 struct rsnd_dai *rdai,
50 struct rsnd_dai_stream *io)
51{
52 struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai);
53 struct rsnd_mod *mod;
54 int ret;
55 int id;
56
57 /*
58 * Gen1 is created by SRU/SSI, and this SRU is base module of
59 * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU)
60 *
61 * Easy image is..
62 * Gen1 SRU = Gen2 SCU + SSIU + etc
63 *
64 * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is
65 * using fixed path.
66 *
67 * Then, SSI id = SCU id here
68 */
69
70 if (rsnd_dai_is_play(rdai, io))
71 id = info->ssi_id_playback;
72 else
73 id = info->ssi_id_capture;
74
75 /* SCU */
76 mod = rsnd_scu_mod_get(priv, id);
77 ret = rsnd_dai_connect(rdai, mod, io);
78
79 return ret;
80}
81
82static int rsnd_gen1_path_exit(struct rsnd_priv *priv,
83 struct rsnd_dai *rdai,
84 struct rsnd_dai_stream *io)
85{
86 struct rsnd_mod *mod, *n;
87 int ret = 0;
88
89 /*
90 * remove all mod from rdai
91 */
92 for_each_rsnd_mod(mod, n, io)
93 ret |= rsnd_dai_disconnect(mod);
94
95 return ret;
96}
97
98static struct rsnd_gen_ops rsnd_gen1_ops = {
99 .path_init = rsnd_gen1_path_init,
100 .path_exit = rsnd_gen1_path_exit,
101};
102
103#define RSND_GEN1_REG_MAP(g, s, i, oi, oa) \
104 do { \
105 (g)->reg_map[RSND_REG_##i].index = RSND_GEN1_##s; \
106 (g)->reg_map[RSND_REG_##i].offset_id = oi; \
107 (g)->reg_map[RSND_REG_##i].offset_adr = oa; \
108 } while (0)
109
110static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen)
111{
112 RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0);
113 RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4);
Kuninori Morimotodfc94032013-07-21 21:36:46 -0700114
115 RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00);
116 RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04);
117 RSND_GEN1_REG_MAP(gen, ADG, SSICKR, 0x0, 0x08);
118 RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL0, 0x0, 0x0c);
119 RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL1, 0x0, 0x10);
120 RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18);
121 RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c);
122 RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20);
Kuninori Morimoto07539c12013-07-21 21:36:35 -0700123}
124
Kuninori Morimoto33377442013-07-21 21:36:21 -0700125static int rsnd_gen1_probe(struct platform_device *pdev,
126 struct rcar_snd_info *info,
127 struct rsnd_priv *priv)
128{
Kuninori Morimoto07539c12013-07-21 21:36:35 -0700129 struct device *dev = rsnd_priv_to_dev(priv);
130 struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
131 struct resource *sru_res;
Kuninori Morimotodfc94032013-07-21 21:36:46 -0700132 struct resource *adg_res;
Kuninori Morimoto07539c12013-07-21 21:36:35 -0700133
134 /*
135 * map address
136 */
137 sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU);
Kuninori Morimotodfc94032013-07-21 21:36:46 -0700138 adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG);
139 if (!sru_res ||
140 !adg_res) {
Kuninori Morimoto07539c12013-07-21 21:36:35 -0700141 dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n");
142 return -ENODEV;
143 }
144
145 gen->ops = &rsnd_gen1_ops;
146
147 gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res);
Kuninori Morimotodfc94032013-07-21 21:36:46 -0700148 gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res);
149 if (!gen->base[RSND_GEN1_SRU] ||
150 !gen->base[RSND_GEN1_ADG]) {
Kuninori Morimoto07539c12013-07-21 21:36:35 -0700151 dev_err(dev, "SRU/SSI/ADG ioremap failed\n");
152 return -ENODEV;
153 }
154
155 rsnd_gen1_reg_map_init(gen);
156
157 dev_dbg(dev, "Gen1 device probed\n");
158 dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start,
159 gen->base[RSND_GEN1_SRU]);
Kuninori Morimotodfc94032013-07-21 21:36:46 -0700160 dev_dbg(dev, "ADG : %08x => %p\n", adg_res->start,
161 gen->base[RSND_GEN1_ADG]);
Kuninori Morimoto07539c12013-07-21 21:36:35 -0700162
Kuninori Morimoto33377442013-07-21 21:36:21 -0700163 return 0;
164}
165
166static void rsnd_gen1_remove(struct platform_device *pdev,
167 struct rsnd_priv *priv)
168{
169}
170
171/*
172 * Gen
173 */
174int rsnd_gen_path_init(struct rsnd_priv *priv,
175 struct rsnd_dai *rdai,
176 struct rsnd_dai_stream *io)
177{
178 struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
179
180 return gen->ops->path_init(priv, rdai, io);
181}
182
183int rsnd_gen_path_exit(struct rsnd_priv *priv,
184 struct rsnd_dai *rdai,
185 struct rsnd_dai_stream *io)
186{
187 struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
188
189 return gen->ops->path_exit(priv, rdai, io);
190}
191
192void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
193 struct rsnd_mod *mod,
194 enum rsnd_reg reg)
195{
196 struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
197 struct device *dev = rsnd_priv_to_dev(priv);
198 int index;
199 u32 offset_id, offset_adr;
200
201 if (reg >= RSND_REG_MAX) {
202 dev_err(dev, "rsnd_reg reg error\n");
203 return NULL;
204 }
205
206 index = gen->reg_map[reg].index;
207 offset_id = gen->reg_map[reg].offset_id;
208 offset_adr = gen->reg_map[reg].offset_adr;
209
210 if (index < 0) {
211 dev_err(dev, "unsupported reg access %d\n", reg);
212 return NULL;
213 }
214
215 if (offset_id && mod)
216 offset_id *= rsnd_mod_id(mod);
217
218 /*
219 * index/offset were set on gen1/gen2
220 */
221
222 return gen->base[index] + offset_id + offset_adr;
223}
224
225int rsnd_gen_probe(struct platform_device *pdev,
226 struct rcar_snd_info *info,
227 struct rsnd_priv *priv)
228{
229 struct device *dev = rsnd_priv_to_dev(priv);
230 struct rsnd_gen *gen;
231 int i;
232
233 gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL);
234 if (!gen) {
235 dev_err(dev, "GEN allocate failed\n");
236 return -ENOMEM;
237 }
238
239 priv->gen = gen;
240
241 /*
242 * see
243 * rsnd_reg_get()
244 * rsnd_gen_probe()
245 */
246 for (i = 0; i < RSND_REG_MAX; i++)
247 gen->reg_map[i].index = -1;
248
249 /*
250 * init each module
251 */
252 if (rsnd_is_gen1(priv))
253 return rsnd_gen1_probe(pdev, info, priv);
254
255 dev_err(dev, "unknown generation R-Car sound device\n");
256
257 return -ENODEV;
258}
259
260void rsnd_gen_remove(struct platform_device *pdev,
261 struct rsnd_priv *priv)
262{
263 if (rsnd_is_gen1(priv))
264 rsnd_gen1_remove(pdev, priv);
265}