Commit | Line | Data |
---|---|---|
9269e3c3 KM |
1 | /* |
2 | * ctu.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 CTU_NAME_SIZE 16 | |
13 | #define CTU_NAME "ctu" | |
14 | ||
15 | struct rsnd_ctu { | |
16 | struct rsnd_ctu_platform_info *info; /* rcar_snd.h */ | |
17 | struct rsnd_mod mod; | |
18 | }; | |
19 | ||
20 | #define rsnd_ctu_nr(priv) ((priv)->ctu_nr) | |
21 | #define for_each_rsnd_ctu(pos, priv, i) \ | |
22 | for ((i) = 0; \ | |
23 | ((i) < rsnd_ctu_nr(priv)) && \ | |
24 | ((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \ | |
25 | i++) | |
26 | ||
27 | #define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1) | |
28 | #define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0) | |
29 | static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable) | |
30 | { | |
31 | rsnd_mod_write(mod, CTU_CTUIR, enable); | |
32 | } | |
33 | ||
34 | static int rsnd_ctu_init(struct rsnd_mod *mod, | |
35 | struct rsnd_dai_stream *io, | |
36 | struct rsnd_priv *priv) | |
37 | { | |
38 | rsnd_mod_hw_start(mod); | |
39 | ||
40 | rsnd_ctu_initialize_lock(mod); | |
41 | ||
42 | rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io)); | |
43 | ||
44 | rsnd_ctu_initialize_unlock(mod); | |
45 | ||
46 | return 0; | |
47 | } | |
48 | ||
49 | static int rsnd_ctu_quit(struct rsnd_mod *mod, | |
50 | struct rsnd_dai_stream *io, | |
51 | struct rsnd_priv *priv) | |
52 | { | |
53 | rsnd_mod_hw_stop(mod); | |
54 | ||
55 | return 0; | |
56 | } | |
57 | ||
58 | static struct rsnd_mod_ops rsnd_ctu_ops = { | |
59 | .name = CTU_NAME, | |
60 | .init = rsnd_ctu_init, | |
61 | .quit = rsnd_ctu_quit, | |
62 | }; | |
63 | ||
64 | struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) | |
65 | { | |
66 | if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv))) | |
67 | id = 0; | |
68 | ||
69 | return &((struct rsnd_ctu *)(priv->ctu) + id)->mod; | |
70 | } | |
71 | ||
e773c2f9 | 72 | static void rsnd_of_parse_ctu(struct platform_device *pdev, |
9269e3c3 KM |
73 | const struct rsnd_of_data *of_data, |
74 | struct rsnd_priv *priv) | |
75 | { | |
76 | struct device_node *node; | |
77 | struct rsnd_ctu_platform_info *ctu_info; | |
78 | struct rcar_snd_info *info = rsnd_priv_to_info(priv); | |
79 | struct device *dev = &pdev->dev; | |
80 | int nr; | |
81 | ||
82 | if (!of_data) | |
83 | return; | |
84 | ||
85 | node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu"); | |
86 | if (!node) | |
87 | return; | |
88 | ||
89 | nr = of_get_child_count(node); | |
90 | if (!nr) | |
91 | goto rsnd_of_parse_ctu_end; | |
92 | ||
93 | ctu_info = devm_kzalloc(dev, | |
94 | sizeof(struct rsnd_ctu_platform_info) * nr, | |
95 | GFP_KERNEL); | |
96 | if (!ctu_info) { | |
97 | dev_err(dev, "ctu info allocation error\n"); | |
98 | goto rsnd_of_parse_ctu_end; | |
99 | } | |
100 | ||
101 | info->ctu_info = ctu_info; | |
102 | info->ctu_info_nr = nr; | |
103 | ||
104 | rsnd_of_parse_ctu_end: | |
105 | of_node_put(node); | |
106 | ||
107 | } | |
108 | ||
109 | int rsnd_ctu_probe(struct platform_device *pdev, | |
110 | const struct rsnd_of_data *of_data, | |
111 | struct rsnd_priv *priv) | |
112 | { | |
113 | struct rcar_snd_info *info = rsnd_priv_to_info(priv); | |
114 | struct device *dev = rsnd_priv_to_dev(priv); | |
115 | struct rsnd_ctu *ctu; | |
116 | struct clk *clk; | |
117 | char name[CTU_NAME_SIZE]; | |
118 | int i, nr, ret; | |
119 | ||
120 | /* This driver doesn't support Gen1 at this point */ | |
121 | if (rsnd_is_gen1(priv)) { | |
122 | dev_warn(dev, "CTU is not supported on Gen1\n"); | |
123 | return -EINVAL; | |
124 | } | |
125 | ||
126 | rsnd_of_parse_ctu(pdev, of_data, priv); | |
127 | ||
128 | nr = info->ctu_info_nr; | |
129 | if (!nr) | |
130 | return 0; | |
131 | ||
132 | ctu = devm_kzalloc(dev, sizeof(*ctu) * nr, GFP_KERNEL); | |
133 | if (!ctu) | |
134 | return -ENOMEM; | |
135 | ||
136 | priv->ctu_nr = nr; | |
137 | priv->ctu = ctu; | |
138 | ||
139 | for_each_rsnd_ctu(ctu, priv, i) { | |
140 | /* | |
141 | * CTU00, CTU01, CTU02, CTU03 => CTU0 | |
142 | * CTU10, CTU11, CTU12, CTU13 => CTU1 | |
143 | */ | |
144 | snprintf(name, CTU_NAME_SIZE, "%s.%d", | |
145 | CTU_NAME, i / 4); | |
146 | ||
147 | clk = devm_clk_get(dev, name); | |
148 | if (IS_ERR(clk)) | |
149 | return PTR_ERR(clk); | |
150 | ||
151 | ctu->info = &info->ctu_info[i]; | |
152 | ||
153 | ret = rsnd_mod_init(priv, &ctu->mod, &rsnd_ctu_ops, | |
154 | clk, RSND_MOD_CTU, i); | |
155 | if (ret) | |
156 | return ret; | |
157 | } | |
158 | ||
159 | return 0; | |
160 | } | |
161 | ||
162 | void rsnd_ctu_remove(struct platform_device *pdev, | |
163 | struct rsnd_priv *priv) | |
164 | { | |
165 | struct rsnd_ctu *ctu; | |
166 | int i; | |
167 | ||
168 | for_each_rsnd_ctu(ctu, priv, i) { | |
169 | rsnd_mod_quit(&ctu->mod); | |
170 | } | |
171 | } |