src: Use "foo **bar" instead of "foo ** bar"
[coreboot.git] / src / device / azalia_device.c
blob6ca80a82d381daf5f8ba8d06a1f6ae754d18f5c3
1 /*
2 * This file is part of the coreboot project.
4 * Copyright (C) 2013 DMP Electronics Inc.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
16 #include <console/console.h>
17 #include <device/device.h>
18 #include <device/pci.h>
19 #include <device/pci_ops.h>
20 #include <device/azalia_device.h>
21 #include <arch/io.h>
22 #include <delay.h>
24 #define HDA_ICII_REG 0x68
25 #define HDA_ICII_BUSY (1 << 0)
26 #define HDA_ICII_VALID (1 << 1)
28 static int set_bits(void *port, u32 mask, u32 val)
30 u32 reg32;
31 int count;
33 /* Write (val & mask) to port */
34 val &= mask;
35 reg32 = read32(port);
36 reg32 &= ~mask;
37 reg32 |= val;
38 write32(port, reg32);
40 /* Wait for readback of register to
41 * match what was just written to it
43 count = 50;
44 do {
45 /* Wait 1ms based on BKDG wait time */
46 mdelay(1);
47 reg32 = read32(port);
48 reg32 &= mask;
49 } while ((reg32 != val) && --count);
51 /* Timeout occurred */
52 if (!count)
53 return -1;
54 return 0;
57 static int codec_detect(u8 *base)
59 u32 reg32;
60 int count;
62 /* Set Bit 0 to 1 to exit reset state (BAR + 0x8)[0] */
63 if (set_bits(base + 0x08, 1, 1) == -1)
64 goto no_codec;
66 /* clear STATESTS bits (BAR + 0xE)[2:0] */
67 reg32 = read32(base + 0x0E);
68 reg32 |= 7;
69 write32(base + 0x0E, reg32);
71 /* Wait for readback of register to
72 * match what was just written to it
74 count = 50;
75 do {
76 /* Wait 1ms based on BKDG wait time */
77 mdelay(1);
78 reg32 = read32(base + 0x0E);
79 } while ((reg32 != 0) && --count);
80 /* Timeout occurred */
81 if (!count)
82 goto no_codec;
84 /* Set Bit0 to 0 to enter reset state (BAR + 0x8)[0] */
85 if (set_bits(base + 0x08, 1, 0) == -1)
86 goto no_codec;
88 /* Set Bit 0 to 1 to exit reset state (BAR + 0x8)[0] */
89 if (set_bits(base + 0x08, 1, 1) == -1)
90 goto no_codec;
92 /* Read in Codec location (BAR + 0xe)[2..0] */
93 reg32 = read32(base + 0xe);
94 reg32 &= 0x0f;
95 if (!reg32)
96 goto no_codec;
98 return reg32;
100 no_codec:
101 /* Codec Not found */
102 /* Put HDA back in reset (BAR + 0x8) [0] */
103 set_bits(base + 0x08, 1, 0);
104 printk(BIOS_DEBUG, "azalia_audio: No codec!\n");
105 return 0;
108 static u32 find_verb(struct device *dev, u32 viddid, const u32 **verb)
110 printk(BIOS_DEBUG, "azalia_audio: dev=%s\n", dev_path(dev));
111 printk(BIOS_DEBUG, "azalia_audio: Reading viddid=%x\n", viddid);
113 int idx = 0;
115 while (idx < (cim_verb_data_size / sizeof(u32))) {
116 u32 verb_size = 4 * cim_verb_data[idx + 2]; // in u32
117 if (cim_verb_data[idx] != viddid) {
118 idx += verb_size + 3; // skip verb + header
119 continue;
121 *verb = &cim_verb_data[idx + 3];
122 return verb_size;
125 /* Not all codecs need to load another verb */
126 return 0;
130 * Wait 50usec for the codec to indicate it is ready
131 * no response would imply that the codec is non-operative
134 static int wait_for_ready(u8 *base)
136 /* Use a 50 usec timeout - the Linux kernel uses the
137 * same duration */
139 int timeout = 50;
141 while (timeout--) {
142 u32 reg32 = read32(base + HDA_ICII_REG);
143 if (!(reg32 & HDA_ICII_BUSY))
144 return 0;
145 udelay(1);
148 return -1;
152 * Wait 50usec for the codec to indicate that it accepted
153 * the previous command. No response would imply that the code
154 * is non-operative
157 static int wait_for_valid(u8 *base)
159 /* Use a 50 usec timeout - the Linux kernel uses the
160 * same duration */
162 int timeout = 25;
164 write32(base + HDA_ICII_REG,
165 HDA_ICII_VALID | HDA_ICII_BUSY);
166 while (timeout--) {
167 udelay(1);
169 timeout = 50;
170 while (timeout--) {
171 u32 reg32 = read32(base + HDA_ICII_REG);
172 if ((reg32 & (HDA_ICII_VALID | HDA_ICII_BUSY)) ==
173 HDA_ICII_VALID)
174 return 0;
175 udelay(1);
178 return -1;
181 static void codec_init(struct device *dev, u8 *base, int addr)
183 u32 reg32;
184 const u32 *verb;
185 u32 verb_size;
186 int i;
188 printk(BIOS_DEBUG, "azalia_audio: Initializing codec #%d\n", addr);
190 /* 1 */
191 if (wait_for_ready(base) == -1)
192 return;
194 reg32 = (addr << 28) | 0x000f0000;
195 write32(base + 0x60, reg32);
197 if (wait_for_valid(base) == -1)
198 return;
200 reg32 = read32(base + 0x64);
202 /* 2 */
203 printk(BIOS_DEBUG, "azalia_audio: codec viddid: %08x\n", reg32);
204 verb_size = find_verb(dev, reg32, &verb);
206 if (!verb_size) {
207 printk(BIOS_DEBUG, "azalia_audio: No verb!\n");
208 return;
210 printk(BIOS_DEBUG, "azalia_audio: verb_size: %d\n", verb_size);
212 /* 3 */
213 for (i = 0; i < verb_size; i++) {
214 if (wait_for_ready(base) == -1)
215 return;
217 write32(base + 0x60, verb[i]);
219 if (wait_for_valid(base) == -1)
220 return;
222 printk(BIOS_DEBUG, "azalia_audio: verb loaded.\n");
225 static void codecs_init(struct device *dev, u8 *base, u32 codec_mask)
227 int i;
229 for (i = 2; i >= 0; i--) {
230 if (codec_mask & (1 << i))
231 codec_init(dev, base, i);
235 void azalia_audio_init(struct device *dev)
237 u8 *base;
238 struct resource *res;
239 u32 codec_mask;
241 res = find_resource(dev, 0x10);
242 if (!res)
243 return;
245 // NOTE this will break as soon as the azalia_audio get's a bar above
246 // 4G. Is there anything we can do about it?
247 base = res2mmio(res, 0, 0);
248 printk(BIOS_DEBUG, "azalia_audio: base = %p\n", base);
249 codec_mask = codec_detect(base);
251 if (codec_mask) {
252 printk(BIOS_DEBUG, "azalia_audio: codec_mask = %02x\n",
253 codec_mask);
254 codecs_init(dev, base, codec_mask);
258 struct pci_operations azalia_audio_pci_ops = {
259 .set_subsystem = pci_dev_set_subsystem,
262 struct device_operations default_azalia_audio_ops = {
263 .read_resources = pci_dev_read_resources,
264 .set_resources = pci_dev_set_resources,
265 .enable_resources = pci_dev_enable_resources,
266 .init = azalia_audio_init,
267 .scan_bus = 0,
268 .ops_pci = &azalia_audio_pci_ops,