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 |
|
---|
47 | static 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 |
|
---|
56 | static 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 |
|
---|
62 | static 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 */
|
---|
81 | static 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 */
|
---|
98 | static 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 */
|
---|
104 | static 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 |
|
---|
109 | static 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 |
|
---|
114 | static 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 */
|
---|
124 | static 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 */
|
---|
141 | static 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 */
|
---|
147 | static 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 |
|
---|
153 | static 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 |
|
---|
158 | hda_codec_t *hda_codec_init(hda_t *hda, uint8_t address)
|
---|
159 | {
|
---|
160 | hda_codec_t *codec;
|
---|
161 | int rc;
|
---|
162 | int sfg, nfg;
|
---|
163 | int saw, naw;
|
---|
164 | int fg, aw;
|
---|
165 | bool unsol;
|
---|
166 | hda_fgrp_type_t grptype;
|
---|
167 | hda_awidget_type_t awtype;
|
---|
168 | uint32_t awcaps;
|
---|
169 | uint32_t cfgdef;
|
---|
170 | uint32_t rates;
|
---|
171 | uint32_t formats;
|
---|
172 |
|
---|
173 | codec = calloc(1, sizeof(hda_codec_t));
|
---|
174 | if (codec == NULL)
|
---|
175 | return NULL;
|
---|
176 |
|
---|
177 | codec->hda = hda;
|
---|
178 | codec->address = address;
|
---|
179 |
|
---|
180 | rc = hda_get_subnc(codec, 0, &sfg, &nfg);
|
---|
181 | if (rc != EOK)
|
---|
182 | goto error;
|
---|
183 |
|
---|
184 | ddf_msg(LVL_NOTE, "hda_get_subnc -> %d", rc);
|
---|
185 | ddf_msg(LVL_NOTE, "sfg=%d nfg=%d", sfg, nfg);
|
---|
186 |
|
---|
187 | for (fg = sfg; fg < sfg + nfg; fg++) {
|
---|
188 | ddf_msg(LVL_NOTE, "Enumerate FG %d", fg);
|
---|
189 |
|
---|
190 | rc = hda_get_fgrp_type(codec, fg, &unsol, &grptype);
|
---|
191 | if (rc != EOK)
|
---|
192 | goto error;
|
---|
193 |
|
---|
194 | ddf_msg(LVL_NOTE, "hda_get_fgrp_type -> %d", rc);
|
---|
195 | ddf_msg(LVL_NOTE, "unsol: %d, grptype: %d", unsol, grptype);
|
---|
196 |
|
---|
197 | rc = hda_get_subnc(codec, fg, &saw, &naw);
|
---|
198 | if (rc != EOK)
|
---|
199 | goto error;
|
---|
200 |
|
---|
201 | ddf_msg(LVL_NOTE, "hda_get_subnc -> %d", rc);
|
---|
202 | ddf_msg(LVL_NOTE, "saw=%d baw=%d", saw, naw);
|
---|
203 |
|
---|
204 | for (aw = saw; aw < saw + naw; aw++) {
|
---|
205 | rc = hda_get_aw_caps(codec, aw, &awtype, &awcaps);
|
---|
206 | if (rc != EOK)
|
---|
207 | goto error;
|
---|
208 | ddf_msg(LVL_NOTE, "aw %d: type=0x%x caps=0x%x",
|
---|
209 | aw, awtype, awcaps);
|
---|
210 |
|
---|
211 | if (awtype == awt_pin_complex) {
|
---|
212 | rc = hda_get_cfg_def(codec, aw, &cfgdef);
|
---|
213 | if (rc != EOK)
|
---|
214 | goto error;
|
---|
215 | ddf_msg(LVL_NOTE, "aw %d: PIN cdfgef=0x%x",
|
---|
216 | aw, cfgdef);
|
---|
217 |
|
---|
218 | } else if (awtype == awt_audio_output) {
|
---|
219 | codec->out_aw = aw;
|
---|
220 | }
|
---|
221 |
|
---|
222 | if ((awcaps & BIT_V(uint32_t, awc_out_amp_present)) != 0) {
|
---|
223 | uint32_t ampcaps;
|
---|
224 | uint32_t gmleft, gmright;
|
---|
225 |
|
---|
226 | rc = hda_get_parameter(codec, aw,
|
---|
227 | hda_out_amp_caps, &caps);
|
---|
228 | if (rc != EOK)
|
---|
229 | goto error;
|
---|
230 |
|
---|
231 | rc = hda_get_amp_gain_mute(codec, aw, 0x8000, &gmleft);
|
---|
232 | if (rc != EOK)
|
---|
233 | goto error;
|
---|
234 |
|
---|
235 | rc = hda_get_amp_gain_mute(codec, aw, 0xc000, &gmright);
|
---|
236 | if (rc != EOK)
|
---|
237 | goto error;
|
---|
238 |
|
---|
239 | ddf_msg(LVL_NOTE, "out amp caps 0x%x "
|
---|
240 | "gain/mute: L:0x%x R:0x%x",
|
---|
241 | ampcaps, gmleft, gmright);
|
---|
242 |
|
---|
243 | rc = hda_set_amp_gain_mute(codec, aw, 0xb04a);
|
---|
244 | if (rc != EOK)
|
---|
245 | goto error;
|
---|
246 | }
|
---|
247 | }
|
---|
248 | }
|
---|
249 |
|
---|
250 | rc = hda_get_supp_rates(codec, codec->out_aw, &rates);
|
---|
251 | if (rc != EOK)
|
---|
252 | goto error;
|
---|
253 |
|
---|
254 | rc = hda_get_supp_formats(codec, codec->out_aw, &formats);
|
---|
255 | if (rc != EOK)
|
---|
256 | goto error;
|
---|
257 |
|
---|
258 | ddf_msg(LVL_NOTE, "Output widget %d: rates=0x%x formats=0x%x",
|
---|
259 | codec->out_aw, rates, formats);
|
---|
260 |
|
---|
261 | /* XXX Choose appropriate parameters */
|
---|
262 | uint32_t fmt;
|
---|
263 | /* 48 kHz, 16-bits, 1 channel */
|
---|
264 | fmt = fmt_bits_16 << fmt_bits_l;
|
---|
265 |
|
---|
266 | /* Create stream */
|
---|
267 | ddf_msg(LVL_NOTE, "Create stream");
|
---|
268 | hda_stream_t *stream;
|
---|
269 | stream = hda_stream_create(hda, sdir_output, fmt);
|
---|
270 | if (stream == NULL)
|
---|
271 | goto error;
|
---|
272 |
|
---|
273 | /* Configure converter */
|
---|
274 |
|
---|
275 | ddf_msg(LVL_NOTE, "Configure converter format");
|
---|
276 | rc = hda_set_converter_fmt(codec, codec->out_aw, fmt);
|
---|
277 | if (rc != EOK)
|
---|
278 | goto error;
|
---|
279 |
|
---|
280 | ddf_msg(LVL_NOTE, "Configure converter stream, channel");
|
---|
281 | rc = hda_set_converter_ctl(codec, codec->out_aw, stream->sid, 0);
|
---|
282 | if (rc != EOK)
|
---|
283 | goto error;
|
---|
284 |
|
---|
285 | ddf_msg(LVL_NOTE, "Start stream");
|
---|
286 | hda_stream_start(stream);
|
---|
287 |
|
---|
288 | ddf_msg(LVL_NOTE, "Codec OK");
|
---|
289 | return codec;
|
---|
290 | error:
|
---|
291 | free(codec);
|
---|
292 | return NULL;
|
---|
293 | }
|
---|
294 |
|
---|
295 | void hda_codec_fini(hda_codec_t *codec)
|
---|
296 | {
|
---|
297 | ddf_msg(LVL_NOTE, "hda_codec_fini()");
|
---|
298 | free(codec);
|
---|
299 | }
|
---|
300 |
|
---|
301 | /** @}
|
---|
302 | */
|
---|