Commit | Line | Data |
---|---|---|
d43f3010 TI |
1 | /* |
2 | * Support for Digigram Lola PCI-e boards | |
3 | * | |
4 | * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the Free | |
8 | * Software Foundation; either version 2 of the License, or (at your option) | |
9 | * any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along with | |
17 | * this program; if not, write to the Free Software Foundation, Inc., 59 | |
18 | * Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
19 | */ | |
20 | ||
21 | #include <linux/kernel.h> | |
22 | #include <linux/init.h> | |
f2e01925 | 23 | #include <linux/delay.h> |
d43f3010 TI |
24 | #include <sound/core.h> |
25 | #include <sound/pcm.h> | |
26 | #include "lola.h" | |
27 | ||
1c5d7b31 | 28 | unsigned int lola_sample_rate_convert(unsigned int coded) |
d43f3010 TI |
29 | { |
30 | unsigned int freq; | |
31 | ||
32 | /* base frequency */ | |
33 | switch (coded & 0x3) { | |
34 | case 0: freq = 48000; break; | |
35 | case 1: freq = 44100; break; | |
36 | case 2: freq = 32000; break; | |
37 | default: return 0; /* error */ | |
38 | } | |
39 | ||
40 | /* multiplier / devisor */ | |
41 | switch (coded & 0x1c) { | |
42 | case (0 << 2): break; | |
43 | case (4 << 2): break; | |
44 | case (1 << 2): freq *= 2; break; | |
45 | case (2 << 2): freq *= 4; break; | |
46 | case (5 << 2): freq /= 2; break; | |
47 | case (6 << 2): freq /= 4; break; | |
48 | default: return 0; /* error */ | |
49 | } | |
50 | ||
51 | /* ajustement */ | |
52 | switch (coded & 0x60) { | |
53 | case (0 << 5): break; | |
54 | case (1 << 5): freq = (freq * 999) / 1000; break; | |
55 | case (2 << 5): freq = (freq * 1001) / 1000; break; | |
56 | default: return 0; /* error */ | |
57 | } | |
58 | return freq; | |
59 | } | |
60 | ||
61 | /* | |
62 | * Granualrity | |
63 | */ | |
64 | ||
65 | #define LOLA_MAXFREQ_AT_GRANULARITY_MIN 48000 | |
66 | #define LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX 96000 | |
67 | ||
68 | static bool check_gran_clock_compatibility(struct lola *chip, | |
69 | unsigned int val, | |
70 | unsigned int freq) | |
71 | { | |
72 | if (!chip->granularity) | |
73 | return true; | |
74 | ||
75 | if (val < LOLA_GRANULARITY_MIN || val > LOLA_GRANULARITY_MAX || | |
76 | (val % LOLA_GRANULARITY_STEP) != 0) | |
77 | return false; | |
78 | ||
79 | if (val == LOLA_GRANULARITY_MIN) { | |
80 | if (freq > LOLA_MAXFREQ_AT_GRANULARITY_MIN) | |
81 | return false; | |
82 | } else if (val < LOLA_GRANULARITY_MAX) { | |
83 | if (freq > LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX) | |
84 | return false; | |
85 | } | |
86 | return true; | |
87 | } | |
88 | ||
89 | int lola_set_granularity(struct lola *chip, unsigned int val, bool force) | |
90 | { | |
91 | int err; | |
92 | ||
93 | if (!force) { | |
94 | if (val == chip->granularity) | |
95 | return 0; | |
96 | #if 0 | |
97 | /* change Gran only if there are no streams allocated ! */ | |
98 | if (chip->audio_in_alloc_mask || chip->audio_out_alloc_mask) | |
99 | return -EBUSY; | |
100 | #endif | |
101 | if (!check_gran_clock_compatibility(chip, val, | |
102 | chip->clock.cur_freq)) | |
103 | return -EINVAL; | |
104 | } | |
105 | ||
106 | chip->granularity = val; | |
107 | val /= LOLA_GRANULARITY_STEP; | |
108 | ||
109 | /* audio function group */ | |
110 | err = lola_codec_write(chip, 1, LOLA_VERB_SET_GRANULARITY_STEPS, | |
111 | val, 0); | |
112 | if (err < 0) | |
113 | return err; | |
114 | /* this can be a very slow function !!! */ | |
115 | usleep_range(400 * val, 20000); | |
116 | return lola_codec_flush(chip); | |
117 | } | |
118 | ||
119 | /* | |
120 | * Clock widget handling | |
121 | */ | |
122 | ||
e23e7a14 | 123 | int lola_init_clock_widget(struct lola *chip, int nid) |
d43f3010 TI |
124 | { |
125 | unsigned int val; | |
126 | int i, j, nitems, nb_verbs, idx, idx_list; | |
127 | int err; | |
128 | ||
129 | err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val); | |
130 | if (err < 0) { | |
f58e2fce | 131 | dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid); |
d43f3010 TI |
132 | return err; |
133 | } | |
134 | ||
135 | if ((val & 0xfff00000) != 0x01f00000) { /* test SubType and Type */ | |
f58e2fce | 136 | dev_dbg(chip->card->dev, "No valid clock widget\n"); |
d43f3010 TI |
137 | return 0; |
138 | } | |
139 | ||
140 | chip->clock.nid = nid; | |
141 | chip->clock.items = val & 0xff; | |
f58e2fce | 142 | dev_dbg(chip->card->dev, "clock_list nid=%x, entries=%d\n", nid, |
d43f3010 TI |
143 | chip->clock.items); |
144 | if (chip->clock.items > MAX_SAMPLE_CLOCK_COUNT) { | |
f58e2fce | 145 | dev_err(chip->card->dev, "CLOCK_LIST too big: %d\n", |
d43f3010 TI |
146 | chip->clock.items); |
147 | return -EINVAL; | |
148 | } | |
149 | ||
150 | nitems = chip->clock.items; | |
151 | nb_verbs = (nitems + 3) / 4; | |
152 | idx = 0; | |
153 | idx_list = 0; | |
154 | for (i = 0; i < nb_verbs; i++) { | |
155 | unsigned int res_ex; | |
156 | unsigned short items[4]; | |
157 | ||
158 | err = lola_codec_read(chip, nid, LOLA_VERB_GET_CLOCK_LIST, | |
159 | idx, 0, &val, &res_ex); | |
160 | if (err < 0) { | |
f58e2fce | 161 | dev_err(chip->card->dev, "Can't read CLOCK_LIST\n"); |
d43f3010 TI |
162 | return -EINVAL; |
163 | } | |
164 | ||
165 | items[0] = val & 0xfff; | |
166 | items[1] = (val >> 16) & 0xfff; | |
167 | items[2] = res_ex & 0xfff; | |
168 | items[3] = (res_ex >> 16) & 0xfff; | |
169 | ||
170 | for (j = 0; j < 4; j++) { | |
171 | unsigned char type = items[j] >> 8; | |
172 | unsigned int freq = items[j] & 0xff; | |
173 | int format = LOLA_CLOCK_FORMAT_NONE; | |
174 | bool add_clock = true; | |
175 | if (type == LOLA_CLOCK_TYPE_INTERNAL) { | |
1c5d7b31 | 176 | freq = lola_sample_rate_convert(freq); |
d43f3010 TI |
177 | if (freq < chip->sample_rate_min) |
178 | add_clock = false; | |
179 | else if (freq == 48000) { | |
180 | chip->clock.cur_index = idx_list; | |
181 | chip->clock.cur_freq = 48000; | |
182 | chip->clock.cur_valid = true; | |
183 | } | |
184 | } else if (type == LOLA_CLOCK_TYPE_VIDEO) { | |
1c5d7b31 | 185 | freq = lola_sample_rate_convert(freq); |
d43f3010 TI |
186 | if (freq < chip->sample_rate_min) |
187 | add_clock = false; | |
188 | /* video clock has a format (0:NTSC, 1:PAL)*/ | |
189 | if (items[j] & 0x80) | |
190 | format = LOLA_CLOCK_FORMAT_NTSC; | |
191 | else | |
192 | format = LOLA_CLOCK_FORMAT_PAL; | |
193 | } | |
194 | if (add_clock) { | |
195 | struct lola_sample_clock *sc; | |
196 | sc = &chip->clock.sample_clock[idx_list]; | |
197 | sc->type = type; | |
198 | sc->format = format; | |
199 | sc->freq = freq; | |
200 | /* keep the index used with the board */ | |
201 | chip->clock.idx_lookup[idx_list] = idx; | |
202 | idx_list++; | |
203 | } else { | |
204 | chip->clock.items--; | |
205 | } | |
206 | if (++idx >= nitems) | |
207 | break; | |
208 | } | |
209 | } | |
210 | return 0; | |
211 | } | |
212 | ||
213 | /* enable unsolicited events of the clock widget */ | |
214 | int lola_enable_clock_events(struct lola *chip) | |
215 | { | |
216 | unsigned int res; | |
217 | int err; | |
218 | ||
219 | err = lola_codec_read(chip, chip->clock.nid, | |
220 | LOLA_VERB_SET_UNSOLICITED_ENABLE, | |
221 | LOLA_UNSOLICITED_ENABLE | LOLA_UNSOLICITED_TAG, | |
222 | 0, &res, NULL); | |
223 | if (err < 0) | |
224 | return err; | |
225 | if (res) { | |
f58e2fce | 226 | dev_warn(chip->card->dev, "error in enable_clock_events %d\n", |
d43f3010 TI |
227 | res); |
228 | return -EINVAL; | |
229 | } | |
230 | return 0; | |
231 | } | |
232 | ||
233 | int lola_set_clock_index(struct lola *chip, unsigned int idx) | |
234 | { | |
235 | unsigned int res; | |
236 | int err; | |
237 | ||
238 | err = lola_codec_read(chip, chip->clock.nid, | |
239 | LOLA_VERB_SET_CLOCK_SELECT, | |
240 | chip->clock.idx_lookup[idx], | |
241 | 0, &res, NULL); | |
242 | if (err < 0) | |
243 | return err; | |
244 | if (res) { | |
f58e2fce | 245 | dev_warn(chip->card->dev, "error in set_clock %d\n", res); |
d43f3010 TI |
246 | return -EINVAL; |
247 | } | |
248 | return 0; | |
249 | } | |
250 | ||
251 | bool lola_update_ext_clock_freq(struct lola *chip, unsigned int val) | |
252 | { | |
253 | unsigned int tag; | |
254 | ||
255 | /* the current EXTERNAL clock information gets updated by interrupt | |
256 | * with an unsolicited response | |
257 | */ | |
258 | if (!val) | |
259 | return false; | |
260 | tag = (val >> LOLA_UNSOL_RESP_TAG_OFFSET) & LOLA_UNSOLICITED_TAG_MASK; | |
261 | if (tag != LOLA_UNSOLICITED_TAG) | |
262 | return false; | |
263 | ||
264 | /* only for current = external clocks */ | |
265 | if (chip->clock.sample_clock[chip->clock.cur_index].type != | |
266 | LOLA_CLOCK_TYPE_INTERNAL) { | |
1c5d7b31 | 267 | chip->clock.cur_freq = lola_sample_rate_convert(val & 0x7f); |
d43f3010 TI |
268 | chip->clock.cur_valid = (val & 0x100) != 0; |
269 | } | |
270 | return true; | |
271 | } | |
272 | ||
273 | int lola_set_clock(struct lola *chip, int idx) | |
274 | { | |
275 | int freq = 0; | |
276 | bool valid = false; | |
277 | ||
278 | if (idx == chip->clock.cur_index) { | |
279 | /* current clock is allowed */ | |
280 | freq = chip->clock.cur_freq; | |
281 | valid = chip->clock.cur_valid; | |
282 | } else if (chip->clock.sample_clock[idx].type == | |
283 | LOLA_CLOCK_TYPE_INTERNAL) { | |
284 | /* internal clocks allowed */ | |
285 | freq = chip->clock.sample_clock[idx].freq; | |
286 | valid = true; | |
287 | } | |
288 | ||
289 | if (!freq || !valid) | |
290 | return -EINVAL; | |
291 | ||
292 | if (!check_gran_clock_compatibility(chip, chip->granularity, freq)) | |
293 | return -EINVAL; | |
294 | ||
295 | if (idx != chip->clock.cur_index) { | |
296 | int err = lola_set_clock_index(chip, idx); | |
297 | if (err < 0) | |
298 | return err; | |
299 | /* update new settings */ | |
300 | chip->clock.cur_index = idx; | |
301 | chip->clock.cur_freq = freq; | |
302 | chip->clock.cur_valid = true; | |
303 | } | |
304 | return 0; | |
305 | } | |
306 | ||
307 | int lola_set_sample_rate(struct lola *chip, int rate) | |
308 | { | |
309 | int i; | |
310 | ||
311 | if (chip->clock.cur_freq == rate && chip->clock.cur_valid) | |
312 | return 0; | |
313 | /* search for new dwClockIndex */ | |
314 | for (i = 0; i < chip->clock.items; i++) { | |
315 | if (chip->clock.sample_clock[i].type == LOLA_CLOCK_TYPE_INTERNAL && | |
316 | chip->clock.sample_clock[i].freq == rate) | |
317 | break; | |
318 | } | |
319 | if (i >= chip->clock.items) | |
320 | return -EINVAL; | |
321 | return lola_set_clock(chip, i); | |
322 | } | |
323 |