source: mainline/uspace/drv/audio/hdaudio/hdaudio.c@ eb13ef8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since eb13ef8 was eb13ef8, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Change IPC_GET_* and IPC_SET_* to accept pointer instead of lvalue

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