diff options
author | Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 2013-07-22 00:36:21 -0400 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2013-07-28 14:34:09 -0400 |
commit | 3337744ac41bee00b0068ad5f926dd9c27540809 (patch) | |
tree | 3a7577c4e2d4c78b6f20ad922670e3abd38b0892 | |
parent | cdaa3cdfb4a710545a53740b1780a683b043618a (diff) |
ASoC: add Renesas R-Car Generation feature
Renesas R-Car series sound circuit consists of SSI and its peripheral.
But this peripheral circuit is different between
R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2)
(Actually, there are many difference in Generation1 chips)
The main difference between Gen1 and Gen2 are
1) register offset, 2) data path
In order to control Gen1/Gen2 by same method,
this patch adds gen.c.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
-rw-r--r-- | include/sound/rcar_snd.h | 10 | ||||
-rw-r--r-- | sound/soc/sh/rcar/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/sh/rcar/core.c | 58 | ||||
-rw-r--r-- | sound/soc/sh/rcar/gen.c | 154 | ||||
-rw-r--r-- | sound/soc/sh/rcar/rsnd.h | 47 |
5 files changed, 269 insertions, 2 deletions
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 7272b2ea7108..14942a827fe5 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h | |||
@@ -22,6 +22,16 @@ struct rsnd_dai_platform_info { | |||
22 | int ssi_id_capture; | 22 | int ssi_id_capture; |
23 | }; | 23 | }; |
24 | 24 | ||
25 | /* | ||
26 | * flags | ||
27 | * | ||
28 | * 0x0000000A | ||
29 | * | ||
30 | * A : generation | ||
31 | */ | ||
32 | #define RSND_GEN1 (1 << 0) /* fixme */ | ||
33 | #define RSND_GEN2 (2 << 0) /* fixme */ | ||
34 | |||
25 | struct rcar_snd_info { | 35 | struct rcar_snd_info { |
26 | u32 flags; | 36 | u32 flags; |
27 | struct rsnd_dai_platform_info *dai_info; | 37 | struct rsnd_dai_platform_info *dai_info; |
diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index cd8089f20c94..b2d313b1eb94 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile | |||
@@ -1,2 +1,2 @@ | |||
1 | snd-soc-rcar-objs := core.o | 1 | snd-soc-rcar-objs := core.o gen.o |
2 | obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o | 2 | obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o |
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index a47fda2aa600..bb8959f93a7d 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c | |||
@@ -108,6 +108,50 @@ | |||
108 | 108 | ||
109 | 109 | ||
110 | /* | 110 | /* |
111 | * basic function | ||
112 | */ | ||
113 | u32 rsnd_read(struct rsnd_priv *priv, | ||
114 | struct rsnd_mod *mod, enum rsnd_reg reg) | ||
115 | { | ||
116 | void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); | ||
117 | |||
118 | BUG_ON(!base); | ||
119 | |||
120 | return ioread32(base); | ||
121 | } | ||
122 | |||
123 | void rsnd_write(struct rsnd_priv *priv, | ||
124 | struct rsnd_mod *mod, | ||
125 | enum rsnd_reg reg, u32 data) | ||
126 | { | ||
127 | void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); | ||
128 | struct device *dev = rsnd_priv_to_dev(priv); | ||
129 | |||
130 | BUG_ON(!base); | ||
131 | |||
132 | dev_dbg(dev, "w %p : %08x\n", base, data); | ||
133 | |||
134 | iowrite32(data, base); | ||
135 | } | ||
136 | |||
137 | void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, | ||
138 | enum rsnd_reg reg, u32 mask, u32 data) | ||
139 | { | ||
140 | void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); | ||
141 | struct device *dev = rsnd_priv_to_dev(priv); | ||
142 | u32 val; | ||
143 | |||
144 | BUG_ON(!base); | ||
145 | |||
146 | val = ioread32(base); | ||
147 | val &= ~mask; | ||
148 | val |= data & mask; | ||
149 | iowrite32(val, base); | ||
150 | |||
151 | dev_dbg(dev, "s %p : %08x\n", base, val); | ||
152 | } | ||
153 | |||
154 | /* | ||
111 | * rsnd_mod functions | 155 | * rsnd_mod functions |
112 | */ | 156 | */ |
113 | char *rsnd_mod_name(struct rsnd_mod *mod) | 157 | char *rsnd_mod_name(struct rsnd_mod *mod) |
@@ -289,6 +333,10 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, | |||
289 | if (ret < 0) | 333 | if (ret < 0) |
290 | goto dai_trigger_end; | 334 | goto dai_trigger_end; |
291 | 335 | ||
336 | ret = rsnd_gen_path_init(priv, rdai, io); | ||
337 | if (ret < 0) | ||
338 | goto dai_trigger_end; | ||
339 | |||
292 | ret = rsnd_dai_call(rdai, io, init); | 340 | ret = rsnd_dai_call(rdai, io, init); |
293 | if (ret < 0) | 341 | if (ret < 0) |
294 | goto dai_trigger_end; | 342 | goto dai_trigger_end; |
@@ -306,10 +354,13 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, | |||
306 | if (ret < 0) | 354 | if (ret < 0) |
307 | goto dai_trigger_end; | 355 | goto dai_trigger_end; |
308 | 356 | ||
309 | ret = rsnd_platform_call(priv, dai, stop, ssi_id); | 357 | ret = rsnd_gen_path_exit(priv, rdai, io); |
310 | if (ret < 0) | 358 | if (ret < 0) |
311 | goto dai_trigger_end; | 359 | goto dai_trigger_end; |
312 | 360 | ||
361 | ret = rsnd_platform_call(priv, dai, stop, ssi_id); | ||
362 | if (ret < 0) | ||
363 | goto dai_trigger_end; | ||
313 | break; | 364 | break; |
314 | default: | 365 | default: |
315 | ret = -EINVAL; | 366 | ret = -EINVAL; |
@@ -572,6 +623,10 @@ static int rsnd_probe(struct platform_device *pdev) | |||
572 | /* | 623 | /* |
573 | * init each module | 624 | * init each module |
574 | */ | 625 | */ |
626 | ret = rsnd_gen_probe(pdev, info, priv); | ||
627 | if (ret < 0) | ||
628 | return ret; | ||
629 | |||
575 | ret = rsnd_dai_probe(pdev, info, priv); | 630 | ret = rsnd_dai_probe(pdev, info, priv); |
576 | if (ret < 0) | 631 | if (ret < 0) |
577 | return ret; | 632 | return ret; |
@@ -615,6 +670,7 @@ static int rsnd_remove(struct platform_device *pdev) | |||
615 | * remove each module | 670 | * remove each module |
616 | */ | 671 | */ |
617 | rsnd_dai_remove(pdev, priv); | 672 | rsnd_dai_remove(pdev, priv); |
673 | rsnd_gen_remove(pdev, priv); | ||
618 | 674 | ||
619 | return 0; | 675 | return 0; |
620 | } | 676 | } |
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c new file mode 100644 index 000000000000..ec67a796eca2 --- /dev/null +++ b/sound/soc/sh/rcar/gen.c | |||
@@ -0,0 +1,154 @@ | |||
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 { | ||
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 | |||
22 | struct 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 | |||
28 | struct 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 | */ | ||
48 | static int rsnd_gen1_probe(struct platform_device *pdev, | ||
49 | struct rcar_snd_info *info, | ||
50 | struct rsnd_priv *priv) | ||
51 | { | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | static void rsnd_gen1_remove(struct platform_device *pdev, | ||
56 | struct rsnd_priv *priv) | ||
57 | { | ||
58 | } | ||
59 | |||
60 | /* | ||
61 | * Gen | ||
62 | */ | ||
63 | int rsnd_gen_path_init(struct rsnd_priv *priv, | ||
64 | struct rsnd_dai *rdai, | ||
65 | struct rsnd_dai_stream *io) | ||
66 | { | ||
67 | struct rsnd_gen *gen = rsnd_priv_to_gen(priv); | ||
68 | |||
69 | return gen->ops->path_init(priv, rdai, io); | ||
70 | } | ||
71 | |||
72 | int rsnd_gen_path_exit(struct rsnd_priv *priv, | ||
73 | struct rsnd_dai *rdai, | ||
74 | struct rsnd_dai_stream *io) | ||
75 | { | ||
76 | struct rsnd_gen *gen = rsnd_priv_to_gen(priv); | ||
77 | |||
78 | return gen->ops->path_exit(priv, rdai, io); | ||
79 | } | ||
80 | |||
81 | void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, | ||
82 | struct rsnd_mod *mod, | ||
83 | enum rsnd_reg reg) | ||
84 | { | ||
85 | struct rsnd_gen *gen = rsnd_priv_to_gen(priv); | ||
86 | struct device *dev = rsnd_priv_to_dev(priv); | ||
87 | int index; | ||
88 | u32 offset_id, offset_adr; | ||
89 | |||
90 | if (reg >= RSND_REG_MAX) { | ||
91 | dev_err(dev, "rsnd_reg reg error\n"); | ||
92 | return NULL; | ||
93 | } | ||
94 | |||
95 | index = gen->reg_map[reg].index; | ||
96 | offset_id = gen->reg_map[reg].offset_id; | ||
97 | offset_adr = gen->reg_map[reg].offset_adr; | ||
98 | |||
99 | if (index < 0) { | ||
100 | dev_err(dev, "unsupported reg access %d\n", reg); | ||
101 | return NULL; | ||
102 | } | ||
103 | |||
104 | if (offset_id && mod) | ||
105 | offset_id *= rsnd_mod_id(mod); | ||
106 | |||
107 | /* | ||
108 | * index/offset were set on gen1/gen2 | ||
109 | */ | ||
110 | |||
111 | return gen->base[index] + offset_id + offset_adr; | ||
112 | } | ||
113 | |||
114 | int rsnd_gen_probe(struct platform_device *pdev, | ||
115 | struct rcar_snd_info *info, | ||
116 | struct rsnd_priv *priv) | ||
117 | { | ||
118 | struct device *dev = rsnd_priv_to_dev(priv); | ||
119 | struct rsnd_gen *gen; | ||
120 | int i; | ||
121 | |||
122 | gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); | ||
123 | if (!gen) { | ||
124 | dev_err(dev, "GEN allocate failed\n"); | ||
125 | return -ENOMEM; | ||
126 | } | ||
127 | |||
128 | priv->gen = gen; | ||
129 | |||
130 | /* | ||
131 | * see | ||
132 | * rsnd_reg_get() | ||
133 | * rsnd_gen_probe() | ||
134 | */ | ||
135 | for (i = 0; i < RSND_REG_MAX; i++) | ||
136 | gen->reg_map[i].index = -1; | ||
137 | |||
138 | /* | ||
139 | * init each module | ||
140 | */ | ||
141 | if (rsnd_is_gen1(priv)) | ||
142 | return rsnd_gen1_probe(pdev, info, priv); | ||
143 | |||
144 | dev_err(dev, "unknown generation R-Car sound device\n"); | ||
145 | |||
146 | return -ENODEV; | ||
147 | } | ||
148 | |||
149 | void rsnd_gen_remove(struct platform_device *pdev, | ||
150 | struct rsnd_priv *priv) | ||
151 | { | ||
152 | if (rsnd_is_gen1(priv)) | ||
153 | rsnd_gen1_remove(pdev, priv); | ||
154 | } | ||
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 65d3835cffbc..8cc36416da25 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h | |||
@@ -27,12 +27,36 @@ | |||
27 | * This driver uses pseudo register in order to hide it. | 27 | * This driver uses pseudo register in order to hide it. |
28 | * see gen1/gen2 for detail | 28 | * see gen1/gen2 for detail |
29 | */ | 29 | */ |
30 | enum rsnd_reg { | ||
31 | RSND_REG_MAX, | ||
32 | }; | ||
33 | |||
30 | struct rsnd_priv; | 34 | struct rsnd_priv; |
31 | struct rsnd_mod; | 35 | struct rsnd_mod; |
32 | struct rsnd_dai; | 36 | struct rsnd_dai; |
33 | struct rsnd_dai_stream; | 37 | struct rsnd_dai_stream; |
34 | 38 | ||
35 | /* | 39 | /* |
40 | * R-Car basic functions | ||
41 | */ | ||
42 | #define rsnd_mod_read(m, r) \ | ||
43 | rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r) | ||
44 | #define rsnd_mod_write(m, r, d) \ | ||
45 | rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) | ||
46 | #define rsnd_mod_bset(m, r, s, d) \ | ||
47 | rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) | ||
48 | |||
49 | #define rsnd_priv_read(p, r) rsnd_read(p, NULL, RSND_REG_##r) | ||
50 | #define rsnd_priv_write(p, r, d) rsnd_write(p, NULL, RSND_REG_##r, d) | ||
51 | #define rsnd_priv_bset(p, r, s, d) rsnd_bset(p, NULL, RSND_REG_##r, s, d) | ||
52 | |||
53 | u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); | ||
54 | void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod, | ||
55 | enum rsnd_reg reg, u32 data); | ||
56 | void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, | ||
57 | u32 mask, u32 data); | ||
58 | |||
59 | /* | ||
36 | * R-Car sound mod | 60 | * R-Car sound mod |
37 | */ | 61 | */ |
38 | 62 | ||
@@ -117,6 +141,24 @@ void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); | |||
117 | int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); | 141 | int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); |
118 | 142 | ||
119 | /* | 143 | /* |
144 | * R-Car Gen1/Gen2 | ||
145 | */ | ||
146 | int rsnd_gen_probe(struct platform_device *pdev, | ||
147 | struct rcar_snd_info *info, | ||
148 | struct rsnd_priv *priv); | ||
149 | void rsnd_gen_remove(struct platform_device *pdev, | ||
150 | struct rsnd_priv *priv); | ||
151 | int rsnd_gen_path_init(struct rsnd_priv *priv, | ||
152 | struct rsnd_dai *rdai, | ||
153 | struct rsnd_dai_stream *io); | ||
154 | int rsnd_gen_path_exit(struct rsnd_priv *priv, | ||
155 | struct rsnd_dai *rdai, | ||
156 | struct rsnd_dai_stream *io); | ||
157 | void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, | ||
158 | struct rsnd_mod *mod, | ||
159 | enum rsnd_reg reg); | ||
160 | |||
161 | /* | ||
120 | * R-Car sound priv | 162 | * R-Car sound priv |
121 | */ | 163 | */ |
122 | struct rsnd_priv { | 164 | struct rsnd_priv { |
@@ -126,6 +168,11 @@ struct rsnd_priv { | |||
126 | spinlock_t lock; | 168 | spinlock_t lock; |
127 | 169 | ||
128 | /* | 170 | /* |
171 | * below value will be filled on rsnd_gen_probe() | ||
172 | */ | ||
173 | void *gen; | ||
174 | |||
175 | /* | ||
129 | * below value will be filled on rsnd_dai_probe() | 176 | * below value will be filled on rsnd_dai_probe() |
130 | */ | 177 | */ |
131 | struct snd_soc_dai_driver *daidrv; | 178 | struct snd_soc_dai_driver *daidrv; |