psc-ac97.c 11.6 KB
Newer Older
1 2 3
/*
 * Au12x0/Au1550 PSC ALSA ASoC audio support.
 *
M
Manuel Lauss 已提交
4 5
 * (c) 2007-2009 MSC Vertriebsges.m.b.H.,
 *	Manuel Lauss <manuel.lauss@gmail.com>
6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * Au1xxx-PSC AC97 glue.
 *
 * NOTE: all of these drivers can only work with a SINGLE instance
 *	 of a PSC. Multiple independent audio devices are impossible
 *	 with ASoC v1.
 */

#include <linux/init.h>
#include <linux/module.h>
20
#include <linux/slab.h>
21 22
#include <linux/device.h>
#include <linux/delay.h>
M
Manuel Lauss 已提交
23
#include <linux/mutex.h>
24 25 26 27 28 29 30 31 32 33
#include <linux/suspend.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-au1x00/au1xxx_psc.h>

#include "psc.h"

M
Manuel Lauss 已提交
34 35 36
/* how often to retry failed codec register reads/writes */
#define AC97_RW_RETRIES	5

37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
#define AC97_DIR	\
	(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)

#define AC97_RATES	\
	SNDRV_PCM_RATE_8000_48000

#define AC97_FMTS	\
	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3BE)

#define AC97PCR_START(stype)	\
	((stype) == PCM_TX ? PSC_AC97PCR_TS : PSC_AC97PCR_RS)
#define AC97PCR_STOP(stype)	\
	((stype) == PCM_TX ? PSC_AC97PCR_TP : PSC_AC97PCR_RP)
#define AC97PCR_CLRFIFO(stype)	\
	((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)

M
Manuel Lauss 已提交
53 54 55
#define AC97STAT_BUSY(stype)	\
	((stype) == PCM_TX ? PSC_AC97STAT_TB : PSC_AC97STAT_RB)

56 57 58 59 60 61 62 63 64
/* instance data. There can be only one, MacLeod!!!! */
static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;

/* AC97 controller reads codec register */
static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
					unsigned short reg)
{
	/* FIXME */
	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
65 66
	unsigned short retry, tmo;
	unsigned long data;
67

M
Manuel Lauss 已提交
68
	au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
69 70
	au_sync();

M
Manuel Lauss 已提交
71 72 73 74 75 76 77 78
	retry = AC97_RW_RETRIES;
	do {
		mutex_lock(&pscdata->lock);

		au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg),
			  AC97_CDC(pscdata));
		au_sync();

79 80 81 82 83 84
		tmo = 20;
		do {
			udelay(21);
			if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
				break;
		} while (--tmo);
85

86
		data = au_readl(AC97_CDC(pscdata));
87

M
Manuel Lauss 已提交
88 89 90 91
		au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
		au_sync();

		mutex_unlock(&pscdata->lock);
92 93 94 95

		if (reg != ((data >> 16) & 0x7f))
			tmo = 1;	/* wrong register, try again */

M
Manuel Lauss 已提交
96
	} while (--retry && !tmo);
97

98
	return retry ? data & 0xffff : 0xffff;
99 100 101 102 103 104 105 106
}

/* AC97 controller writes to codec register */
static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
				unsigned short val)
{
	/* FIXME */
	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
M
Manuel Lauss 已提交
107
	unsigned int tmo, retry;
108

M
Manuel Lauss 已提交
109
	au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
110
	au_sync();
M
Manuel Lauss 已提交
111 112 113 114 115 116 117

	retry = AC97_RW_RETRIES;
	do {
		mutex_lock(&pscdata->lock);

		au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff),
			  AC97_CDC(pscdata));
118 119
		au_sync();

120 121 122 123 124 125
		tmo = 20;
		do {
			udelay(21);
			if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
				break;
		} while (--tmo);
M
Manuel Lauss 已提交
126 127 128 129 130 131

		au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
		au_sync();

		mutex_unlock(&pscdata->lock);
	} while (--retry && !tmo);
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
}

/* AC97 controller asserts a warm reset */
static void au1xpsc_ac97_warm_reset(struct snd_ac97 *ac97)
{
	/* FIXME */
	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;

	au_writel(PSC_AC97RST_SNC, AC97_RST(pscdata));
	au_sync();
	msleep(10);
	au_writel(0, AC97_RST(pscdata));
	au_sync();
}

static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
{
	/* FIXME */
	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
	int i;

	/* disable PSC during cold reset */
	au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
	au_sync();
	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(pscdata));
	au_sync();

	/* issue cold reset */
	au_writel(PSC_AC97RST_RST, AC97_RST(pscdata));
	au_sync();
	msleep(500);
	au_writel(0, AC97_RST(pscdata));
	au_sync();

	/* enable PSC */
	au_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
	au_sync();

	/* wait for PSC to indicate it's ready */
M
Manuel Lauss 已提交
171
	i = 1000;
172
	while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i))
M
Manuel Lauss 已提交
173
		msleep(1);
174 175 176 177 178 179 180 181 182 183 184

	if (i == 0) {
		printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n");
		return;
	}

	/* enable the ac97 function */
	au_writel(pscdata->cfg | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
	au_sync();

	/* wait for AC97 core to become ready */
M
Manuel Lauss 已提交
185
	i = 1000;
186
	while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i))
M
Manuel Lauss 已提交
187
		msleep(1);
188 189 190 191 192 193 194 195 196 197 198 199 200 201
	if (i == 0)
		printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n");
}

/* AC97 controller operations */
struct snd_ac97_bus_ops soc_ac97_ops = {
	.read		= au1xpsc_ac97_read,
	.write		= au1xpsc_ac97_write,
	.reset		= au1xpsc_ac97_cold_reset,
	.warm_reset	= au1xpsc_ac97_warm_reset,
};
EXPORT_SYMBOL_GPL(soc_ac97_ops);

static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
202 203
				  struct snd_pcm_hw_params *params,
				  struct snd_soc_dai *dai)
204 205 206
{
	/* FIXME */
	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
M
Manuel Lauss 已提交
207
	unsigned long r, ro, stat;
208
	int chans, t, stype = SUBSTREAM_TYPE(substream);
209 210 211

	chans = params_channels(params);

M
Manuel Lauss 已提交
212
	r = ro = au_readl(AC97_CFG(pscdata));
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
	stat = au_readl(AC97_STAT(pscdata));

	/* already active? */
	if (stat & (PSC_AC97STAT_TB | PSC_AC97STAT_RB)) {
		/* reject parameters not currently set up */
		if ((PSC_AC97CFG_GET_LEN(r) != params->msbits) ||
		    (pscdata->rate != params_rate(params)))
			return -EINVAL;
	} else {

		/* set sample bitdepth: REG[24:21]=(BITS-2)/2 */
		r &= ~PSC_AC97CFG_LEN_MASK;
		r |= PSC_AC97CFG_SET_LEN(params->msbits);

		/* channels: enable slots for front L/R channel */
		if (stype == PCM_TX) {
			r &= ~PSC_AC97CFG_TXSLOT_MASK;
			r |= PSC_AC97CFG_TXSLOT_ENA(3);
			r |= PSC_AC97CFG_TXSLOT_ENA(4);
		} else {
			r &= ~PSC_AC97CFG_RXSLOT_MASK;
			r |= PSC_AC97CFG_RXSLOT_ENA(3);
			r |= PSC_AC97CFG_RXSLOT_ENA(4);
		}

M
Manuel Lauss 已提交
238 239 240 241 242 243 244 245 246 247 248 249
		/* do we need to poke the hardware? */
		if (!(r ^ ro))
			goto out;

		/* ac97 engine is about to be disabled */
		mutex_lock(&pscdata->lock);

		/* disable AC97 device controller first... */
		au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
		au_sync();

		/* ...wait for it... */
250 251 252 253 254 255
		t = 100;
		while ((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) && --t)
			msleep(1);

		if (!t)
			printk(KERN_ERR "PSC-AC97: can't disable!\n");
M
Manuel Lauss 已提交
256 257 258 259 260 261

		/* ...write config... */
		au_writel(r, AC97_CFG(pscdata));
		au_sync();

		/* ...enable the AC97 controller again... */
262 263 264
		au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
		au_sync();

M
Manuel Lauss 已提交
265
		/* ...and wait for ready bit */
266 267 268 269 270 271
		t = 100;
		while ((!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && --t)
			msleep(1);

		if (!t)
			printk(KERN_ERR "PSC-AC97: can't enable!\n");
M
Manuel Lauss 已提交
272 273 274

		mutex_unlock(&pscdata->lock);

275 276 277 278
		pscdata->cfg = r;
		pscdata->rate = params_rate(params);
	}

M
Manuel Lauss 已提交
279
out:
280 281 282 283
	return 0;
}

static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
284
				int cmd, struct snd_soc_dai *dai)
285 286 287 288 289 290 291 292 293 294
{
	/* FIXME */
	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
	int ret, stype = SUBSTREAM_TYPE(substream);

	ret = 0;

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_RESUME:
M
Manuel Lauss 已提交
295 296
		au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
		au_sync();
297 298 299 300 301 302 303
		au_writel(AC97PCR_START(stype), AC97_PCR(pscdata));
		au_sync();
		break;
	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_SUSPEND:
		au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata));
		au_sync();
M
Manuel Lauss 已提交
304 305 306 307 308 309 310

		while (au_readl(AC97_STAT(pscdata)) & AC97STAT_BUSY(stype))
			asm volatile ("nop");

		au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
		au_sync();

311 312 313 314 315 316 317
		break;
	default:
		ret = -EINVAL;
	}
	return ret;
}

318
static int au1xpsc_ac97_probe(struct snd_soc_dai *dai)
319 320 321 322 323 324 325 326 327
{
	return au1xpsc_ac97_workdata ? 0 : -ENODEV;
}

static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
	.trigger	= au1xpsc_ac97_trigger,
	.hw_params	= au1xpsc_ac97_hw_params,
};

328
struct snd_soc_dai_driver au1xpsc_ac97_dai = {
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
	.ac97_control		= 1,
	.probe			= au1xpsc_ac97_probe,
	.playback = {
		.rates		= AC97_RATES,
		.formats	= AC97_FMTS,
		.channels_min	= 2,
		.channels_max	= 2,
	},
	.capture = {
		.rates		= AC97_RATES,
		.formats	= AC97_FMTS,
		.channels_min	= 2,
		.channels_max	= 2,
	},
	.ops = &au1xpsc_ac97_dai_ops,
};
EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);

static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
348 349 350 351
{
	int ret;
	struct resource *r;
	unsigned long sel;
352
	struct au1xpsc_audio_data *wd;
353 354 355 356

	if (au1xpsc_ac97_workdata)
		return -EBUSY;

357 358
	wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
	if (!wd)
359 360
		return -ENOMEM;

361
	mutex_init(&wd->lock);
M
Manuel Lauss 已提交
362

363 364 365 366 367 368 369
	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!r) {
		ret = -ENODEV;
		goto out0;
	}

	ret = -EBUSY;
W
Wan ZongShun 已提交
370
	if (!request_mem_region(r->start, resource_size(r), pdev->name))
371 372
		goto out0;

W
Wan ZongShun 已提交
373
	wd->mmio = ioremap(r->start, resource_size(r));
374
	if (!wd->mmio)
375 376 377
		goto out1;

	/* configuration: max dma trigger threshold, enable ac97 */
378 379
	wd->cfg = PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8 |
		  PSC_AC97CFG_DE_ENABLE;
380

381 382 383
	/* preserve PSC clock source set up by platform	 */
	sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
384
	au_sync();
385
	au_writel(0, PSC_SEL(wd));
386
	au_sync();
387
	au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(wd));
388 389
	au_sync();

390
	ret = snd_soc_register_dai(&pdev->dev, &au1xpsc_ac97_dai);
391 392 393 394 395 396 397 398 399
	if (ret)
		goto out1;

	wd->dmapd = au1xpsc_pcm_add(pdev);
	if (wd->dmapd) {
		platform_set_drvdata(pdev, wd);
		au1xpsc_ac97_workdata = wd;	/* MDEV */
		return 0;
	}
400

401
	snd_soc_unregister_dai(&pdev->dev);
402
out1:
W
Wan ZongShun 已提交
403
	release_mem_region(r->start, resource_size(r));
404
out0:
405
	kfree(wd);
406 407 408
	return ret;
}

409
static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev)
410
{
411
	struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
W
Wan ZongShun 已提交
412
	struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
413 414 415 416

	if (wd->dmapd)
		au1xpsc_pcm_destroy(wd->dmapd);

417
	snd_soc_unregister_dai(&pdev->dev);
418

419
	/* disable PSC completely */
420
	au_writel(0, AC97_CFG(wd));
421
	au_sync();
422
	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
423 424
	au_sync();

425
	iounmap(wd->mmio);
W
Wan ZongShun 已提交
426
	release_mem_region(r->start, resource_size(r));
427 428 429 430 431
	kfree(wd);

	au1xpsc_ac97_workdata = NULL;	/* MDEV */

	return 0;
432 433
}

434 435
#ifdef CONFIG_PM
static int au1xpsc_ac97_drvsuspend(struct device *dev)
436
{
437 438
	struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);

439
	/* save interesting registers and disable PSC */
440
	wd->pm[0] = au_readl(PSC_SEL(wd));
441

442
	au_writel(0, AC97_CFG(wd));
443
	au_sync();
444
	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
445 446 447 448 449
	au_sync();

	return 0;
}

450
static int au1xpsc_ac97_drvresume(struct device *dev)
451
{
452 453
	struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);

454
	/* restore PSC clock config */
455
	au_writel(wd->pm[0] | PSC_SEL_PS_AC97MODE, PSC_SEL(wd));
456 457 458 459 460 461 462 463 464
	au_sync();

	/* after this point the ac97 core will cold-reset the codec.
	 * During cold-reset the PSC is reinitialized and the last
	 * configuration set up in hw_params() is restored.
	 */
	return 0;
}

465 466 467
static struct dev_pm_ops au1xpscac97_pmops = {
	.suspend	= au1xpsc_ac97_drvsuspend,
	.resume		= au1xpsc_ac97_drvresume,
468 469
};

470 471 472 473 474 475 476 477 478 479
#define AU1XPSCAC97_PMOPS &au1xpscac97_pmops

#else

#define AU1XPSCAC97_PMOPS NULL

#endif

static struct platform_driver au1xpsc_ac97_driver = {
	.driver	= {
480
		.name	= "au1xpsc-ac97",
481 482
		.owner	= THIS_MODULE,
		.pm	= AU1XPSCAC97_PMOPS,
483
	},
484 485
	.probe		= au1xpsc_ac97_drvprobe,
	.remove		= __devexit_p(au1xpsc_ac97_drvremove),
486 487
};

488
static int __init au1xpsc_ac97_load(void)
489 490
{
	au1xpsc_ac97_workdata = NULL;
491
	return platform_driver_register(&au1xpsc_ac97_driver);
492 493
}

494
static void __exit au1xpsc_ac97_unload(void)
495
{
496
	platform_driver_unregister(&au1xpsc_ac97_driver);
497 498
}

499 500
module_init(au1xpsc_ac97_load);
module_exit(au1xpsc_ac97_unload);
501 502 503

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
504 505
MODULE_AUTHOR("Manuel Lauss");