2 * TXx9 ACLC AC97 driver
4 * Copyright (C) 2009 Atsushi Nemoto
6 * Based on RBTX49xx patch from CELF patch archive.
7 * (C) Copyright TOSHIBA CORPORATION 2004-2006
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.
14 #include <linux/init.h>
15 #include <linux/module.h>
16 #include <linux/delay.h>
17 #include <linux/interrupt.h>
19 #include <linux/gfp.h>
20 #include <sound/core.h>
21 #include <sound/pcm.h>
22 #include <sound/soc.h>
26 (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
29 SNDRV_PCM_RATE_8000_48000
32 #define AC97_FMTS SNDRV_PCM_FMTBIT_S16_BE
34 #define AC97_FMTS SNDRV_PCM_FMTBIT_S16_LE
37 static DECLARE_WAIT_QUEUE_HEAD(ac97_waitq
);
39 /* REVISIT: How to find txx9aclc_drvdata from snd_ac97? */
40 static struct txx9aclc_plat_drvdata
*txx9aclc_drvdata
;
42 static int txx9aclc_regready(struct txx9aclc_plat_drvdata
*drvdata
)
44 return __raw_readl(drvdata
->base
+ ACINTSTS
) & ACINT_REGACCRDY
;
47 /* AC97 controller reads codec register */
48 static unsigned short txx9aclc_ac97_read(struct snd_ac97
*ac97
,
51 struct txx9aclc_plat_drvdata
*drvdata
= txx9aclc_drvdata
;
52 void __iomem
*base
= drvdata
->base
;
55 if (!(__raw_readl(base
+ ACINTSTS
) & ACINT_CODECRDY(ac97
->num
)))
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
);
67 dat
= __raw_readl(base
+ ACREGACC
);
68 if (((dat
>> ACREGACC_REG_SHIFT
) & 0xff) != reg
) {
69 printk(KERN_ERR
"reg mismatch %x with %x\n",
74 dat
= (dat
>> ACREGACC_DAT_SHIFT
) & 0xffff;
76 __raw_writel(ACINT_REGACCRDY
, base
+ ACINTDIS
);
80 /* AC97 controller writes to codec register */
81 static void txx9aclc_ac97_write(struct snd_ac97
*ac97
, unsigned short reg
,
84 struct txx9aclc_plat_drvdata
*drvdata
= txx9aclc_drvdata
;
85 void __iomem
*base
= drvdata
->base
;
87 __raw_writel(((reg
| (ac97
->num
<< 7)) << ACREGACC_REG_SHIFT
) |
88 (val
<< ACREGACC_DAT_SHIFT
),
90 __raw_writel(ACINT_REGACCRDY
, base
+ ACINTEN
);
91 if (!wait_event_timeout(ac97_waitq
, txx9aclc_regready(txx9aclc_drvdata
), HZ
)) {
93 "ac97 write timeout (reg %#x)\n", reg
);
95 __raw_writel(ACINT_REGACCRDY
, base
+ ACINTDIS
);
98 static void txx9aclc_ac97_cold_reset(struct snd_ac97
*ac97
)
100 struct txx9aclc_plat_drvdata
*drvdata
= txx9aclc_drvdata
;
101 void __iomem
*base
= drvdata
->base
;
102 u32 ready
= ACINT_CODECRDY(ac97
->num
) | ACINT_REGACCRDY
;
104 __raw_writel(ACCTL_ENLINK
, base
+ ACCTLDIS
);
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
,
113 dev_err(&ac97
->dev
, "primary codec is not ready "
115 __raw_readl(base
+ ACINTSTS
));
117 __raw_writel(ACINT_REGACCRDY
, base
+ ACINTSTS
);
118 __raw_writel(ready
, base
+ ACINTDIS
);
121 /* AC97 controller operations */
122 static struct snd_ac97_bus_ops txx9aclc_ac97_ops
= {
123 .read
= txx9aclc_ac97_read
,
124 .write
= txx9aclc_ac97_write
,
125 .reset
= txx9aclc_ac97_cold_reset
,
128 static irqreturn_t
txx9aclc_ac97_irq(int irq
, void *dev_id
)
130 struct txx9aclc_plat_drvdata
*drvdata
= dev_id
;
131 void __iomem
*base
= drvdata
->base
;
133 __raw_writel(__raw_readl(base
+ ACINTMSTS
), base
+ ACINTDIS
);
134 wake_up(&ac97_waitq
);
138 static int txx9aclc_ac97_probe(struct snd_soc_dai
*dai
)
140 txx9aclc_drvdata
= snd_soc_dai_get_drvdata(dai
);
144 static int txx9aclc_ac97_remove(struct snd_soc_dai
*dai
)
146 struct txx9aclc_plat_drvdata
*drvdata
= snd_soc_dai_get_drvdata(dai
);
148 /* disable AC-link */
149 __raw_writel(ACCTL_ENLINK
, drvdata
->base
+ ACCTLDIS
);
150 txx9aclc_drvdata
= NULL
;
154 static struct snd_soc_dai_driver txx9aclc_ac97_dai
= {
156 .probe
= txx9aclc_ac97_probe
,
157 .remove
= txx9aclc_ac97_remove
,
160 .formats
= AC97_FMTS
,
166 .formats
= AC97_FMTS
,
172 static const struct snd_soc_component_driver txx9aclc_ac97_component
= {
173 .name
= "txx9aclc-ac97",
176 static int txx9aclc_ac97_dev_probe(struct platform_device
*pdev
)
178 struct txx9aclc_plat_drvdata
*drvdata
;
183 irq
= platform_get_irq(pdev
, 0);
187 drvdata
= devm_kzalloc(&pdev
->dev
, sizeof(*drvdata
), GFP_KERNEL
);
191 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
192 drvdata
->base
= devm_ioremap_resource(&pdev
->dev
, r
);
193 if (IS_ERR(drvdata
->base
))
194 return PTR_ERR(drvdata
->base
);
196 platform_set_drvdata(pdev
, drvdata
);
197 drvdata
->physbase
= r
->start
;
198 if (sizeof(drvdata
->physbase
) > sizeof(r
->start
) &&
199 r
->start
>= TXX9_DIRECTMAP_BASE
&&
200 r
->start
< TXX9_DIRECTMAP_BASE
+ 0x400000)
201 drvdata
->physbase
|= 0xf00000000ull
;
202 err
= devm_request_irq(&pdev
->dev
, irq
, txx9aclc_ac97_irq
,
203 0, dev_name(&pdev
->dev
), drvdata
);
207 err
= snd_soc_set_ac97_ops(&txx9aclc_ac97_ops
);
211 return snd_soc_register_component(&pdev
->dev
, &txx9aclc_ac97_component
,
212 &txx9aclc_ac97_dai
, 1);
215 static int txx9aclc_ac97_dev_remove(struct platform_device
*pdev
)
217 snd_soc_unregister_component(&pdev
->dev
);
218 snd_soc_set_ac97_ops(NULL
);
222 static struct platform_driver txx9aclc_ac97_driver
= {
223 .probe
= txx9aclc_ac97_dev_probe
,
224 .remove
= txx9aclc_ac97_dev_remove
,
226 .name
= "txx9aclc-ac97",
227 .owner
= THIS_MODULE
,
231 module_platform_driver(txx9aclc_ac97_driver
);
233 MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
234 MODULE_DESCRIPTION("TXx9 ACLC AC97 driver");
235 MODULE_LICENSE("GPL");
236 MODULE_ALIAS("platform:txx9aclc-ac97");