source: mainline/uspace/drv/audio/hdaudio/hdaudio.c@ 6747b929

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

Garbled playback via hound.

  • Property mode set to 100644
File size: 8.9 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 driver
33 */
34
35#include <assert.h>
36#include <bitops.h>
37#include <ddi.h>
38#include <device/hw_res_parsed.h>
39#include <stdio.h>
40#include <errno.h>
41#include <str_error.h>
42#include <ddf/driver.h>
43#include <ddf/interrupt.h>
44#include <ddf/log.h>
45
46#include "hdactl.h"
47#include "hdaudio.h"
48#include "pcm_iface.h"
49#include "spec/regs.h"
50
51#define NAME "hdaudio"
52
53static int hda_dev_add(ddf_dev_t *dev);
54static int hda_dev_remove(ddf_dev_t *dev);
55static int hda_dev_gone(ddf_dev_t *dev);
56static int hda_fun_online(ddf_fun_t *fun);
57static int hda_fun_offline(ddf_fun_t *fun);
58
59static void hdaudio_interrupt(ddf_dev_t *, ipc_callid_t, ipc_call_t *);
60
61static driver_ops_t driver_ops = {
62 .dev_add = &hda_dev_add,
63 .dev_remove = &hda_dev_remove,
64 .dev_gone = &hda_dev_gone,
65 .fun_online = &hda_fun_online,
66 .fun_offline = &hda_fun_offline
67};
68
69static driver_t hda_driver = {
70 .name = NAME,
71 .driver_ops = &driver_ops
72};
73
74ddf_dev_ops_t hda_pcm_ops = {
75 .interfaces[AUDIO_PCM_BUFFER_IFACE] = &hda_pcm_iface
76};
77
78irq_pio_range_t hdaudio_irq_pio_ranges[] = {
79 {
80 .base = 0,
81 .size = 8192
82 }
83};
84
85irq_cmd_t hdaudio_irq_commands[] = {
86 /* 0 */
87 {
88 .cmd = CMD_PIO_READ_8,
89 .addr = NULL, /* rirbsts */
90 .dstarg = 2
91 },
92 /* 1 */
93 {
94 .cmd = CMD_AND,
95 .value = BIT_V(uint8_t, rirbsts_intfl),
96 .srcarg = 2,
97 .dstarg = 3
98 },
99 /* 2 */
100 {
101 .cmd = CMD_PREDICATE,
102 .value = 2,
103 .srcarg = 3
104 },
105 /* 3 */
106 {
107 .cmd = CMD_PIO_WRITE_8,
108 .addr = NULL, /* rirbsts */
109 .value = BIT_V(uint8_t, rirbsts_intfl)
110 },
111 /* 4 */
112 {
113 .cmd = CMD_ACCEPT
114 }
115};
116
117irq_cmd_t hdaudio_irq_commands_sdesc[] = {
118 /* 0 */
119 {
120 .cmd = CMD_PIO_READ_32,
121 .addr = NULL, /* intsts */
122 .dstarg = 2
123 },
124 /* 1 */
125 {
126 .cmd = CMD_AND,
127 .value = 0, /* 1 << idx */
128 .srcarg = 2,
129 .dstarg = 3,
130 },
131 /* 2 */
132 {
133 .cmd = CMD_PREDICATE,
134 .value = 2,
135 .srcarg = 3
136 },
137 /* 3 */
138 {
139 .cmd = CMD_PIO_WRITE_8,
140 .addr = NULL, /* sdesc[x].sts */
141 .value = 0x4 /* XXX sdesc.sts.BCIS */
142 },
143 /* 4 */
144 {
145 .cmd = CMD_ACCEPT
146 }
147};
148
149static int hda_dev_add(ddf_dev_t *dev)
150{
151 ddf_fun_t *fun_pcm;
152 hda_t *hda = NULL;
153 hw_res_list_parsed_t res;
154 irq_code_t irq_code;
155 irq_cmd_t *cmds;
156 size_t ncmds_base;
157 size_t ncmds_sdesc;
158 size_t ncmds;
159 int i;
160 void *regs;
161 int rc;
162
163 ddf_msg(LVL_NOTE, "hda_dev_add()");
164
165 hda = ddf_dev_data_alloc(dev, sizeof(hda_t));
166 if (hda == NULL) {
167 ddf_msg(LVL_ERROR, "Failed allocating soft state.\n");
168 rc = ENOMEM;
169 goto error;
170 }
171
172 ddf_msg(LVL_NOTE, "create parent sess");
173 hda->parent_sess = ddf_dev_parent_sess_create(dev,
174 EXCHANGE_SERIALIZE);
175 if (hda->parent_sess == NULL) {
176 ddf_msg(LVL_ERROR, "Failed connecting parent driver.\n");
177 rc = ENOMEM;
178 goto error;
179 }
180
181 ddf_msg(LVL_NOTE, "get HW res list");
182 hw_res_list_parsed_init(&res);
183 rc = hw_res_get_list_parsed(hda->parent_sess, &res, 0);
184 if (rc != EOK) {
185 ddf_msg(LVL_ERROR, "Failed getting resource list.\n");
186 goto error;
187 }
188
189 if (res.mem_ranges.count != 1) {
190 ddf_msg(LVL_ERROR, "Expected exactly one memory range.\n");
191 rc = EINVAL;
192 goto error;
193 }
194
195 hda->rwbase = RNGABS(res.mem_ranges.ranges[0]);
196 hda->rwsize = RNGSZ(res.mem_ranges.ranges[0]);
197
198 ddf_msg(LVL_NOTE, "hda reg base: %" PRIx64,
199 RNGABS(res.mem_ranges.ranges[0]));
200
201 if (hda->rwsize < sizeof(hda_regs_t)) {
202 ddf_msg(LVL_ERROR, "Memory range is too small.");
203 rc = EINVAL;
204 goto error;
205 }
206
207 ddf_msg(LVL_NOTE, "enable PIO");
208 rc = pio_enable((void *)(uintptr_t)hda->rwbase, hda->rwsize, &regs);
209 if (rc != EOK) {
210 ddf_msg(LVL_ERROR, "Error enabling PIO range.");
211 goto error;
212 }
213
214 hda->regs = (hda_regs_t *)regs;
215
216 ddf_msg(LVL_NOTE, "IRQs: %d", res.irqs.count);
217 if (res.irqs.count != 1) {
218 ddf_msg(LVL_ERROR, "Unexpected IRQ count %d (!= 1)",
219 res.irqs.count);
220 goto error;
221 }
222 ddf_msg(LVL_NOTE, "interrupt no: %d", res.irqs.irqs[0]);
223
224 ncmds_base = sizeof(hdaudio_irq_commands) / sizeof(irq_cmd_t);
225 ncmds_sdesc = sizeof(hdaudio_irq_commands_sdesc) / sizeof(irq_cmd_t);
226 ncmds = ncmds_base + 30 * ncmds_sdesc;
227
228 cmds = calloc(ncmds, sizeof(irq_cmd_t));
229 if (cmds == NULL) {
230 ddf_msg(LVL_ERROR, "Out of memory");
231 goto error;
232 }
233
234 irq_code.rangecount = sizeof(hdaudio_irq_pio_ranges) /
235 sizeof(irq_pio_range_t);
236 irq_code.ranges = hdaudio_irq_pio_ranges;
237 irq_code.cmdcount = ncmds;
238 irq_code.cmds = cmds;
239
240 hda_regs_t *rphys = (hda_regs_t *)(uintptr_t)hda->rwbase;
241 hdaudio_irq_pio_ranges[0].base = (uintptr_t)hda->rwbase;
242
243 memcpy(cmds, hdaudio_irq_commands, sizeof(hdaudio_irq_commands));
244 cmds[0].addr = (void *)&rphys->rirbsts;
245 cmds[3].addr = (void *)&rphys->rirbsts;
246
247 for (i = 0; i < 30; i++) {
248 memcpy(&cmds[ncmds_base + i * ncmds_sdesc],
249 hdaudio_irq_commands_sdesc, sizeof(hdaudio_irq_commands_sdesc));
250 cmds[ncmds_base + i * ncmds_sdesc + 0].addr = (void *)&rphys->intsts;
251 cmds[ncmds_base + i * ncmds_sdesc + 1].value = BIT_V(uint32_t, i);
252 cmds[ncmds_base + i * ncmds_sdesc + 3].addr = (void *)&rphys->sdesc[i].sts;
253 }
254
255 ddf_msg(LVL_NOTE, "range0.base=%x", hdaudio_irq_pio_ranges[0].base);
256
257 rc = register_interrupt_handler(dev, res.irqs.irqs[0],
258 hdaudio_interrupt, &irq_code);
259 if (rc != EOK) {
260 ddf_msg(LVL_ERROR, "Failed registering interrupt handler. (%d)",
261 rc);
262 goto error;
263 }
264
265 if (hda_ctl_init(hda) == NULL) {
266 rc = EIO;
267 goto error;
268 }
269
270 ddf_msg(LVL_NOTE, "create function");
271 fun_pcm = ddf_fun_create(dev, fun_exposed, "pcm");
272 if (fun_pcm == NULL) {
273 ddf_msg(LVL_ERROR, "Failed creating function 'pcm'.");
274 rc = ENOMEM;
275 goto error;
276 }
277
278 hda->fun_pcm = fun_pcm;
279
280 ddf_fun_set_ops(fun_pcm, &hda_pcm_ops);
281
282 rc = ddf_fun_bind(fun_pcm);
283 if (rc != EOK) {
284 ddf_msg(LVL_ERROR, "Failed binding function 'pcm'.");
285 ddf_fun_destroy(fun_pcm);
286 goto error;
287 }
288
289 ddf_fun_add_to_category(fun_pcm, "audio-pcm");
290 return EOK;
291error:
292 if (hda != NULL) {
293 if (hda->ctl != NULL)
294 hda_ctl_fini(hda->ctl);
295 }
296
297 ddf_msg(LVL_NOTE, "Failing hda_dev_add() -> %d", rc);
298 return rc;
299}
300
301static int hda_dev_remove(ddf_dev_t *dev)
302{
303 hda_t *hda = (hda_t *)ddf_dev_data_get(dev);
304 int rc;
305
306 ddf_msg(LVL_DEBUG, "hda_dev_remove(%p)", dev);
307
308 if (hda->fun_pcm != NULL) {
309 rc = ddf_fun_offline(hda->fun_pcm);
310 if (rc != EOK)
311 return rc;
312
313 rc = ddf_fun_unbind(hda->fun_pcm);
314 if (rc != EOK)
315 return rc;
316 }
317
318 return EOK;
319}
320
321static int hda_dev_gone(ddf_dev_t *dev)
322{
323 hda_t *hda = (hda_t *)ddf_dev_data_get(dev);
324 int rc;
325
326 ddf_msg(LVL_DEBUG, "hda_dev_remove(%p)", dev);
327
328 if (hda->fun_pcm != NULL) {
329 rc = ddf_fun_unbind(hda->fun_pcm);
330 if (rc != EOK)
331 return rc;
332 }
333
334 return EOK;
335}
336
337static int hda_fun_online(ddf_fun_t *fun)
338{
339 ddf_msg(LVL_DEBUG, "hda_fun_online()");
340 return ddf_fun_online(fun);
341}
342
343static int hda_fun_offline(ddf_fun_t *fun)
344{
345 ddf_msg(LVL_DEBUG, "hda_fun_offline()");
346 return ddf_fun_offline(fun);
347}
348
349static void hdaudio_interrupt(ddf_dev_t *dev, ipc_callid_t iid,
350 ipc_call_t *icall)
351{
352 hda_t *hda = (hda_t *)ddf_dev_data_get(dev);
353
354 if (0) ddf_msg(LVL_NOTE, "## interrupt ##");
355// ddf_msg(LVL_NOTE, "interrupt arg4=0x%x", (int)IPC_GET_ARG4(*icall));
356 if (IPC_GET_ARG3(*icall) != 0) {
357 hda_pcm_event(hda, PCM_EVENT_FRAMES_PLAYED);
358 hda_pcm_event(hda, PCM_EVENT_FRAMES_PLAYED);
359 hda_pcm_event(hda, PCM_EVENT_FRAMES_PLAYED);
360 hda_pcm_event(hda, PCM_EVENT_FRAMES_PLAYED);
361/* hda_pcm_event(hda, PCM_EVENT_FRAMES_PLAYED);
362 hda_pcm_event(hda, PCM_EVENT_FRAMES_PLAYED);
363 hda_pcm_event(hda, PCM_EVENT_FRAMES_PLAYED);
364 hda_pcm_event(hda, PCM_EVENT_FRAMES_PLAYED);*/
365 }
366 hda_ctl_interrupt(hda->ctl);
367}
368
369int main(int argc, char *argv[])
370{
371 printf(NAME ": High Definition Audio driver\n");
372 ddf_log_init(NAME);
373 return ddf_driver_main(&hda_driver);
374}
375
376/** @}
377 */
Note: See TracBrowser for help on using the repository browser.