source: mainline/uspace/drv/audio/hdaudio/hdactl.c@ 7978d1e7

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

Initialize CORB and RIRB.

  • Property mode set to 100644
File size: 8.5 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 controller
33 */
34
[7978d1e7]35#include <as.h>
36#include <async.h>
37#include <bitops.h>
[b229062]38#include <ddf/log.h>
[7978d1e7]39#include <ddi.h>
40#include <errno.h>
41#include <macros.h>
[b229062]42#include <stdint.h>
43
44#include "hdactl.h"
45#include "regif.h"
[7978d1e7]46#include "spec/regs.h"
47
48enum {
49 ctrl_init_wait_max = 10,
50 codec_enum_wait_us = 512
51};
52
53/** Select an appropriate CORB/RIRB size.
54 *
55 * We always use the largest available size. In @a sizecap each of bits
56 * 0, 1, 2 determine whether one of the supported size (0 == 2 enries,
57 * 1 == 16 entries, 2 == 256 entries) is supported. @a *selsz is set to
58 * one of 0, 1, 2 on success.
59 *
60 * @param sizecap CORB/RIRB Size Capability
61 * @param selsz Place to store CORB/RIRB Size
62 * @return EOK on success, EINVAL if sizecap has no valid bits set
63 *
64 */
65static int hda_rb_size_select(uint8_t sizecap, uint8_t *selsz)
66{
67 int i;
68
69 for (i = 2; i >= 0; --i) {
70 if ((sizecap & BIT_V(uint8_t, i)) != 0) {
71 *selsz = i;
72 return EOK;
73 }
74 }
75
76 return EINVAL;
77}
78
79static size_t hda_rb_entries(uint8_t selsz)
80{
81 switch (selsz) {
82 case 0:
83 return 2;
84 case 1:
85 return 16;
86 case 2:
87 return 256;
88 default:
89 assert(false);
90 return 0;
91 }
92}
93
94/** Initialize the CORB */
95static int hda_corb_init(hda_t *hda)
96{
97 uint8_t ctl;
98 uint8_t corbsz;
99 uint8_t sizecap;
100 uint8_t selsz;
101 bool ok64bit;
102 int rc;
103
104 ddf_msg(LVL_NOTE, "hda_corb_init()");
105
106 /* Stop CORB if not stopped. */
107 ctl = hda_reg8_read(&hda->regs->corbctl);
108 if ((ctl & BIT_V(uint8_t, corbctl_run)) != 0) {
109 ddf_msg(LVL_NOTE, "CORB is enabled, disabling first.");
110 hda_reg8_write(&hda->regs->corbctl, ctl & ~BIT_V(uint8_t,
111 corbctl_run));
112 }
113
114 /* Determine CORB size and allocate CORB buffer */
115 corbsz = hda_reg8_read(&hda->regs->corbsize);
116 sizecap = BIT_RANGE_EXTRACT(uint8_t, corbsize_cap_h,
117 corbsize_cap_l, corbsz);
118 rc = hda_rb_size_select(sizecap, &selsz);
119 if (rc != EOK) {
120 ddf_msg(LVL_ERROR, "Invalid CORB Size Capability");
121 goto error;
122 }
123 corbsz = corbsz & ~BIT_RANGE(uint8_t, corbsize_size_h, corbsize_size_l);
124 corbsz = corbsz | selsz;
125
126 ddf_msg(LVL_NOTE, "Setting CORB Size register to 0x%x", corbsz);
127 hda_reg8_write(&hda->regs->corbsize, corbsz);
128 hda->ctl->corb_entries = hda_rb_entries(selsz);
129
130 ddf_msg(LVL_NOTE, "Read GCAP");
131 uint16_t gcap = hda_reg16_read(&hda->regs->gcap);
132 ok64bit = (gcap & BIT_V(uint8_t, gcap_64ok)) != 0;
133 ddf_msg(LVL_NOTE, "GCAP: 0x%x (64OK=%d)", gcap, ok64bit);
134
135 /*
136 * CORB must be aligned to 128 bytes. If 64OK is not set,
137 * it must be within the 32-bit address space.
138 */
139 hda->ctl->corb_virt = AS_AREA_ANY;
140 rc = dmamem_map_anonymous(hda->ctl->corb_entries * sizeof(uint32_t),
141 ok64bit ? 0 : DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0,
142 &hda->ctl->corb_phys, &hda->ctl->corb_virt);
143
144 ddf_msg(LVL_NOTE, "Set CORB base registers");
145
146 /* Update CORB base registers */
147 hda_reg32_write(&hda->regs->corblbase, LOWER32(hda->ctl->corb_phys));
148 hda_reg32_write(&hda->regs->corbubase, UPPER32(hda->ctl->corb_phys));
149
150 ddf_msg(LVL_NOTE, "Rset CORB Read/Write pointers");
151
152 /* Reset CORB Read Pointer */
153 hda_reg16_write(&hda->regs->corbrp, BIT_V(uint16_t, corbrp_rst));
154
155 /* Reset CORB Write Poitner */
156 hda_reg16_write(&hda->regs->corbwp, 0);
157
158 ddf_msg(LVL_NOTE, "CORB initialized");
159 return EOK;
160error:
161 return EIO;
162}
163
164/** Initialize the RIRB */
165static int hda_rirb_init(hda_t *hda)
166{
167 uint8_t ctl;
168 uint8_t rirbsz;
169 uint8_t sizecap;
170 uint8_t selsz;
171 bool ok64bit;
172 int rc;
173
174 ddf_msg(LVL_NOTE, "hda_rirb_init()");
175
176 /* Stop RIRB if not stopped. */
177 ctl = hda_reg8_read(&hda->regs->rirbctl);
178 if ((ctl & BIT_V(uint8_t, rirbctl_run)) != 0) {
179 ddf_msg(LVL_NOTE, "RIRB is enabled, disabling first.");
180 hda_reg8_write(&hda->regs->corbctl, ctl & ~BIT_V(uint8_t,
181 rirbctl_run));
182 }
183
184 /* Determine RIRB size and allocate RIRB buffer */
185 rirbsz = hda_reg8_read(&hda->regs->rirbsize);
186 sizecap = BIT_RANGE_EXTRACT(uint8_t, rirbsize_cap_h,
187 rirbsize_cap_l, rirbsz);
188 rc = hda_rb_size_select(sizecap, &selsz);
189 if (rc != EOK) {
190 ddf_msg(LVL_ERROR, "Invalid RIRB Size Capability");
191 goto error;
192 }
193 rirbsz = rirbsz & ~BIT_RANGE(uint8_t, rirbsize_size_h, rirbsize_size_l);
194 rirbsz = rirbsz | selsz;
195
196 ddf_msg(LVL_NOTE, "Setting RIRB Size register to 0x%x", rirbsz);
197 hda_reg8_write(&hda->regs->rirbsize, rirbsz);
198 hda->ctl->rirb_entries = hda_rb_entries(selsz);
199
200 ddf_msg(LVL_NOTE, "Read GCAP");
201 uint16_t gcap = hda_reg16_read(&hda->regs->gcap);
202 ok64bit = (gcap & BIT_V(uint8_t, gcap_64ok)) != 0;
203 ddf_msg(LVL_NOTE, "GCAP: 0x%x (64OK=%d)", gcap, ok64bit);
204
205 /*
206 * RIRB must be aligned to 128 bytes. If 64OK is not set,
207 * it must be within the 32-bit address space.
208 */
209 hda->ctl->rirb_virt = AS_AREA_ANY;
210 rc = dmamem_map_anonymous(hda->ctl->rirb_entries * sizeof(uint64_t),
211 ok64bit ? 0 : DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0,
212 &hda->ctl->rirb_phys, &hda->ctl->rirb_virt);
213
214 ddf_msg(LVL_NOTE, "Set RIRB base registers");
215
216 /* Update RIRB base registers */
217 hda_reg32_write(&hda->regs->rirblbase, LOWER32(hda->ctl->rirb_phys));
218 hda_reg32_write(&hda->regs->rirbubase, UPPER32(hda->ctl->rirb_phys));
219
220 ddf_msg(LVL_NOTE, "Rset RIRB Write pointer");
221
222 /* Reset RIRB Write Pointer */
223 hda_reg16_write(&hda->regs->rirbwp, BIT_V(uint16_t, rirbwp_rst));
224
225 ddf_msg(LVL_NOTE, "RIRB initialized");
226 return EOK;
227error:
228 return EIO;
229}
[b229062]230
231hda_ctl_t *hda_ctl_init(hda_t *hda)
232{
233 hda_ctl_t *ctl;
[7978d1e7]234 uint32_t gctl;
235 int cnt;
236 int rc;
[b229062]237
238 ctl = calloc(1, sizeof(hda_ctl_t));
239 if (ctl == NULL)
240 return NULL;
241
[7978d1e7]242 hda->ctl = ctl;
243
[b229062]244 uint8_t vmaj = hda_reg8_read(&hda->regs->vmaj);
245 uint8_t vmin = hda_reg8_read(&hda->regs->vmin);
246 ddf_msg(LVL_NOTE, "HDA version %d.%d", vmaj, vmin);
247
248 if (vmaj != 1 || vmin != 0) {
249 ddf_msg(LVL_ERROR, "Unsupported HDA version (%d.%d).",
250 vmaj, vmin);
251 goto error;
252 }
253
[7978d1e7]254 ddf_msg(LVL_NOTE, "reg 0x%zx STATESTS = 0x%x",
255 (void *)&hda->regs->statests - (void *)hda->regs, hda_reg16_read(&hda->regs->statests));
256
257 gctl = hda_reg32_read(&hda->regs->gctl);
258 if ((gctl & BIT_V(uint32_t, gctl_crst)) != 0) {
259 ddf_msg(LVL_NOTE, "Controller not in reset. Resetting.");
260 hda_reg32_write(&hda->regs->gctl, gctl & ~BIT_V(uint32_t, gctl_crst));
261 }
262
263 ddf_msg(LVL_NOTE, "Taking controller out of reset.");
264 hda_reg32_write(&hda->regs->gctl, gctl | BIT_V(uint32_t, gctl_crst));
265
266 /* Wait for CRST to read as 1 */
267 cnt = ctrl_init_wait_max;
268 while (cnt > 0) {
269 gctl = hda_reg32_read(&hda->regs->gctl);
270 if ((gctl & BIT_V(uint32_t, gctl_crst)) != 0) {
271 ddf_msg(LVL_NOTE, "gctl=0x%x", gctl);
272 break;
273 }
274
275 ddf_msg(LVL_NOTE, "Waiting for controller to initialize.");
276 async_usleep(100*1000);
277 --cnt;
278 }
279
280 if (cnt == 0) {
281 ddf_msg(LVL_ERROR, "Timed out waiting for controller to come up.");
282 goto error;
283 }
284
285 ddf_msg(LVL_NOTE, "Controller is out of reset.");
286
287 /* Give codecs enough time to enumerate themselves */
288 async_usleep(codec_enum_wait_us);
289
290 ddf_msg(LVL_NOTE, "STATESTS = 0x%x",
291 hda_reg16_read(&hda->regs->statests));
292
293 rc = hda_corb_init(hda);
294 if (rc != EOK)
295 goto error;
296
297 rc = hda_rirb_init(hda);
298 if (rc != EOK)
299 goto error;
300
[b229062]301 return ctl;
302error:
303 free(ctl);
[7978d1e7]304 hda->ctl = NULL;
[b229062]305 return NULL;
306}
307
308void hda_ctl_fini(hda_ctl_t *ctl)
309{
310 ddf_msg(LVL_NOTE, "hda_ctl_fini()");
311 free(ctl);
312}
313
314/** @}
315 */
Note: See TracBrowser for help on using the repository browser.