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 struct snd_ac97_bus_ops soc_ac97_ops
= {
123 .read
= txx9aclc_ac97_read
,
124 .write
= txx9aclc_ac97_write
,
125 .reset
= txx9aclc_ac97_cold_reset
,
127 EXPORT_SYMBOL_GPL(soc_ac97_ops
);
129 static irqreturn_t
txx9aclc_ac97_irq(int irq
, void *dev_id
)
131 struct txx9aclc_plat_drvdata
*drvdata
= dev_id
;
132 void __iomem
*base
= drvdata
->base
;
134 __raw_writel(__raw_readl(base
+ ACINTMSTS
), base
+ ACINTDIS
);
135 wake_up(&ac97_waitq
);
139 static int txx9aclc_ac97_probe(struct snd_soc_dai
*dai
)
141 txx9aclc_drvdata
= snd_soc_dai_get_drvdata(dai
);
145 static int txx9aclc_ac97_remove(struct snd_soc_dai
*dai
)
147 struct txx9aclc_plat_drvdata
*drvdata
= snd_soc_dai_get_drvdata(dai
);
149 /* disable AC-link */
150 __raw_writel(ACCTL_ENLINK
, drvdata
->base
+ ACCTLDIS
);
151 txx9aclc_drvdata
= NULL
;
155 static struct snd_soc_dai_driver txx9aclc_ac97_dai
= {
157 .probe
= txx9aclc_ac97_probe
,
158 .remove
= txx9aclc_ac97_remove
,
161 .formats
= AC97_FMTS
,
167 .formats
= AC97_FMTS
,
173 static int __devinit
txx9aclc_ac97_dev_probe(struct platform_device
*pdev
)
175 struct txx9aclc_plat_drvdata
*drvdata
;
180 irq
= platform_get_irq(pdev
, 0);
183 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
187 if (!devm_request_mem_region(&pdev
->dev
, r
->start
, resource_size(r
),
188 dev_name(&pdev
->dev
)))
191 drvdata
= devm_kzalloc(&pdev
->dev
, sizeof(*drvdata
), GFP_KERNEL
);
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
));
203 err
= devm_request_irq(&pdev
->dev
, irq
, txx9aclc_ac97_irq
,
204 0, dev_name(&pdev
->dev
), drvdata
);
208 return snd_soc_register_dai(&pdev
->dev
, &txx9aclc_ac97_dai
);
211 static int __devexit
txx9aclc_ac97_dev_remove(struct platform_device
*pdev
)
213 snd_soc_unregister_dai(&pdev
->dev
);
217 static struct platform_driver txx9aclc_ac97_driver
= {
218 .probe
= txx9aclc_ac97_dev_probe
,
219 .remove
= __devexit_p(txx9aclc_ac97_dev_remove
),
221 .name
= "txx9aclc-ac97",
222 .owner
= THIS_MODULE
,
226 static int __init
txx9aclc_ac97_init(void)
228 return platform_driver_register(&txx9aclc_ac97_driver
);
231 static void __exit
txx9aclc_ac97_exit(void)
233 platform_driver_unregister(&txx9aclc_ac97_driver
);
236 module_init(txx9aclc_ac97_init
);
237 module_exit(txx9aclc_ac97_exit
);
239 MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
240 MODULE_DESCRIPTION("TXx9 ACLC AC97 driver");
241 MODULE_LICENSE("GPL");
242 MODULE_ALIAS("platform:txx9aclc-ac97");