• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/sound/soc/au1x/
1/*
2 * Au12x0/Au1550 PSC ALSA ASoC audio support.
3 *
4 * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
5 *	Manuel Lauss <manuel.lauss@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * Au1xxx-PSC I2S glue.
12 *
13 * NOTE: all of these drivers can only work with a SINGLE instance
14 *	 of a PSC. Multiple independent audio devices are impossible
15 *	 with ASoC v1.
16 * NOTE: so far only PSC slave mode (bit- and frameclock) is supported.
17 */
18
19#include <linux/init.h>
20#include <linux/module.h>
21#include <linux/slab.h>
22#include <linux/suspend.h>
23#include <sound/core.h>
24#include <sound/pcm.h>
25#include <sound/initval.h>
26#include <sound/soc.h>
27#include <asm/mach-au1x00/au1000.h>
28#include <asm/mach-au1x00/au1xxx_psc.h>
29
30#include "psc.h"
31
32/* supported I2S DAI hardware formats */
33#define AU1XPSC_I2S_DAIFMT \
34	(SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J |	\
35	 SND_SOC_DAIFMT_NB_NF)
36
37/* supported I2S direction */
38#define AU1XPSC_I2S_DIR \
39	(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
40
41#define AU1XPSC_I2S_RATES \
42	SNDRV_PCM_RATE_8000_192000
43
44#define AU1XPSC_I2S_FMTS \
45	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
46
47#define I2SSTAT_BUSY(stype)	\
48	((stype) == PCM_TX ? PSC_I2SSTAT_TB : PSC_I2SSTAT_RB)
49#define I2SPCR_START(stype)	\
50	((stype) == PCM_TX ? PSC_I2SPCR_TS : PSC_I2SPCR_RS)
51#define I2SPCR_STOP(stype)	\
52	((stype) == PCM_TX ? PSC_I2SPCR_TP : PSC_I2SPCR_RP)
53#define I2SPCR_CLRFIFO(stype)	\
54	((stype) == PCM_TX ? PSC_I2SPCR_TC : PSC_I2SPCR_RC)
55
56
57/* instance data. There can be only one, MacLeod!!!! */
58static struct au1xpsc_audio_data *au1xpsc_i2s_workdata;
59
60static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
61			       unsigned int fmt)
62{
63	struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
64	unsigned long ct;
65	int ret;
66
67	ret = -EINVAL;
68
69	ct = pscdata->cfg;
70
71	ct &= ~(PSC_I2SCFG_XM | PSC_I2SCFG_MLJ);	/* left-justified */
72	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
73	case SND_SOC_DAIFMT_I2S:
74		ct |= PSC_I2SCFG_XM;	/* enable I2S mode */
75		break;
76	case SND_SOC_DAIFMT_MSB:
77		break;
78	case SND_SOC_DAIFMT_LSB:
79		ct |= PSC_I2SCFG_MLJ;	/* LSB (right-) justified */
80		break;
81	default:
82		goto out;
83	}
84
85	ct &= ~(PSC_I2SCFG_BI | PSC_I2SCFG_WI);		/* IB-IF */
86	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
87	case SND_SOC_DAIFMT_NB_NF:
88		ct |= PSC_I2SCFG_BI | PSC_I2SCFG_WI;
89		break;
90	case SND_SOC_DAIFMT_NB_IF:
91		ct |= PSC_I2SCFG_BI;
92		break;
93	case SND_SOC_DAIFMT_IB_NF:
94		ct |= PSC_I2SCFG_WI;
95		break;
96	case SND_SOC_DAIFMT_IB_IF:
97		break;
98	default:
99		goto out;
100	}
101
102	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
103	case SND_SOC_DAIFMT_CBM_CFM:	/* CODEC master */
104		ct |= PSC_I2SCFG_MS;	/* PSC I2S slave mode */
105		break;
106	case SND_SOC_DAIFMT_CBS_CFS:	/* CODEC slave */
107		ct &= ~PSC_I2SCFG_MS;	/* PSC I2S Master mode */
108		break;
109	default:
110		goto out;
111	}
112
113	pscdata->cfg = ct;
114	ret = 0;
115out:
116	return ret;
117}
118
119static int au1xpsc_i2s_hw_params(struct snd_pcm_substream *substream,
120				 struct snd_pcm_hw_params *params,
121				 struct snd_soc_dai *dai)
122{
123	struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
124
125	int cfgbits;
126	unsigned long stat;
127
128	/* check if the PSC is already streaming data */
129	stat = au_readl(I2S_STAT(pscdata));
130	if (stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB)) {
131		/* reject parameters not currently set up in hardware */
132		cfgbits = au_readl(I2S_CFG(pscdata));
133		if ((PSC_I2SCFG_GET_LEN(cfgbits) != params->msbits) ||
134		    (params_rate(params) != pscdata->rate))
135			return -EINVAL;
136	} else {
137		/* set sample bitdepth */
138		pscdata->cfg &= ~(0x1f << 4);
139		pscdata->cfg |= PSC_I2SCFG_SET_LEN(params->msbits);
140		/* remember current rate for other stream */
141		pscdata->rate = params_rate(params);
142	}
143	return 0;
144}
145
146/* Configure PSC late:  on my devel systems the codec  is I2S master and
147 * supplies the i2sbitclock __AND__ i2sMclk (!) to the PSC unit.  ASoC
148 * uses aggressive PM and  switches the codec off  when it is not in use
149 * which also means the PSC unit doesn't get any clocks and is therefore
150 * dead. That's why this chunk here gets called from the trigger callback
151 * because I can be reasonably certain the codec is driving the clocks.
152 */
153static int au1xpsc_i2s_configure(struct au1xpsc_audio_data *pscdata)
154{
155	unsigned long tmo;
156
157	/* bring PSC out of sleep, and configure I2S unit */
158	au_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
159	au_sync();
160
161	tmo = 1000000;
162	while (!(au_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_SR) && tmo)
163		tmo--;
164
165	if (!tmo)
166		goto psc_err;
167
168	au_writel(0, I2S_CFG(pscdata));
169	au_sync();
170	au_writel(pscdata->cfg | PSC_I2SCFG_DE_ENABLE, I2S_CFG(pscdata));
171	au_sync();
172
173	/* wait for I2S controller to become ready */
174	tmo = 1000000;
175	while (!(au_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_DR) && tmo)
176		tmo--;
177
178	if (tmo)
179		return 0;
180
181psc_err:
182	au_writel(0, I2S_CFG(pscdata));
183	au_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
184	au_sync();
185	return -ETIMEDOUT;
186}
187
188static int au1xpsc_i2s_start(struct au1xpsc_audio_data *pscdata, int stype)
189{
190	unsigned long tmo, stat;
191	int ret;
192
193	ret = 0;
194
195	/* if both TX and RX are idle, configure the PSC  */
196	stat = au_readl(I2S_STAT(pscdata));
197	if (!(stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB))) {
198		ret = au1xpsc_i2s_configure(pscdata);
199		if (ret)
200			goto out;
201	}
202
203	au_writel(I2SPCR_CLRFIFO(stype), I2S_PCR(pscdata));
204	au_sync();
205	au_writel(I2SPCR_START(stype), I2S_PCR(pscdata));
206	au_sync();
207
208	/* wait for start confirmation */
209	tmo = 1000000;
210	while (!(au_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
211		tmo--;
212
213	if (!tmo) {
214		au_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
215		au_sync();
216		ret = -ETIMEDOUT;
217	}
218out:
219	return ret;
220}
221
222static int au1xpsc_i2s_stop(struct au1xpsc_audio_data *pscdata, int stype)
223{
224	unsigned long tmo, stat;
225
226	au_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata));
227	au_sync();
228
229	/* wait for stop confirmation */
230	tmo = 1000000;
231	while ((au_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo)
232		tmo--;
233
234	/* if both TX and RX are idle, disable PSC */
235	stat = au_readl(I2S_STAT(pscdata));
236	if (!(stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB))) {
237		au_writel(0, I2S_CFG(pscdata));
238		au_sync();
239		au_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
240		au_sync();
241	}
242	return 0;
243}
244
245static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
246			       struct snd_soc_dai *dai)
247{
248	struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
249	int ret, stype = SUBSTREAM_TYPE(substream);
250
251	switch (cmd) {
252	case SNDRV_PCM_TRIGGER_START:
253	case SNDRV_PCM_TRIGGER_RESUME:
254		ret = au1xpsc_i2s_start(pscdata, stype);
255		break;
256	case SNDRV_PCM_TRIGGER_STOP:
257	case SNDRV_PCM_TRIGGER_SUSPEND:
258		ret = au1xpsc_i2s_stop(pscdata, stype);
259		break;
260	default:
261		ret = -EINVAL;
262	}
263	return ret;
264}
265
266static int au1xpsc_i2s_probe(struct platform_device *pdev,
267			     struct snd_soc_dai *dai)
268{
269	return 	au1xpsc_i2s_workdata ? 0 : -ENODEV;
270}
271
272static void au1xpsc_i2s_remove(struct platform_device *pdev,
273			       struct snd_soc_dai *dai)
274{
275}
276
277static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
278	.trigger	= au1xpsc_i2s_trigger,
279	.hw_params	= au1xpsc_i2s_hw_params,
280	.set_fmt	= au1xpsc_i2s_set_fmt,
281};
282
283struct snd_soc_dai au1xpsc_i2s_dai = {
284	.name			= "au1xpsc_i2s",
285	.probe			= au1xpsc_i2s_probe,
286	.remove			= au1xpsc_i2s_remove,
287	.playback = {
288		.rates		= AU1XPSC_I2S_RATES,
289		.formats	= AU1XPSC_I2S_FMTS,
290		.channels_min	= 2,
291		.channels_max	= 8,	/* 2 without external help */
292	},
293	.capture = {
294		.rates		= AU1XPSC_I2S_RATES,
295		.formats	= AU1XPSC_I2S_FMTS,
296		.channels_min	= 2,
297		.channels_max	= 8,	/* 2 without external help */
298	},
299	.ops = &au1xpsc_i2s_dai_ops,
300};
301EXPORT_SYMBOL(au1xpsc_i2s_dai);
302
303static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev)
304{
305	struct resource *r;
306	unsigned long sel;
307	int ret;
308	struct au1xpsc_audio_data *wd;
309
310	if (au1xpsc_i2s_workdata)
311		return -EBUSY;
312
313	wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
314	if (!wd)
315		return -ENOMEM;
316
317	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
318	if (!r) {
319		ret = -ENODEV;
320		goto out0;
321	}
322
323	ret = -EBUSY;
324	if (!request_mem_region(r->start, resource_size(r), pdev->name))
325		goto out0;
326
327	wd->mmio = ioremap(r->start, resource_size(r));
328	if (!wd->mmio)
329		goto out1;
330
331	/* preserve PSC clock source set up by platform (dev.platform_data
332	 * is already occupied by soc layer)
333	 */
334	sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
335	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
336	au_sync();
337	au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(wd));
338	au_writel(0, I2S_CFG(wd));
339	au_sync();
340
341	/* preconfigure: set max rx/tx fifo depths */
342	wd->cfg |= PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
343
344	/* don't wait for I2S core to become ready now; clocks may not
345	 * be running yet; depending on clock input for PSC a wait might
346	 * time out.
347	 */
348
349	ret = snd_soc_register_dai(&au1xpsc_i2s_dai);
350	if (ret)
351		goto out1;
352
353	/* finally add the DMA device for this PSC */
354	wd->dmapd = au1xpsc_pcm_add(pdev);
355	if (wd->dmapd) {
356		platform_set_drvdata(pdev, wd);
357		au1xpsc_i2s_workdata = wd;
358		return 0;
359	}
360
361	snd_soc_unregister_dai(&au1xpsc_i2s_dai);
362out1:
363	release_mem_region(r->start, resource_size(r));
364out0:
365	kfree(wd);
366	return ret;
367}
368
369static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev)
370{
371	struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
372	struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
373
374	if (wd->dmapd)
375		au1xpsc_pcm_destroy(wd->dmapd);
376
377	snd_soc_unregister_dai(&au1xpsc_i2s_dai);
378
379	au_writel(0, I2S_CFG(wd));
380	au_sync();
381	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
382	au_sync();
383
384	iounmap(wd->mmio);
385	release_mem_region(r->start, resource_size(r));
386	kfree(wd);
387
388	au1xpsc_i2s_workdata = NULL;	/* MDEV */
389
390	return 0;
391}
392
393#ifdef CONFIG_PM
394static int au1xpsc_i2s_drvsuspend(struct device *dev)
395{
396	struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
397
398	/* save interesting register and disable PSC */
399	wd->pm[0] = au_readl(PSC_SEL(wd));
400
401	au_writel(0, I2S_CFG(wd));
402	au_sync();
403	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
404	au_sync();
405
406	return 0;
407}
408
409static int au1xpsc_i2s_drvresume(struct device *dev)
410{
411	struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
412
413	/* select I2S mode and PSC clock */
414	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
415	au_sync();
416	au_writel(0, PSC_SEL(wd));
417	au_sync();
418	au_writel(wd->pm[0], PSC_SEL(wd));
419	au_sync();
420
421	return 0;
422}
423
424static struct dev_pm_ops au1xpsci2s_pmops = {
425	.suspend	= au1xpsc_i2s_drvsuspend,
426	.resume		= au1xpsc_i2s_drvresume,
427};
428
429#define AU1XPSCI2S_PMOPS &au1xpsci2s_pmops
430
431#else
432
433#define AU1XPSCI2S_PMOPS NULL
434
435#endif
436
437static struct platform_driver au1xpsc_i2s_driver = {
438	.driver		= {
439		.name	= "au1xpsc_i2s",
440		.owner	= THIS_MODULE,
441		.pm	= AU1XPSCI2S_PMOPS,
442	},
443	.probe		= au1xpsc_i2s_drvprobe,
444	.remove		= __devexit_p(au1xpsc_i2s_drvremove),
445};
446
447static int __init au1xpsc_i2s_load(void)
448{
449	au1xpsc_i2s_workdata = NULL;
450	return platform_driver_register(&au1xpsc_i2s_driver);
451}
452
453static void __exit au1xpsc_i2s_unload(void)
454{
455	platform_driver_unregister(&au1xpsc_i2s_driver);
456}
457
458module_init(au1xpsc_i2s_load);
459module_exit(au1xpsc_i2s_unload);
460
461MODULE_LICENSE("GPL");
462MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver");
463MODULE_AUTHOR("Manuel Lauss");
464