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