Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
9c636342 DB |
2 | /* |
3 | * Based on sound/arm/pxa2xx-ac97.c and sound/soc/pxa/pxa2xx-ac97.c | |
4 | * which contain: | |
5 | * | |
6 | * Author: Nicolas Pitre | |
7 | * Created: Dec 02, 2004 | |
8 | * Copyright: MontaVista Software Inc. | |
9c636342 DB |
9 | */ |
10 | ||
11 | #include <linux/kernel.h> | |
12 | #include <linux/platform_device.h> | |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/clk.h> | |
15 | #include <linux/delay.h> | |
da155d5b | 16 | #include <linux/module.h> |
23019a73 | 17 | #include <linux/io.h> |
3b4bc7bc | 18 | #include <linux/gpio.h> |
a4519526 | 19 | #include <linux/of_gpio.h> |
08d3df8c | 20 | #include <linux/soc/pxa/cpu.h> |
9c636342 | 21 | |
9c636342 DB |
22 | #include <sound/pxa2xx-lib.h> |
23 | ||
22f08665 | 24 | #include <linux/platform_data/asoc-pxa.h> |
9c636342 | 25 | |
8ff06452 AB |
26 | #include "pxa2xx-ac97-regs.h" |
27 | ||
9c636342 DB |
28 | static DEFINE_MUTEX(car_mutex); |
29 | static DECLARE_WAIT_QUEUE_HEAD(gsr_wq); | |
30 | static volatile long gsr_bits; | |
31 | static struct clk *ac97_clk; | |
9c636342 | 32 | static struct clk *ac97conf_clk; |
26ade896 | 33 | static int reset_gpio; |
8ff06452 | 34 | static void __iomem *ac97_reg_base; |
9c636342 DB |
35 | |
36 | /* | |
37 | * Beware PXA27x bugs: | |
38 | * | |
39 | * o Slot 12 read from modem space will hang controller. | |
40 | * o CDONE, SDONE interrupt fails after any slot 12 IO. | |
41 | * | |
42 | * We therefore have an hybrid approach for waiting on SDONE (interrupt or | |
43 | * 1 jiffy timeout if interrupt never comes). | |
44 | */ | |
45 | ||
6f8acad6 | 46 | int pxa2xx_ac97_read(int slot, unsigned short reg) |
9c636342 | 47 | { |
6f8acad6 | 48 | int val = -ENODEV; |
8ff06452 | 49 | u32 __iomem *reg_addr; |
9c636342 | 50 | |
6f8acad6 RJ |
51 | if (slot > 0) |
52 | return -ENODEV; | |
53 | ||
9c636342 DB |
54 | mutex_lock(&car_mutex); |
55 | ||
56 | /* set up primary or secondary codec space */ | |
8825e8e8 | 57 | if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS) |
8ff06452 AB |
58 | reg_addr = ac97_reg_base + |
59 | (slot ? SMC_REG_BASE : PMC_REG_BASE); | |
9c636342 | 60 | else |
8ff06452 AB |
61 | reg_addr = ac97_reg_base + |
62 | (slot ? SAC_REG_BASE : PAC_REG_BASE); | |
9c636342 DB |
63 | reg_addr += (reg >> 1); |
64 | ||
65 | /* start read access across the ac97 link */ | |
8ff06452 | 66 | writel(GSR_CDONE | GSR_SDONE, ac97_reg_base + GSR); |
9c636342 | 67 | gsr_bits = 0; |
8ff06452 | 68 | val = (readl(reg_addr) & 0xffff); |
9c636342 DB |
69 | if (reg == AC97_GPIO_STATUS) |
70 | goto out; | |
8ff06452 AB |
71 | if (wait_event_timeout(gsr_wq, (readl(ac97_reg_base + GSR) | gsr_bits) & GSR_SDONE, 1) <= 0 && |
72 | !((readl(ac97_reg_base + GSR) | gsr_bits) & GSR_SDONE)) { | |
9c636342 | 73 | printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n", |
8ff06452 | 74 | __func__, reg, readl(ac97_reg_base + GSR) | gsr_bits); |
6f8acad6 | 75 | val = -ETIMEDOUT; |
9c636342 DB |
76 | goto out; |
77 | } | |
78 | ||
79 | /* valid data now */ | |
8ff06452 | 80 | writel(GSR_CDONE | GSR_SDONE, ac97_reg_base + GSR); |
9c636342 | 81 | gsr_bits = 0; |
8ff06452 | 82 | val = (readl(reg_addr) & 0xffff); |
9c636342 | 83 | /* but we've just started another cycle... */ |
8ff06452 | 84 | wait_event_timeout(gsr_wq, (readl(ac97_reg_base + GSR) | gsr_bits) & GSR_SDONE, 1); |
9c636342 DB |
85 | |
86 | out: mutex_unlock(&car_mutex); | |
87 | return val; | |
88 | } | |
89 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_read); | |
90 | ||
6f8acad6 | 91 | int pxa2xx_ac97_write(int slot, unsigned short reg, unsigned short val) |
9c636342 | 92 | { |
8ff06452 | 93 | u32 __iomem *reg_addr; |
6f8acad6 | 94 | int ret = 0; |
9c636342 DB |
95 | |
96 | mutex_lock(&car_mutex); | |
97 | ||
98 | /* set up primary or secondary codec space */ | |
8825e8e8 | 99 | if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS) |
8ff06452 AB |
100 | reg_addr = ac97_reg_base + |
101 | (slot ? SMC_REG_BASE : PMC_REG_BASE); | |
9c636342 | 102 | else |
8ff06452 AB |
103 | reg_addr = ac97_reg_base + |
104 | (slot ? SAC_REG_BASE : PAC_REG_BASE); | |
9c636342 DB |
105 | reg_addr += (reg >> 1); |
106 | ||
8ff06452 | 107 | writel(GSR_CDONE | GSR_SDONE, ac97_reg_base + GSR); |
9c636342 | 108 | gsr_bits = 0; |
8ff06452 AB |
109 | writel(val, reg_addr); |
110 | if (wait_event_timeout(gsr_wq, (readl(ac97_reg_base + GSR) | gsr_bits) & GSR_CDONE, 1) <= 0 && | |
111 | !((readl(ac97_reg_base + GSR) | gsr_bits) & GSR_CDONE)) { | |
9c636342 | 112 | printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n", |
8ff06452 | 113 | __func__, reg, readl(ac97_reg_base + GSR) | gsr_bits); |
6f8acad6 RJ |
114 | ret = -EIO; |
115 | } | |
9c636342 DB |
116 | |
117 | mutex_unlock(&car_mutex); | |
6f8acad6 | 118 | return ret; |
9c636342 DB |
119 | } |
120 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_write); | |
121 | ||
9d1cf39b DB |
122 | #ifdef CONFIG_PXA25x |
123 | static inline void pxa_ac97_warm_pxa25x(void) | |
9c636342 | 124 | { |
9c636342 DB |
125 | gsr_bits = 0; |
126 | ||
8ff06452 | 127 | writel(readl(ac97_reg_base + GCR) | (GCR_WARM_RST), ac97_reg_base + GCR); |
9d1cf39b DB |
128 | } |
129 | ||
130 | static inline void pxa_ac97_cold_pxa25x(void) | |
131 | { | |
8ff06452 AB |
132 | writel(readl(ac97_reg_base + GCR) & ( GCR_COLD_RST), ac97_reg_base + GCR); /* clear everything but nCRST */ |
133 | writel(readl(ac97_reg_base + GCR) & (~GCR_COLD_RST), ac97_reg_base + GCR); /* then assert nCRST */ | |
9d1cf39b DB |
134 | |
135 | gsr_bits = 0; | |
136 | ||
8ff06452 | 137 | writel(GCR_COLD_RST, ac97_reg_base + GCR); |
9d1cf39b DB |
138 | } |
139 | #endif | |
140 | ||
9c636342 | 141 | #ifdef CONFIG_PXA27x |
9d1cf39b DB |
142 | static inline void pxa_ac97_warm_pxa27x(void) |
143 | { | |
144 | gsr_bits = 0; | |
145 | ||
fb1bf8cd | 146 | /* warm reset broken on Bulverde, so manually keep AC97 reset high */ |
053fe0f1 | 147 | pxa27x_configure_ac97reset(reset_gpio, true); |
9c636342 | 148 | udelay(10); |
8ff06452 | 149 | writel(readl(ac97_reg_base + GCR) | (GCR_WARM_RST), ac97_reg_base + GCR); |
053fe0f1 | 150 | pxa27x_configure_ac97reset(reset_gpio, false); |
9c636342 | 151 | udelay(500); |
9d1cf39b DB |
152 | } |
153 | ||
154 | static inline void pxa_ac97_cold_pxa27x(void) | |
155 | { | |
8ff06452 AB |
156 | writel(readl(ac97_reg_base + GCR) & ( GCR_COLD_RST), ac97_reg_base + GCR); /* clear everything but nCRST */ |
157 | writel(readl(ac97_reg_base + GCR) & (~GCR_COLD_RST), ac97_reg_base + GCR); /* then assert nCRST */ | |
9d1cf39b DB |
158 | |
159 | gsr_bits = 0; | |
160 | ||
161 | /* PXA27x Developers Manual section 13.5.2.2.1 */ | |
4091d342 | 162 | clk_prepare_enable(ac97conf_clk); |
9d1cf39b | 163 | udelay(5); |
4091d342 | 164 | clk_disable_unprepare(ac97conf_clk); |
8ff06452 | 165 | writel(GCR_COLD_RST | GCR_WARM_RST, ac97_reg_base + GCR); |
9d1cf39b | 166 | } |
9c636342 DB |
167 | #endif |
168 | ||
9d1cf39b DB |
169 | #ifdef CONFIG_PXA3xx |
170 | static inline void pxa_ac97_warm_pxa3xx(void) | |
171 | { | |
9d1cf39b | 172 | gsr_bits = 0; |
9c636342 | 173 | |
9d1cf39b | 174 | /* Can't use interrupts */ |
8ff06452 | 175 | writel(readl(ac97_reg_base + GCR) | (GCR_WARM_RST), ac97_reg_base + GCR); |
9c636342 | 176 | } |
9c636342 | 177 | |
9d1cf39b | 178 | static inline void pxa_ac97_cold_pxa3xx(void) |
9c636342 | 179 | { |
9c636342 | 180 | /* Hold CLKBPB for 100us */ |
8ff06452 AB |
181 | writel(0, ac97_reg_base + GCR); |
182 | writel(GCR_CLKBPB, ac97_reg_base + GCR); | |
9c636342 | 183 | udelay(100); |
8ff06452 | 184 | writel(0, ac97_reg_base + GCR); |
9c636342 | 185 | |
8ff06452 AB |
186 | writel(readl(ac97_reg_base + GCR) & ( GCR_COLD_RST), ac97_reg_base + GCR); /* clear everything but nCRST */ |
187 | writel(readl(ac97_reg_base + GCR) & (~GCR_COLD_RST), ac97_reg_base + GCR); /* then assert nCRST */ | |
9c636342 DB |
188 | |
189 | gsr_bits = 0; | |
9d1cf39b | 190 | |
9c636342 | 191 | /* Can't use interrupts on PXA3xx */ |
8ff06452 | 192 | writel(readl(ac97_reg_base + GCR) & (~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN)), ac97_reg_base + GCR); |
9c636342 | 193 | |
8ff06452 | 194 | writel(GCR_WARM_RST | GCR_COLD_RST, ac97_reg_base + GCR); |
9d1cf39b DB |
195 | } |
196 | #endif | |
197 | ||
6f8acad6 | 198 | bool pxa2xx_ac97_try_warm_reset(void) |
9d1cf39b | 199 | { |
057de50c | 200 | unsigned long gsr; |
beb02cdd | 201 | unsigned int timeout = 100; |
057de50c | 202 | |
9d1cf39b | 203 | #ifdef CONFIG_PXA25x |
8825e8e8 | 204 | if (cpu_is_pxa25x()) |
9d1cf39b DB |
205 | pxa_ac97_warm_pxa25x(); |
206 | else | |
9c636342 | 207 | #endif |
9d1cf39b DB |
208 | #ifdef CONFIG_PXA27x |
209 | if (cpu_is_pxa27x()) | |
210 | pxa_ac97_warm_pxa27x(); | |
211 | else | |
212 | #endif | |
213 | #ifdef CONFIG_PXA3xx | |
214 | if (cpu_is_pxa3xx()) | |
215 | pxa_ac97_warm_pxa3xx(); | |
216 | else | |
217 | #endif | |
88ec7ae8 | 218 | snd_BUG(); |
beb02cdd | 219 | |
8ff06452 | 220 | while (!((readl(ac97_reg_base + GSR) | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) |
beb02cdd DES |
221 | mdelay(1); |
222 | ||
8ff06452 | 223 | gsr = readl(ac97_reg_base + GSR) | gsr_bits; |
057de50c | 224 | if (!(gsr & (GSR_PCR | GSR_SCR))) { |
9d1cf39b | 225 | printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n", |
057de50c | 226 | __func__, gsr); |
9d1cf39b DB |
227 | |
228 | return false; | |
229 | } | |
230 | ||
231 | return true; | |
232 | } | |
233 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset); | |
234 | ||
6f8acad6 | 235 | bool pxa2xx_ac97_try_cold_reset(void) |
9d1cf39b | 236 | { |
057de50c | 237 | unsigned long gsr; |
beb02cdd | 238 | unsigned int timeout = 1000; |
057de50c | 239 | |
9d1cf39b | 240 | #ifdef CONFIG_PXA25x |
8825e8e8 | 241 | if (cpu_is_pxa25x()) |
9d1cf39b DB |
242 | pxa_ac97_cold_pxa25x(); |
243 | else | |
244 | #endif | |
245 | #ifdef CONFIG_PXA27x | |
246 | if (cpu_is_pxa27x()) | |
247 | pxa_ac97_cold_pxa27x(); | |
248 | else | |
249 | #endif | |
250 | #ifdef CONFIG_PXA3xx | |
251 | if (cpu_is_pxa3xx()) | |
252 | pxa_ac97_cold_pxa3xx(); | |
253 | else | |
254 | #endif | |
88ec7ae8 | 255 | snd_BUG(); |
9c636342 | 256 | |
8ff06452 | 257 | while (!((readl(ac97_reg_base + GSR) | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) |
beb02cdd DES |
258 | mdelay(1); |
259 | ||
8ff06452 | 260 | gsr = readl(ac97_reg_base + GSR) | gsr_bits; |
057de50c | 261 | if (!(gsr & (GSR_PCR | GSR_SCR))) { |
9c636342 | 262 | printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n", |
057de50c | 263 | __func__, gsr); |
9c636342 DB |
264 | |
265 | return false; | |
266 | } | |
267 | ||
268 | return true; | |
269 | } | |
270 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_cold_reset); | |
271 | ||
272 | ||
6f8acad6 | 273 | void pxa2xx_ac97_finish_reset(void) |
9c636342 | 274 | { |
8ff06452 AB |
275 | u32 gcr = readl(ac97_reg_base + GCR); |
276 | gcr &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); | |
277 | gcr |= GCR_SDONE_IE|GCR_CDONE_IE; | |
278 | writel(gcr, ac97_reg_base + GCR); | |
9c636342 DB |
279 | } |
280 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_finish_reset); | |
281 | ||
282 | static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id) | |
283 | { | |
284 | long status; | |
285 | ||
8ff06452 | 286 | status = readl(ac97_reg_base + GSR); |
9c636342 | 287 | if (status) { |
8ff06452 | 288 | writel(status, ac97_reg_base + GSR); |
9c636342 DB |
289 | gsr_bits |= status; |
290 | wake_up(&gsr_wq); | |
291 | ||
9c636342 DB |
292 | /* Although we don't use those we still need to clear them |
293 | since they tend to spuriously trigger when MMC is used | |
294 | (hardware bug? go figure)... */ | |
9d1cf39b | 295 | if (cpu_is_pxa27x()) { |
8ff06452 AB |
296 | writel(MISR_EOC, ac97_reg_base + MISR); |
297 | writel(PISR_EOC, ac97_reg_base + PISR); | |
298 | writel(MCSR_EOC, ac97_reg_base + MCSR); | |
9d1cf39b | 299 | } |
9c636342 DB |
300 | |
301 | return IRQ_HANDLED; | |
302 | } | |
303 | ||
304 | return IRQ_NONE; | |
305 | } | |
306 | ||
307 | #ifdef CONFIG_PM | |
308 | int pxa2xx_ac97_hw_suspend(void) | |
309 | { | |
8ff06452 | 310 | writel(readl(ac97_reg_base + GCR) | (GCR_ACLINK_OFF), ac97_reg_base + GCR); |
4091d342 | 311 | clk_disable_unprepare(ac97_clk); |
9c636342 DB |
312 | return 0; |
313 | } | |
314 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_suspend); | |
315 | ||
316 | int pxa2xx_ac97_hw_resume(void) | |
317 | { | |
4091d342 | 318 | clk_prepare_enable(ac97_clk); |
9c636342 DB |
319 | return 0; |
320 | } | |
321 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_resume); | |
322 | #endif | |
323 | ||
e21596bb | 324 | int pxa2xx_ac97_hw_probe(struct platform_device *dev) |
9c636342 DB |
325 | { |
326 | int ret; | |
2548e6c7 | 327 | int irq; |
eae17754 | 328 | pxa2xx_audio_ops_t *pdata = dev->dev.platform_data; |
26ade896 | 329 | |
8ff06452 AB |
330 | ac97_reg_base = devm_platform_ioremap_resource(dev, 0); |
331 | if (IS_ERR(ac97_reg_base)) { | |
332 | dev_err(&dev->dev, "Missing MMIO resource\n"); | |
333 | return PTR_ERR(ac97_reg_base); | |
334 | } | |
335 | ||
26ade896 RJ |
336 | if (pdata) { |
337 | switch (pdata->reset_gpio) { | |
338 | case 95: | |
339 | case 113: | |
340 | reset_gpio = pdata->reset_gpio; | |
341 | break; | |
342 | case 0: | |
343 | reset_gpio = 113; | |
344 | break; | |
345 | case -1: | |
346 | break; | |
347 | default: | |
1f218695 | 348 | dev_err(&dev->dev, "Invalid reset GPIO %d\n", |
26ade896 RJ |
349 | pdata->reset_gpio); |
350 | } | |
a4519526 RJ |
351 | } else if (!pdata && dev->dev.of_node) { |
352 | pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL); | |
353 | if (!pdata) | |
354 | return -ENOMEM; | |
355 | pdata->reset_gpio = of_get_named_gpio(dev->dev.of_node, | |
356 | "reset-gpios", 0); | |
357 | if (pdata->reset_gpio == -ENOENT) | |
358 | pdata->reset_gpio = -1; | |
359 | else if (pdata->reset_gpio < 0) | |
360 | return pdata->reset_gpio; | |
361 | reset_gpio = pdata->reset_gpio; | |
26ade896 RJ |
362 | } else { |
363 | if (cpu_is_pxa27x()) | |
364 | reset_gpio = 113; | |
365 | } | |
9c636342 | 366 | |
9d1cf39b | 367 | if (cpu_is_pxa27x()) { |
3b4bc7bc MD |
368 | /* |
369 | * This gpio is needed for a work-around to a bug in the ac97 | |
370 | * controller during warm reset. The direction and level is set | |
371 | * here so that it is an output driven high when switching from | |
372 | * AC97_nRESET alt function to generic gpio. | |
373 | */ | |
374 | ret = gpio_request_one(reset_gpio, GPIOF_OUT_INIT_HIGH, | |
375 | "pxa27x ac97 reset"); | |
376 | if (ret < 0) { | |
377 | pr_err("%s: gpio_request_one() failed: %d\n", | |
378 | __func__, ret); | |
379 | goto err_conf; | |
380 | } | |
053fe0f1 | 381 | pxa27x_configure_ac97reset(reset_gpio, false); |
3b4bc7bc | 382 | |
9d1cf39b DB |
383 | ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK"); |
384 | if (IS_ERR(ac97conf_clk)) { | |
385 | ret = PTR_ERR(ac97conf_clk); | |
386 | ac97conf_clk = NULL; | |
79612336 | 387 | goto err_conf; |
9d1cf39b | 388 | } |
9c636342 | 389 | } |
9c636342 DB |
390 | |
391 | ac97_clk = clk_get(&dev->dev, "AC97CLK"); | |
392 | if (IS_ERR(ac97_clk)) { | |
393 | ret = PTR_ERR(ac97_clk); | |
394 | ac97_clk = NULL; | |
79612336 | 395 | goto err_clk; |
9c636342 DB |
396 | } |
397 | ||
4091d342 | 398 | ret = clk_prepare_enable(ac97_clk); |
79612336 DB |
399 | if (ret) |
400 | goto err_clk2; | |
401 | ||
2548e6c7 | 402 | irq = platform_get_irq(dev, 0); |
46cf1954 YY |
403 | if (irq < 0) { |
404 | ret = irq; | |
2548e6c7 | 405 | goto err_irq; |
46cf1954 | 406 | } |
2548e6c7 AB |
407 | |
408 | ret = request_irq(irq, pxa2xx_ac97_irq, 0, "AC97", NULL); | |
79612336 DB |
409 | if (ret < 0) |
410 | goto err_irq; | |
411 | ||
412 | return 0; | |
9c636342 DB |
413 | |
414 | err_irq: | |
8ff06452 | 415 | writel(readl(ac97_reg_base + GCR) | (GCR_ACLINK_OFF), ac97_reg_base + GCR); |
79612336 DB |
416 | err_clk2: |
417 | clk_put(ac97_clk); | |
418 | ac97_clk = NULL; | |
419 | err_clk: | |
9c636342 DB |
420 | if (ac97conf_clk) { |
421 | clk_put(ac97conf_clk); | |
422 | ac97conf_clk = NULL; | |
423 | } | |
79612336 | 424 | err_conf: |
9c636342 DB |
425 | return ret; |
426 | } | |
427 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_probe); | |
428 | ||
429 | void pxa2xx_ac97_hw_remove(struct platform_device *dev) | |
430 | { | |
3b4bc7bc MD |
431 | if (cpu_is_pxa27x()) |
432 | gpio_free(reset_gpio); | |
8ff06452 | 433 | writel(readl(ac97_reg_base + GCR) | (GCR_ACLINK_OFF), ac97_reg_base + GCR); |
2548e6c7 | 434 | free_irq(platform_get_irq(dev, 0), NULL); |
9d1cf39b DB |
435 | if (ac97conf_clk) { |
436 | clk_put(ac97conf_clk); | |
437 | ac97conf_clk = NULL; | |
438 | } | |
4091d342 | 439 | clk_disable_unprepare(ac97_clk); |
9c636342 DB |
440 | clk_put(ac97_clk); |
441 | ac97_clk = NULL; | |
442 | } | |
443 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_remove); | |
444 | ||
e217b085 AB |
445 | u32 pxa2xx_ac97_read_modr(void) |
446 | { | |
8ff06452 AB |
447 | if (!ac97_reg_base) |
448 | return 0; | |
449 | ||
450 | return readl(ac97_reg_base + MODR); | |
e217b085 AB |
451 | } |
452 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_read_modr); | |
453 | ||
454 | u32 pxa2xx_ac97_read_misr(void) | |
455 | { | |
8ff06452 AB |
456 | if (!ac97_reg_base) |
457 | return 0; | |
458 | ||
459 | return readl(ac97_reg_base + MISR); | |
e217b085 AB |
460 | } |
461 | EXPORT_SYMBOL_GPL(pxa2xx_ac97_read_misr); | |
462 | ||
9c636342 DB |
463 | MODULE_AUTHOR("Nicolas Pitre"); |
464 | MODULE_DESCRIPTION("Intel/Marvell PXA sound library"); | |
465 | MODULE_LICENSE("GPL"); | |
466 |