Commit | Line | Data |
---|---|---|
3337744a KM |
1 | /* |
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 | ||
13 | struct rsnd_gen_ops { | |
072188b6 KM |
14 | int (*probe)(struct platform_device *pdev, |
15 | struct rcar_snd_info *info, | |
16 | struct rsnd_priv *priv); | |
17 | void (*remove)(struct platform_device *pdev, | |
18 | struct rsnd_priv *priv); | |
3337744a KM |
19 | int (*path_init)(struct rsnd_priv *priv, |
20 | struct rsnd_dai *rdai, | |
21 | struct rsnd_dai_stream *io); | |
22 | int (*path_exit)(struct rsnd_priv *priv, | |
23 | struct rsnd_dai *rdai, | |
24 | struct rsnd_dai_stream *io); | |
25 | }; | |
26 | ||
27 | struct rsnd_gen_reg_map { | |
28 | int index; /* -1 : not supported */ | |
29 | u32 offset_id; /* offset of ssi0, ssi1, ssi2... */ | |
30 | u32 offset_adr; /* offset of SSICR, SSISR, ... */ | |
31 | }; | |
32 | ||
33 | struct rsnd_gen { | |
34 | void __iomem *base[RSND_BASE_MAX]; | |
35 | ||
36 | struct rsnd_gen_reg_map reg_map[RSND_REG_MAX]; | |
37 | struct rsnd_gen_ops *ops; | |
38 | }; | |
39 | ||
40 | #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) | |
41 | ||
3337744a KM |
42 | /* |
43 | * Gen2 | |
44 | * will be filled in the future | |
45 | */ | |
46 | ||
47 | /* | |
48 | * Gen1 | |
49 | */ | |
07539c1d KM |
50 | static int rsnd_gen1_path_init(struct rsnd_priv *priv, |
51 | struct rsnd_dai *rdai, | |
52 | struct rsnd_dai_stream *io) | |
53 | { | |
07539c1d KM |
54 | struct rsnd_mod *mod; |
55 | int ret; | |
56 | int id; | |
57 | ||
58 | /* | |
59 | * Gen1 is created by SRU/SSI, and this SRU is base module of | |
60 | * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU) | |
61 | * | |
62 | * Easy image is.. | |
63 | * Gen1 SRU = Gen2 SCU + SSIU + etc | |
64 | * | |
65 | * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is | |
66 | * using fixed path. | |
67 | * | |
68 | * Then, SSI id = SCU id here | |
69 | */ | |
70 | ||
4b4dab82 KM |
71 | /* get SSI's ID */ |
72 | mod = rsnd_ssi_mod_get_frm_dai(priv, | |
73 | rsnd_dai_id(priv, rdai), | |
74 | rsnd_dai_is_play(rdai, io)); | |
75 | id = rsnd_mod_id(mod); | |
07539c1d | 76 | |
ae5c3223 KM |
77 | /* SSI */ |
78 | mod = rsnd_ssi_mod_get(priv, id); | |
79 | ret = rsnd_dai_connect(rdai, mod, io); | |
80 | if (ret < 0) | |
81 | return ret; | |
82 | ||
07539c1d KM |
83 | /* SCU */ |
84 | mod = rsnd_scu_mod_get(priv, id); | |
85 | ret = rsnd_dai_connect(rdai, mod, io); | |
86 | ||
87 | return ret; | |
88 | } | |
89 | ||
90 | static int rsnd_gen1_path_exit(struct rsnd_priv *priv, | |
91 | struct rsnd_dai *rdai, | |
92 | struct rsnd_dai_stream *io) | |
93 | { | |
94 | struct rsnd_mod *mod, *n; | |
95 | int ret = 0; | |
96 | ||
97 | /* | |
98 | * remove all mod from rdai | |
99 | */ | |
100 | for_each_rsnd_mod(mod, n, io) | |
101 | ret |= rsnd_dai_disconnect(mod); | |
102 | ||
103 | return ret; | |
104 | } | |
105 | ||
07539c1d KM |
106 | #define RSND_GEN1_REG_MAP(g, s, i, oi, oa) \ |
107 | do { \ | |
108 | (g)->reg_map[RSND_REG_##i].index = RSND_GEN1_##s; \ | |
109 | (g)->reg_map[RSND_REG_##i].offset_id = oi; \ | |
110 | (g)->reg_map[RSND_REG_##i].offset_adr = oa; \ | |
111 | } while (0) | |
112 | ||
113 | static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) | |
114 | { | |
374a5281 KM |
115 | RSND_GEN1_REG_MAP(gen, SRU, SRC_ROUTE_SEL, 0x0, 0x00); |
116 | RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL0, 0x0, 0x08); | |
117 | RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL1, 0x0, 0x0c); | |
118 | RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL2, 0x0, 0x10); | |
119 | RSND_GEN1_REG_MAP(gen, SRU, SRC_CTRL, 0x0, 0xc0); | |
07539c1d KM |
120 | RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); |
121 | RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); | |
374a5281 KM |
122 | RSND_GEN1_REG_MAP(gen, SRU, BUSIF_MODE, 0x4, 0x20); |
123 | RSND_GEN1_REG_MAP(gen, SRU, BUSIF_ADINR, 0x40, 0x214); | |
dfc9403b KM |
124 | |
125 | RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00); | |
126 | RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04); | |
127 | RSND_GEN1_REG_MAP(gen, ADG, SSICKR, 0x0, 0x08); | |
128 | RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL0, 0x0, 0x0c); | |
129 | RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL1, 0x0, 0x10); | |
130 | RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18); | |
131 | RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c); | |
132 | RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20); | |
ae5c3223 KM |
133 | |
134 | RSND_GEN1_REG_MAP(gen, SSI, SSICR, 0x40, 0x00); | |
135 | RSND_GEN1_REG_MAP(gen, SSI, SSISR, 0x40, 0x04); | |
136 | RSND_GEN1_REG_MAP(gen, SSI, SSITDR, 0x40, 0x08); | |
137 | RSND_GEN1_REG_MAP(gen, SSI, SSIRDR, 0x40, 0x0c); | |
138 | RSND_GEN1_REG_MAP(gen, SSI, SSIWSR, 0x40, 0x20); | |
07539c1d KM |
139 | } |
140 | ||
3337744a KM |
141 | static int rsnd_gen1_probe(struct platform_device *pdev, |
142 | struct rcar_snd_info *info, | |
143 | struct rsnd_priv *priv) | |
144 | { | |
07539c1d KM |
145 | struct device *dev = rsnd_priv_to_dev(priv); |
146 | struct rsnd_gen *gen = rsnd_priv_to_gen(priv); | |
147 | struct resource *sru_res; | |
dfc9403b | 148 | struct resource *adg_res; |
ae5c3223 | 149 | struct resource *ssi_res; |
07539c1d KM |
150 | |
151 | /* | |
152 | * map address | |
153 | */ | |
154 | sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU); | |
dfc9403b | 155 | adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG); |
ae5c3223 | 156 | ssi_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SSI); |
07539c1d KM |
157 | |
158 | gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res); | |
dfc9403b | 159 | gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res); |
ae5c3223 | 160 | gen->base[RSND_GEN1_SSI] = devm_ioremap_resource(dev, ssi_res); |
70263cb4 WY |
161 | if (IS_ERR(gen->base[RSND_GEN1_SRU]) || |
162 | IS_ERR(gen->base[RSND_GEN1_ADG]) || | |
163 | IS_ERR(gen->base[RSND_GEN1_SSI])) | |
07539c1d | 164 | return -ENODEV; |
07539c1d KM |
165 | |
166 | rsnd_gen1_reg_map_init(gen); | |
167 | ||
168 | dev_dbg(dev, "Gen1 device probed\n"); | |
169 | dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start, | |
170 | gen->base[RSND_GEN1_SRU]); | |
dfc9403b KM |
171 | dev_dbg(dev, "ADG : %08x => %p\n", adg_res->start, |
172 | gen->base[RSND_GEN1_ADG]); | |
ae5c3223 KM |
173 | dev_dbg(dev, "SSI : %08x => %p\n", ssi_res->start, |
174 | gen->base[RSND_GEN1_SSI]); | |
07539c1d | 175 | |
3337744a | 176 | return 0; |
ae5c3223 | 177 | |
3337744a KM |
178 | } |
179 | ||
180 | static void rsnd_gen1_remove(struct platform_device *pdev, | |
181 | struct rsnd_priv *priv) | |
182 | { | |
183 | } | |
184 | ||
072188b6 KM |
185 | static struct rsnd_gen_ops rsnd_gen1_ops = { |
186 | .probe = rsnd_gen1_probe, | |
187 | .remove = rsnd_gen1_remove, | |
188 | .path_init = rsnd_gen1_path_init, | |
189 | .path_exit = rsnd_gen1_path_exit, | |
190 | }; | |
191 | ||
3337744a KM |
192 | /* |
193 | * Gen | |
194 | */ | |
195 | int rsnd_gen_path_init(struct rsnd_priv *priv, | |
196 | struct rsnd_dai *rdai, | |
197 | struct rsnd_dai_stream *io) | |
198 | { | |
199 | struct rsnd_gen *gen = rsnd_priv_to_gen(priv); | |
200 | ||
201 | return gen->ops->path_init(priv, rdai, io); | |
202 | } | |
203 | ||
204 | int rsnd_gen_path_exit(struct rsnd_priv *priv, | |
205 | struct rsnd_dai *rdai, | |
206 | struct rsnd_dai_stream *io) | |
207 | { | |
208 | struct rsnd_gen *gen = rsnd_priv_to_gen(priv); | |
209 | ||
210 | return gen->ops->path_exit(priv, rdai, io); | |
211 | } | |
212 | ||
213 | void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, | |
214 | struct rsnd_mod *mod, | |
215 | enum rsnd_reg reg) | |
216 | { | |
217 | struct rsnd_gen *gen = rsnd_priv_to_gen(priv); | |
218 | struct device *dev = rsnd_priv_to_dev(priv); | |
219 | int index; | |
220 | u32 offset_id, offset_adr; | |
221 | ||
222 | if (reg >= RSND_REG_MAX) { | |
223 | dev_err(dev, "rsnd_reg reg error\n"); | |
224 | return NULL; | |
225 | } | |
226 | ||
227 | index = gen->reg_map[reg].index; | |
228 | offset_id = gen->reg_map[reg].offset_id; | |
229 | offset_adr = gen->reg_map[reg].offset_adr; | |
230 | ||
231 | if (index < 0) { | |
232 | dev_err(dev, "unsupported reg access %d\n", reg); | |
233 | return NULL; | |
234 | } | |
235 | ||
236 | if (offset_id && mod) | |
237 | offset_id *= rsnd_mod_id(mod); | |
238 | ||
239 | /* | |
240 | * index/offset were set on gen1/gen2 | |
241 | */ | |
242 | ||
243 | return gen->base[index] + offset_id + offset_adr; | |
244 | } | |
245 | ||
246 | int rsnd_gen_probe(struct platform_device *pdev, | |
247 | struct rcar_snd_info *info, | |
248 | struct rsnd_priv *priv) | |
249 | { | |
250 | struct device *dev = rsnd_priv_to_dev(priv); | |
251 | struct rsnd_gen *gen; | |
252 | int i; | |
253 | ||
254 | gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); | |
255 | if (!gen) { | |
256 | dev_err(dev, "GEN allocate failed\n"); | |
257 | return -ENOMEM; | |
258 | } | |
259 | ||
072188b6 KM |
260 | if (rsnd_is_gen1(priv)) |
261 | gen->ops = &rsnd_gen1_ops; | |
262 | ||
263 | if (!gen->ops) { | |
264 | dev_err(dev, "unknown generation R-Car sound device\n"); | |
265 | return -ENODEV; | |
266 | } | |
267 | ||
3337744a KM |
268 | priv->gen = gen; |
269 | ||
270 | /* | |
271 | * see | |
272 | * rsnd_reg_get() | |
273 | * rsnd_gen_probe() | |
274 | */ | |
275 | for (i = 0; i < RSND_REG_MAX; i++) | |
276 | gen->reg_map[i].index = -1; | |
277 | ||
072188b6 | 278 | return gen->ops->probe(pdev, info, priv); |
3337744a KM |
279 | } |
280 | ||
281 | void rsnd_gen_remove(struct platform_device *pdev, | |
282 | struct rsnd_priv *priv) | |
283 | { | |
072188b6 KM |
284 | struct rsnd_gen *gen = rsnd_priv_to_gen(priv); |
285 | ||
286 | gen->ops->remove(pdev, priv); | |
3337744a | 287 | } |