source: mainline/uspace/drv/audio/hdaudio/codec.c@ 1e92bc3

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1e92bc3 was 1e92bc3, checked in by Jiri Svoboda <jiri@…>, 11 years ago

Enable interrupt in IRC and max all input and output amps.

  • Property mode set to 100644
File size: 8.7 KB
Line 
1/*
2 * Copyright (c) 2014 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup hdaudio
30 * @{
31 */
32/** @file High Definition Audio codec
33 */
34
35#include <async.h>
36#include <bitops.h>
37#include <ddf/log.h>
38#include <errno.h>
39#include <stdlib.h>
40
41#include "codec.h"
42#include "hdactl.h"
43#include "spec/codec.h"
44#include "spec/fmt.h"
45#include "stream.h"
46
47static int hda_ccmd(hda_codec_t *codec, int node, uint32_t vid, uint32_t payload,
48 uint32_t *resp)
49{
50 uint32_t verb;
51
52 verb = (codec->address << 28) | (node << 20) | (vid << 8) | payload;
53 return hda_cmd(codec->hda, verb, resp);
54}
55
56static int hda_get_parameter(hda_codec_t *codec, int node, hda_param_id_t param,
57 uint32_t *resp)
58{
59 return hda_ccmd(codec, node, hda_param_get, param, resp);
60}
61
62static int hda_get_subnc(hda_codec_t *codec, int node, int *startnode,
63 int *nodecount)
64{
65 int rc;
66 uint32_t resp;
67
68 rc = hda_get_parameter(codec, node, hda_sub_nc, &resp);
69 if (rc != EOK)
70 return rc;
71
72 *startnode = BIT_RANGE_EXTRACT(uint32_t, subnc_startnode_h,
73 subnc_startnode_l, resp);
74 *nodecount = BIT_RANGE_EXTRACT(uint32_t, subnc_nodecount_h,
75 subnc_nodecount_l, resp);
76
77 return EOK;
78}
79
80/** Get Function Group Type */
81static int hda_get_fgrp_type(hda_codec_t *codec, int node, bool *unsol,
82 hda_fgrp_type_t *type)
83{
84 int rc;
85 uint32_t resp;
86
87 rc = hda_get_parameter(codec, node, hda_fgrp_type, &resp);
88 if (rc != EOK)
89 return rc;
90
91 *unsol = (resp & BIT_V(uint32_t, fgrpt_unsol)) != 0;
92 *type = BIT_RANGE_EXTRACT(uint32_t, fgrpt_type_h, fgrpt_type_l, resp);
93
94 return EOK;
95}
96
97/** Get Suppported PCM Size, Rates */
98static int hda_get_supp_rates(hda_codec_t *codec, int node, uint32_t *rates)
99{
100 return hda_get_parameter(codec, node, hda_supp_rates, rates);
101}
102
103/** Get Suppported Stream Formats */
104static int hda_get_supp_formats(hda_codec_t *codec, int node, uint32_t *fmts)
105{
106 return hda_get_parameter(codec, node, hda_supp_formats, fmts);
107}
108
109static int hda_set_converter_fmt(hda_codec_t *codec, int node, uint16_t fmt)
110{
111 return hda_ccmd(codec, node, hda_converter_fmt_set, fmt, NULL);
112}
113
114static int hda_set_converter_ctl(hda_codec_t *codec, int node, uint8_t stream,
115 uint8_t channel)
116{
117 uint32_t ctl;
118
119 ctl = (stream << cctl_stream_l) | (channel << cctl_channel_l);
120 return hda_ccmd(codec, node, hda_converter_ctl_set, ctl, NULL);
121}
122
123/** Get Audio Widget Capabilities */
124static int hda_get_aw_caps(hda_codec_t *codec, int node,
125 hda_awidget_type_t *type, uint32_t *caps)
126{
127 int rc;
128 uint32_t resp;
129
130 rc = hda_get_parameter(codec, node, hda_aw_caps, &resp);
131 if (rc != EOK)
132 return rc;
133
134 *type = BIT_RANGE_EXTRACT(uint32_t, awc_type_h, awc_type_l, resp);
135 *caps = resp;
136
137 return EOK;
138}
139
140/** Get Configuration Default */
141static int hda_get_cfg_def(hda_codec_t *codec, int node, uint32_t *cfgdef)
142{
143 return hda_ccmd(codec, node, hda_cfg_def_get, 0, cfgdef);
144}
145
146/** Get Amplifier Gain / Mute */
147static int hda_get_amp_gain_mute(hda_codec_t *codec, int node, uint16_t payload,
148 uint32_t *resp)
149{
150 return hda_ccmd(codec, node, hda_amp_gain_mute_get, payload, resp);
151}
152
153static int hda_set_amp_gain_mute(hda_codec_t *codec, int node, uint16_t payload)
154{
155 return hda_ccmd(codec, node, hda_amp_gain_mute_set, payload, NULL);
156}
157
158static int hda_set_out_amp_max(hda_codec_t *codec, uint8_t aw)
159{
160 uint32_t ampcaps;
161 uint32_t gmleft, gmright;
162 uint32_t offset;
163 int rc;
164
165 rc = hda_get_parameter(codec, aw,
166 hda_out_amp_caps, &ampcaps);
167 if (rc != EOK)
168 goto error;
169
170 rc = hda_get_amp_gain_mute(codec, aw, 0x8000, &gmleft);
171 if (rc != EOK)
172 goto error;
173
174 rc = hda_get_amp_gain_mute(codec, aw, 0xc000, &gmright);
175 if (rc != EOK)
176 goto error;
177
178 ddf_msg(LVL_NOTE, "out amp caps 0x%x "
179 "gain/mute: L:0x%x R:0x%x",
180 ampcaps, gmleft, gmright);
181 offset = ampcaps & 0x7f;
182
183 rc = hda_set_amp_gain_mute(codec, aw, 0xb000 + offset);
184 if (rc != EOK)
185 goto error;
186
187 return EOK;
188error:
189 return rc;
190}
191
192static int hda_set_in_amp_max(hda_codec_t *codec, uint8_t aw)
193{
194 uint32_t ampcaps;
195 uint32_t gmleft, gmright;
196 uint32_t offset;
197 int i;
198 int rc;
199
200 rc = hda_get_parameter(codec, aw,
201 hda_out_amp_caps, &ampcaps);
202 if (rc != EOK)
203 goto error;
204
205 ddf_msg(LVL_NOTE, "in amp caps 0x%x ", ampcaps);
206 offset = ampcaps & 0x7f;
207
208 for (i = 0; i < 15; i++) {
209 rc = hda_get_amp_gain_mute(codec, aw, 0x0000 + i, &gmleft);
210 if (rc != EOK)
211 goto error;
212
213 rc = hda_get_amp_gain_mute(codec, aw, 0x4000 + i, &gmright);
214 if (rc != EOK)
215 goto error;
216
217 ddf_msg(LVL_NOTE, "in:%d gain/mute: L:0x%x R:0x%x",
218 i, gmleft, gmright);
219
220 rc = hda_set_amp_gain_mute(codec, aw, 0x7000 + (i << 8) + offset);
221 if (rc != EOK)
222 goto error;
223 }
224
225 return EOK;
226error:
227 return rc;
228}
229
230hda_codec_t *hda_codec_init(hda_t *hda, uint8_t address)
231{
232 hda_codec_t *codec;
233 int rc;
234 int sfg, nfg;
235 int saw, naw;
236 int fg, aw;
237 bool unsol;
238 hda_fgrp_type_t grptype;
239 hda_awidget_type_t awtype;
240 uint32_t awcaps;
241 uint32_t cfgdef;
242 uint32_t rates;
243 uint32_t formats;
244
245 codec = calloc(1, sizeof(hda_codec_t));
246 if (codec == NULL)
247 return NULL;
248
249 codec->hda = hda;
250 codec->address = address;
251
252 rc = hda_get_subnc(codec, 0, &sfg, &nfg);
253 if (rc != EOK)
254 goto error;
255
256 ddf_msg(LVL_NOTE, "hda_get_subnc -> %d", rc);
257 ddf_msg(LVL_NOTE, "sfg=%d nfg=%d", sfg, nfg);
258
259 for (fg = sfg; fg < sfg + nfg; fg++) {
260 ddf_msg(LVL_NOTE, "Enumerate FG %d", fg);
261
262 rc = hda_get_fgrp_type(codec, fg, &unsol, &grptype);
263 if (rc != EOK)
264 goto error;
265
266 ddf_msg(LVL_NOTE, "hda_get_fgrp_type -> %d", rc);
267 ddf_msg(LVL_NOTE, "unsol: %d, grptype: %d", unsol, grptype);
268
269 rc = hda_get_subnc(codec, fg, &saw, &naw);
270 if (rc != EOK)
271 goto error;
272
273 ddf_msg(LVL_NOTE, "hda_get_subnc -> %d", rc);
274 ddf_msg(LVL_NOTE, "saw=%d baw=%d", saw, naw);
275
276 for (aw = saw; aw < saw + naw; aw++) {
277 rc = hda_get_aw_caps(codec, aw, &awtype, &awcaps);
278 if (rc != EOK)
279 goto error;
280 ddf_msg(LVL_NOTE, "aw %d: type=0x%x caps=0x%x",
281 aw, awtype, awcaps);
282
283 if (awtype == awt_pin_complex) {
284 rc = hda_get_cfg_def(codec, aw, &cfgdef);
285 if (rc != EOK)
286 goto error;
287 ddf_msg(LVL_NOTE, "aw %d: PIN cdfgef=0x%x",
288 aw, cfgdef);
289
290 } else if (awtype == awt_audio_output) {
291 codec->out_aw = aw;
292 codec->out_aw_list[codec->out_aw_num++] = aw;
293 }
294
295 if ((awcaps & BIT_V(uint32_t, awc_out_amp_present)) != 0)
296 hda_set_out_amp_max(codec, aw);
297
298 if ((awcaps & BIT_V(uint32_t, awc_in_amp_present)) != 0)
299 hda_set_in_amp_max(codec, aw);
300 }
301 }
302
303 rc = hda_get_supp_rates(codec, codec->out_aw, &rates);
304 if (rc != EOK)
305 goto error;
306
307 rc = hda_get_supp_formats(codec, codec->out_aw, &formats);
308 if (rc != EOK)
309 goto error;
310
311 ddf_msg(LVL_NOTE, "Output widget %d: rates=0x%x formats=0x%x",
312 codec->out_aw, rates, formats);
313
314 ddf_msg(LVL_NOTE, "Codec OK");
315 return codec;
316error:
317 free(codec);
318 return NULL;
319}
320
321void hda_codec_fini(hda_codec_t *codec)
322{
323 ddf_msg(LVL_NOTE, "hda_codec_fini()");
324 free(codec);
325}
326
327int hda_out_converter_setup(hda_codec_t *codec, uint8_t sid)
328{
329 int rc;
330 int out_aw;
331 int i;
332
333 for (i = 0; i < codec->out_aw_num; i++) {
334 out_aw = codec->out_aw_list[i];
335
336 /* XXX Choose appropriate parameters */
337 uint32_t fmt;
338 /* 48 kHz, 16-bits, 1 channel */
339 fmt = (fmt_base_44khz << fmt_base) | (fmt_bits_16 << fmt_bits_l);
340
341 /* Configure converter */
342
343 ddf_msg(LVL_NOTE, "Configure converter format");
344 rc = hda_set_converter_fmt(codec, out_aw, fmt);
345 if (rc != EOK)
346 goto error;
347
348 ddf_msg(LVL_NOTE, "Configure converter stream, channel");
349 rc = hda_set_converter_ctl(codec, out_aw, sid, 0);
350 if (rc != EOK)
351 goto error;
352 }
353
354 return EOK;
355error:
356 return rc;
357}
358
359/** @}
360 */
Note: See TracBrowser for help on using the repository browser.