Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/soc/txx9/txx9aclc-ac97.c
10817 views
1
/*
2
* TXx9 ACLC AC97 driver
3
*
4
* Copyright (C) 2009 Atsushi Nemoto
5
*
6
* Based on RBTX49xx patch from CELF patch archive.
7
* (C) Copyright TOSHIBA CORPORATION 2004-2006
8
*
9
* This program is free software; you can redistribute it and/or modify
10
* it under the terms of the GNU General Public License version 2 as
11
* published by the Free Software Foundation.
12
*/
13
14
#include <linux/init.h>
15
#include <linux/module.h>
16
#include <linux/delay.h>
17
#include <linux/interrupt.h>
18
#include <linux/io.h>
19
#include <linux/gfp.h>
20
#include <sound/core.h>
21
#include <sound/pcm.h>
22
#include <sound/soc.h>
23
#include "txx9aclc.h"
24
25
#define AC97_DIR \
26
(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
27
28
#define AC97_RATES \
29
SNDRV_PCM_RATE_8000_48000
30
31
#ifdef __BIG_ENDIAN
32
#define AC97_FMTS SNDRV_PCM_FMTBIT_S16_BE
33
#else
34
#define AC97_FMTS SNDRV_PCM_FMTBIT_S16_LE
35
#endif
36
37
static DECLARE_WAIT_QUEUE_HEAD(ac97_waitq);
38
39
/* REVISIT: How to find txx9aclc_drvdata from snd_ac97? */
40
static struct txx9aclc_plat_drvdata *txx9aclc_drvdata;
41
42
static int txx9aclc_regready(struct txx9aclc_plat_drvdata *drvdata)
43
{
44
return __raw_readl(drvdata->base + ACINTSTS) & ACINT_REGACCRDY;
45
}
46
47
/* AC97 controller reads codec register */
48
static unsigned short txx9aclc_ac97_read(struct snd_ac97 *ac97,
49
unsigned short reg)
50
{
51
struct txx9aclc_plat_drvdata *drvdata = txx9aclc_drvdata;
52
void __iomem *base = drvdata->base;
53
u32 dat;
54
55
if (!(__raw_readl(base + ACINTSTS) & ACINT_CODECRDY(ac97->num)))
56
return 0xffff;
57
reg |= ac97->num << 7;
58
dat = (reg << ACREGACC_REG_SHIFT) | ACREGACC_READ;
59
__raw_writel(dat, base + ACREGACC);
60
__raw_writel(ACINT_REGACCRDY, base + ACINTEN);
61
if (!wait_event_timeout(ac97_waitq, txx9aclc_regready(txx9aclc_drvdata), HZ)) {
62
__raw_writel(ACINT_REGACCRDY, base + ACINTDIS);
63
printk(KERN_ERR "ac97 read timeout (reg %#x)\n", reg);
64
dat = 0xffff;
65
goto done;
66
}
67
dat = __raw_readl(base + ACREGACC);
68
if (((dat >> ACREGACC_REG_SHIFT) & 0xff) != reg) {
69
printk(KERN_ERR "reg mismatch %x with %x\n",
70
dat, reg);
71
dat = 0xffff;
72
goto done;
73
}
74
dat = (dat >> ACREGACC_DAT_SHIFT) & 0xffff;
75
done:
76
__raw_writel(ACINT_REGACCRDY, base + ACINTDIS);
77
return dat;
78
}
79
80
/* AC97 controller writes to codec register */
81
static void txx9aclc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
82
unsigned short val)
83
{
84
struct txx9aclc_plat_drvdata *drvdata = txx9aclc_drvdata;
85
void __iomem *base = drvdata->base;
86
87
__raw_writel(((reg | (ac97->num << 7)) << ACREGACC_REG_SHIFT) |
88
(val << ACREGACC_DAT_SHIFT),
89
base + ACREGACC);
90
__raw_writel(ACINT_REGACCRDY, base + ACINTEN);
91
if (!wait_event_timeout(ac97_waitq, txx9aclc_regready(txx9aclc_drvdata), HZ)) {
92
printk(KERN_ERR
93
"ac97 write timeout (reg %#x)\n", reg);
94
}
95
__raw_writel(ACINT_REGACCRDY, base + ACINTDIS);
96
}
97
98
static void txx9aclc_ac97_cold_reset(struct snd_ac97 *ac97)
99
{
100
struct txx9aclc_plat_drvdata *drvdata = txx9aclc_drvdata;
101
void __iomem *base = drvdata->base;
102
u32 ready = ACINT_CODECRDY(ac97->num) | ACINT_REGACCRDY;
103
104
__raw_writel(ACCTL_ENLINK, base + ACCTLDIS);
105
mmiowb();
106
udelay(1);
107
__raw_writel(ACCTL_ENLINK, base + ACCTLEN);
108
/* wait for primary codec ready status */
109
__raw_writel(ready, base + ACINTEN);
110
if (!wait_event_timeout(ac97_waitq,
111
(__raw_readl(base + ACINTSTS) & ready) == ready,
112
HZ)) {
113
dev_err(&ac97->dev, "primary codec is not ready "
114
"(status %#x)\n",
115
__raw_readl(base + ACINTSTS));
116
}
117
__raw_writel(ACINT_REGACCRDY, base + ACINTSTS);
118
__raw_writel(ready, base + ACINTDIS);
119
}
120
121
/* AC97 controller operations */
122
struct snd_ac97_bus_ops soc_ac97_ops = {
123
.read = txx9aclc_ac97_read,
124
.write = txx9aclc_ac97_write,
125
.reset = txx9aclc_ac97_cold_reset,
126
};
127
EXPORT_SYMBOL_GPL(soc_ac97_ops);
128
129
static irqreturn_t txx9aclc_ac97_irq(int irq, void *dev_id)
130
{
131
struct txx9aclc_plat_drvdata *drvdata = dev_id;
132
void __iomem *base = drvdata->base;
133
134
__raw_writel(__raw_readl(base + ACINTMSTS), base + ACINTDIS);
135
wake_up(&ac97_waitq);
136
return IRQ_HANDLED;
137
}
138
139
static int txx9aclc_ac97_probe(struct snd_soc_dai *dai)
140
{
141
txx9aclc_drvdata = snd_soc_dai_get_drvdata(dai);
142
return 0;
143
}
144
145
static int txx9aclc_ac97_remove(struct snd_soc_dai *dai)
146
{
147
struct txx9aclc_plat_drvdata *drvdata = snd_soc_dai_get_drvdata(dai);
148
149
/* disable AC-link */
150
__raw_writel(ACCTL_ENLINK, drvdata->base + ACCTLDIS);
151
txx9aclc_drvdata = NULL;
152
return 0;
153
}
154
155
static struct snd_soc_dai_driver txx9aclc_ac97_dai = {
156
.ac97_control = 1,
157
.probe = txx9aclc_ac97_probe,
158
.remove = txx9aclc_ac97_remove,
159
.playback = {
160
.rates = AC97_RATES,
161
.formats = AC97_FMTS,
162
.channels_min = 2,
163
.channels_max = 2,
164
},
165
.capture = {
166
.rates = AC97_RATES,
167
.formats = AC97_FMTS,
168
.channels_min = 2,
169
.channels_max = 2,
170
},
171
};
172
173
static int __devinit txx9aclc_ac97_dev_probe(struct platform_device *pdev)
174
{
175
struct txx9aclc_plat_drvdata *drvdata;
176
struct resource *r;
177
int err;
178
int irq;
179
180
irq = platform_get_irq(pdev, 0);
181
if (irq < 0)
182
return irq;
183
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
184
if (!r)
185
return -EBUSY;
186
187
if (!devm_request_mem_region(&pdev->dev, r->start, resource_size(r),
188
dev_name(&pdev->dev)))
189
return -EBUSY;
190
191
drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
192
if (!drvdata)
193
return -ENOMEM;
194
platform_set_drvdata(pdev, drvdata);
195
drvdata->physbase = r->start;
196
if (sizeof(drvdata->physbase) > sizeof(r->start) &&
197
r->start >= TXX9_DIRECTMAP_BASE &&
198
r->start < TXX9_DIRECTMAP_BASE + 0x400000)
199
drvdata->physbase |= 0xf00000000ull;
200
drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
201
if (!drvdata->base)
202
return -EBUSY;
203
err = devm_request_irq(&pdev->dev, irq, txx9aclc_ac97_irq,
204
IRQF_DISABLED, dev_name(&pdev->dev), drvdata);
205
if (err < 0)
206
return err;
207
208
return snd_soc_register_dai(&pdev->dev, &txx9aclc_ac97_dai);
209
}
210
211
static int __devexit txx9aclc_ac97_dev_remove(struct platform_device *pdev)
212
{
213
snd_soc_unregister_dai(&pdev->dev);
214
return 0;
215
}
216
217
static struct platform_driver txx9aclc_ac97_driver = {
218
.probe = txx9aclc_ac97_dev_probe,
219
.remove = __devexit_p(txx9aclc_ac97_dev_remove),
220
.driver = {
221
.name = "txx9aclc-ac97",
222
.owner = THIS_MODULE,
223
},
224
};
225
226
static int __init txx9aclc_ac97_init(void)
227
{
228
return platform_driver_register(&txx9aclc_ac97_driver);
229
}
230
231
static void __exit txx9aclc_ac97_exit(void)
232
{
233
platform_driver_unregister(&txx9aclc_ac97_driver);
234
}
235
236
module_init(txx9aclc_ac97_init);
237
module_exit(txx9aclc_ac97_exit);
238
239
MODULE_AUTHOR("Atsushi Nemoto <[email protected]>");
240
MODULE_DESCRIPTION("TXx9 ACLC AC97 driver");
241
MODULE_LICENSE("GPL");
242
MODULE_ALIAS("platform:txx9aclc-ac97");
243
244