1 // SPDX-License-Identifier: GPL-2.0-only
3 * TXx9 ACLC AC97 driver
5 * Copyright (C) 2009 Atsushi Nemoto
7 * Based on RBTX49xx patch from CELF patch archive.
8 * (C) Copyright TOSHIBA CORPORATION 2004-2006
11 #include <linux/init.h>
12 #include <linux/module.h>
13 #include <linux/delay.h>
14 #include <linux/interrupt.h>
16 #include <linux/gfp.h>
17 #include <sound/core.h>
18 #include <sound/pcm.h>
19 #include <sound/soc.h>
23 (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
26 SNDRV_PCM_RATE_8000_48000
29 #define AC97_FMTS SNDRV_PCM_FMTBIT_S16_BE
31 #define AC97_FMTS SNDRV_PCM_FMTBIT_S16_LE
34 static DECLARE_WAIT_QUEUE_HEAD(ac97_waitq
);
36 /* REVISIT: How to find txx9aclc_drvdata from snd_ac97? */
37 static struct txx9aclc_plat_drvdata
*txx9aclc_drvdata
;
39 static int txx9aclc_regready(struct txx9aclc_plat_drvdata
*drvdata
)
41 return __raw_readl(drvdata
->base
+ ACINTSTS
) & ACINT_REGACCRDY
;
44 /* AC97 controller reads codec register */
45 static unsigned short txx9aclc_ac97_read(struct snd_ac97
*ac97
,
48 struct txx9aclc_plat_drvdata
*drvdata
= txx9aclc_drvdata
;
49 void __iomem
*base
= drvdata
->base
;
52 if (!(__raw_readl(base
+ ACINTSTS
) & ACINT_CODECRDY(ac97
->num
)))
54 reg
|= ac97
->num
<< 7;
55 dat
= (reg
<< ACREGACC_REG_SHIFT
) | ACREGACC_READ
;
56 __raw_writel(dat
, base
+ ACREGACC
);
57 __raw_writel(ACINT_REGACCRDY
, base
+ ACINTEN
);
58 if (!wait_event_timeout(ac97_waitq
, txx9aclc_regready(txx9aclc_drvdata
), HZ
)) {
59 __raw_writel(ACINT_REGACCRDY
, base
+ ACINTDIS
);
60 printk(KERN_ERR
"ac97 read timeout (reg %#x)\n", reg
);
64 dat
= __raw_readl(base
+ ACREGACC
);
65 if (((dat
>> ACREGACC_REG_SHIFT
) & 0xff) != reg
) {
66 printk(KERN_ERR
"reg mismatch %x with %x\n",
71 dat
= (dat
>> ACREGACC_DAT_SHIFT
) & 0xffff;
73 __raw_writel(ACINT_REGACCRDY
, base
+ ACINTDIS
);
77 /* AC97 controller writes to codec register */
78 static void txx9aclc_ac97_write(struct snd_ac97
*ac97
, unsigned short reg
,
81 struct txx9aclc_plat_drvdata
*drvdata
= txx9aclc_drvdata
;
82 void __iomem
*base
= drvdata
->base
;
84 __raw_writel(((reg
| (ac97
->num
<< 7)) << ACREGACC_REG_SHIFT
) |
85 (val
<< ACREGACC_DAT_SHIFT
),
87 __raw_writel(ACINT_REGACCRDY
, base
+ ACINTEN
);
88 if (!wait_event_timeout(ac97_waitq
, txx9aclc_regready(txx9aclc_drvdata
), HZ
)) {
90 "ac97 write timeout (reg %#x)\n", reg
);
92 __raw_writel(ACINT_REGACCRDY
, base
+ ACINTDIS
);
95 static void txx9aclc_ac97_cold_reset(struct snd_ac97
*ac97
)
97 struct txx9aclc_plat_drvdata
*drvdata
= txx9aclc_drvdata
;
98 void __iomem
*base
= drvdata
->base
;
99 u32 ready
= ACINT_CODECRDY(ac97
->num
) | ACINT_REGACCRDY
;
101 __raw_writel(ACCTL_ENLINK
, base
+ ACCTLDIS
);
103 __raw_writel(ACCTL_ENLINK
, base
+ ACCTLEN
);
104 /* wait for primary codec ready status */
105 __raw_writel(ready
, base
+ ACINTEN
);
106 if (!wait_event_timeout(ac97_waitq
,
107 (__raw_readl(base
+ ACINTSTS
) & ready
) == ready
,
109 dev_err(&ac97
->dev
, "primary codec is not ready "
111 __raw_readl(base
+ ACINTSTS
));
113 __raw_writel(ACINT_REGACCRDY
, base
+ ACINTSTS
);
114 __raw_writel(ready
, base
+ ACINTDIS
);
117 /* AC97 controller operations */
118 static struct snd_ac97_bus_ops txx9aclc_ac97_ops
= {
119 .read
= txx9aclc_ac97_read
,
120 .write
= txx9aclc_ac97_write
,
121 .reset
= txx9aclc_ac97_cold_reset
,
124 static irqreturn_t
txx9aclc_ac97_irq(int irq
, void *dev_id
)
126 struct txx9aclc_plat_drvdata
*drvdata
= dev_id
;
127 void __iomem
*base
= drvdata
->base
;
129 __raw_writel(__raw_readl(base
+ ACINTMSTS
), base
+ ACINTDIS
);
130 wake_up(&ac97_waitq
);
134 static int txx9aclc_ac97_probe(struct snd_soc_dai
*dai
)
136 txx9aclc_drvdata
= snd_soc_dai_get_drvdata(dai
);
140 static int txx9aclc_ac97_remove(struct snd_soc_dai
*dai
)
142 struct txx9aclc_plat_drvdata
*drvdata
= snd_soc_dai_get_drvdata(dai
);
144 /* disable AC-link */
145 __raw_writel(ACCTL_ENLINK
, drvdata
->base
+ ACCTLDIS
);
146 txx9aclc_drvdata
= NULL
;
150 static struct snd_soc_dai_driver txx9aclc_ac97_dai
= {
151 .probe
= txx9aclc_ac97_probe
,
152 .remove
= txx9aclc_ac97_remove
,
155 .formats
= AC97_FMTS
,
161 .formats
= AC97_FMTS
,
167 static const struct snd_soc_component_driver txx9aclc_ac97_component
= {
168 .name
= "txx9aclc-ac97",
171 static int txx9aclc_ac97_dev_probe(struct platform_device
*pdev
)
173 struct txx9aclc_plat_drvdata
*drvdata
;
178 irq
= platform_get_irq(pdev
, 0);
182 drvdata
= devm_kzalloc(&pdev
->dev
, sizeof(*drvdata
), GFP_KERNEL
);
186 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
187 drvdata
->base
= devm_ioremap_resource(&pdev
->dev
, r
);
188 if (IS_ERR(drvdata
->base
))
189 return PTR_ERR(drvdata
->base
);
191 platform_set_drvdata(pdev
, drvdata
);
192 drvdata
->physbase
= r
->start
;
193 if (sizeof(drvdata
->physbase
) > sizeof(r
->start
) &&
194 r
->start
>= TXX9_DIRECTMAP_BASE
&&
195 r
->start
< TXX9_DIRECTMAP_BASE
+ 0x400000)
196 drvdata
->physbase
|= 0xf00000000ull
;
197 err
= devm_request_irq(&pdev
->dev
, irq
, txx9aclc_ac97_irq
,
198 0, dev_name(&pdev
->dev
), drvdata
);
202 err
= snd_soc_set_ac97_ops(&txx9aclc_ac97_ops
);
206 return devm_snd_soc_register_component(&pdev
->dev
, &txx9aclc_ac97_component
,
207 &txx9aclc_ac97_dai
, 1);
210 static int txx9aclc_ac97_dev_remove(struct platform_device
*pdev
)
212 snd_soc_set_ac97_ops(NULL
);
216 static struct platform_driver txx9aclc_ac97_driver
= {
217 .probe
= txx9aclc_ac97_dev_probe
,
218 .remove
= txx9aclc_ac97_dev_remove
,
220 .name
= "txx9aclc-ac97",
224 module_platform_driver(txx9aclc_ac97_driver
);
226 MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
227 MODULE_DESCRIPTION("TXx9 ACLC AC97 driver");
228 MODULE_LICENSE("GPL");
229 MODULE_ALIAS("platform:txx9aclc-ac97");