xref: /openbmc/linux/sound/arm/pxa2xx-ac97.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22c484df0STakashi Iwai /*
32c484df0STakashi Iwai  * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip.
42c484df0STakashi Iwai  *
52c484df0STakashi Iwai  * Author:	Nicolas Pitre
62c484df0STakashi Iwai  * Created:	Dec 02, 2004
72c484df0STakashi Iwai  * Copyright:	MontaVista Software Inc.
82c484df0STakashi Iwai  */
92c484df0STakashi Iwai 
102c484df0STakashi Iwai #include <linux/init.h>
1123019a73SRob Herring #include <linux/io.h>
122c484df0STakashi Iwai #include <linux/module.h>
13d052d1beSRussell King #include <linux/platform_device.h>
14d65a1458SDaniel Mack #include <linux/dmaengine.h>
1595acb005SDaniel Mack #include <linux/dma-mapping.h>
162c484df0STakashi Iwai 
172c484df0STakashi Iwai #include <sound/core.h>
182c484df0STakashi Iwai #include <sound/pcm.h>
192c484df0STakashi Iwai #include <sound/ac97_codec.h>
202c484df0STakashi Iwai #include <sound/initval.h>
219c636342SDmitry Baryshkov #include <sound/pxa2xx-lib.h>
22d65a1458SDaniel Mack #include <sound/dmaengine_pcm.h>
232c484df0STakashi Iwai 
2422f08665SArnd Bergmann #include <linux/platform_data/asoc-pxa.h>
252c484df0STakashi Iwai 
pxa2xx_ac97_legacy_reset(struct snd_ac97 * ac97)266f8acad6SRobert Jarzmik static void pxa2xx_ac97_legacy_reset(struct snd_ac97 *ac97)
272c484df0STakashi Iwai {
286f8acad6SRobert Jarzmik 	if (!pxa2xx_ac97_try_cold_reset())
296f8acad6SRobert Jarzmik 		pxa2xx_ac97_try_warm_reset();
306f8acad6SRobert Jarzmik 
316f8acad6SRobert Jarzmik 	pxa2xx_ac97_finish_reset();
322c484df0STakashi Iwai }
332c484df0STakashi Iwai 
pxa2xx_ac97_legacy_read(struct snd_ac97 * ac97,unsigned short reg)346f8acad6SRobert Jarzmik static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97,
356f8acad6SRobert Jarzmik 					      unsigned short reg)
366f8acad6SRobert Jarzmik {
376f8acad6SRobert Jarzmik 	int ret;
386f8acad6SRobert Jarzmik 
396f8acad6SRobert Jarzmik 	ret = pxa2xx_ac97_read(ac97->num, reg);
406f8acad6SRobert Jarzmik 	if (ret < 0)
416f8acad6SRobert Jarzmik 		return 0;
426f8acad6SRobert Jarzmik 	else
436f8acad6SRobert Jarzmik 		return (unsigned short)(ret & 0xffff);
446f8acad6SRobert Jarzmik }
456f8acad6SRobert Jarzmik 
pxa2xx_ac97_legacy_write(struct snd_ac97 * ac97,unsigned short reg,unsigned short val)466f8acad6SRobert Jarzmik static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97,
476f8acad6SRobert Jarzmik 				     unsigned short reg, unsigned short val)
486f8acad6SRobert Jarzmik {
49f4a85e00Szuoqilin 	pxa2xx_ac97_write(ac97->num, reg, val);
502c484df0STakashi Iwai }
512c484df0STakashi Iwai 
5274d2bae3STakashi Iwai static const struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
536f8acad6SRobert Jarzmik 	.read	= pxa2xx_ac97_legacy_read,
546f8acad6SRobert Jarzmik 	.write	= pxa2xx_ac97_legacy_write,
556f8acad6SRobert Jarzmik 	.reset	= pxa2xx_ac97_legacy_reset,
562c484df0STakashi Iwai };
572c484df0STakashi Iwai 
58d18f8376STakashi Iwai static struct snd_pcm *pxa2xx_ac97_pcm;
59d18f8376STakashi Iwai static struct snd_ac97 *pxa2xx_ac97_ac97;
602c484df0STakashi Iwai 
pxa2xx_ac97_pcm_open(struct snd_pcm_substream * substream)6195acb005SDaniel Mack static int pxa2xx_ac97_pcm_open(struct snd_pcm_substream *substream)
622c484df0STakashi Iwai {
63d18f8376STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
642c484df0STakashi Iwai 	pxa2xx_audio_ops_t *platform_ops;
6595acb005SDaniel Mack 	int ret, i;
6695acb005SDaniel Mack 
67a7160670SDaniel Mack 	ret = pxa2xx_pcm_open(substream);
6895acb005SDaniel Mack 	if (ret)
6995acb005SDaniel Mack 		return ret;
702c484df0STakashi Iwai 
712c484df0STakashi Iwai 	runtime->hw.channels_min = 2;
722c484df0STakashi Iwai 	runtime->hw.channels_max = 2;
732c484df0STakashi Iwai 
7495acb005SDaniel Mack 	i = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
752c484df0STakashi Iwai 		AC97_RATES_FRONT_DAC : AC97_RATES_ADC;
7695acb005SDaniel Mack 	runtime->hw.rates = pxa2xx_ac97_ac97->rates[i];
772c484df0STakashi Iwai 	snd_pcm_limit_hw_rates(runtime);
782c484df0STakashi Iwai 
792c484df0STakashi Iwai 	platform_ops = substream->pcm->card->dev->platform_data;
8095acb005SDaniel Mack 	if (platform_ops && platform_ops->startup) {
8195acb005SDaniel Mack 		ret = platform_ops->startup(substream, platform_ops->priv);
8295acb005SDaniel Mack 		if (ret < 0)
83a7160670SDaniel Mack 			pxa2xx_pcm_close(substream);
842c484df0STakashi Iwai 	}
852c484df0STakashi Iwai 
8695acb005SDaniel Mack 	return ret;
8795acb005SDaniel Mack }
8895acb005SDaniel Mack 
pxa2xx_ac97_pcm_close(struct snd_pcm_substream * substream)8995acb005SDaniel Mack static int pxa2xx_ac97_pcm_close(struct snd_pcm_substream *substream)
902c484df0STakashi Iwai {
912c484df0STakashi Iwai 	pxa2xx_audio_ops_t *platform_ops;
922c484df0STakashi Iwai 
932c484df0STakashi Iwai 	platform_ops = substream->pcm->card->dev->platform_data;
942c484df0STakashi Iwai 	if (platform_ops && platform_ops->shutdown)
952c484df0STakashi Iwai 		platform_ops->shutdown(substream, platform_ops->priv);
9695acb005SDaniel Mack 
9795acb005SDaniel Mack 	return 0;
982c484df0STakashi Iwai }
992c484df0STakashi Iwai 
pxa2xx_ac97_pcm_prepare(struct snd_pcm_substream * substream)100d18f8376STakashi Iwai static int pxa2xx_ac97_pcm_prepare(struct snd_pcm_substream *substream)
1012c484df0STakashi Iwai {
102d18f8376STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
1032c484df0STakashi Iwai 	int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
1042c484df0STakashi Iwai 		  AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
10595acb005SDaniel Mack 	int ret;
10695acb005SDaniel Mack 
107a7160670SDaniel Mack 	ret = pxa2xx_pcm_prepare(substream);
10895acb005SDaniel Mack 	if (ret < 0)
10995acb005SDaniel Mack 		return ret;
11095acb005SDaniel Mack 
1112c484df0STakashi Iwai 	return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate);
1122c484df0STakashi Iwai }
1132c484df0STakashi Iwai 
114d34e4e00STakashi Iwai #ifdef CONFIG_PM_SLEEP
1152c484df0STakashi Iwai 
pxa2xx_ac97_do_suspend(struct snd_card * card)116284e7ca7STakashi Iwai static int pxa2xx_ac97_do_suspend(struct snd_card *card)
1172c484df0STakashi Iwai {
1182c484df0STakashi Iwai 	pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
119792a6c51STakashi Iwai 
120792a6c51STakashi Iwai 	snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
1212c484df0STakashi Iwai 	snd_ac97_suspend(pxa2xx_ac97_ac97);
1222c484df0STakashi Iwai 	if (platform_ops && platform_ops->suspend)
1232c484df0STakashi Iwai 		platform_ops->suspend(platform_ops->priv);
1242c484df0STakashi Iwai 
1259c636342SDmitry Baryshkov 	return pxa2xx_ac97_hw_suspend();
1262c484df0STakashi Iwai }
1272c484df0STakashi Iwai 
pxa2xx_ac97_do_resume(struct snd_card * card)128d18f8376STakashi Iwai static int pxa2xx_ac97_do_resume(struct snd_card *card)
1292c484df0STakashi Iwai {
1302c484df0STakashi Iwai 	pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
1319c636342SDmitry Baryshkov 	int rc;
132792a6c51STakashi Iwai 
1339c636342SDmitry Baryshkov 	rc = pxa2xx_ac97_hw_resume();
1349c636342SDmitry Baryshkov 	if (rc)
1359c636342SDmitry Baryshkov 		return rc;
1369c636342SDmitry Baryshkov 
1372c484df0STakashi Iwai 	if (platform_ops && platform_ops->resume)
1382c484df0STakashi Iwai 		platform_ops->resume(platform_ops->priv);
1392c484df0STakashi Iwai 	snd_ac97_resume(pxa2xx_ac97_ac97);
1402c484df0STakashi Iwai 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
1412c484df0STakashi Iwai 
1422c484df0STakashi Iwai 	return 0;
1432c484df0STakashi Iwai }
1442c484df0STakashi Iwai 
pxa2xx_ac97_suspend(struct device * dev)1452ba9fd0dSMike Rapoport static int pxa2xx_ac97_suspend(struct device *dev)
1462c484df0STakashi Iwai {
1472ba9fd0dSMike Rapoport 	struct snd_card *card = dev_get_drvdata(dev);
1482c484df0STakashi Iwai 	int ret = 0;
1492c484df0STakashi Iwai 
1509480e307SRussell King 	if (card)
151284e7ca7STakashi Iwai 		ret = pxa2xx_ac97_do_suspend(card);
1522c484df0STakashi Iwai 
1532c484df0STakashi Iwai 	return ret;
1542c484df0STakashi Iwai }
1552c484df0STakashi Iwai 
pxa2xx_ac97_resume(struct device * dev)1562ba9fd0dSMike Rapoport static int pxa2xx_ac97_resume(struct device *dev)
1572c484df0STakashi Iwai {
1582ba9fd0dSMike Rapoport 	struct snd_card *card = dev_get_drvdata(dev);
1592c484df0STakashi Iwai 	int ret = 0;
1602c484df0STakashi Iwai 
1619480e307SRussell King 	if (card)
162a55bfdc5SDirk Opfer 		ret = pxa2xx_ac97_do_resume(card);
1632c484df0STakashi Iwai 
1642c484df0STakashi Iwai 	return ret;
1652c484df0STakashi Iwai }
1662c484df0STakashi Iwai 
167284e7ca7STakashi Iwai static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_suspend, pxa2xx_ac97_resume);
1682c484df0STakashi Iwai #endif
1692c484df0STakashi Iwai 
1707afd1b0bSDaniel Mack static const struct snd_pcm_ops pxa2xx_ac97_pcm_ops = {
17195acb005SDaniel Mack 	.open		= pxa2xx_ac97_pcm_open,
17295acb005SDaniel Mack 	.close		= pxa2xx_ac97_pcm_close,
173a7160670SDaniel Mack 	.hw_params	= pxa2xx_pcm_hw_params,
17495acb005SDaniel Mack 	.prepare	= pxa2xx_ac97_pcm_prepare,
17595acb005SDaniel Mack 	.trigger	= pxa2xx_pcm_trigger,
17695acb005SDaniel Mack 	.pointer	= pxa2xx_pcm_pointer,
17795acb005SDaniel Mack };
17895acb005SDaniel Mack 
17995acb005SDaniel Mack 
pxa2xx_ac97_pcm_new(struct snd_card * card)18095acb005SDaniel Mack static int pxa2xx_ac97_pcm_new(struct snd_card *card)
18195acb005SDaniel Mack {
18295acb005SDaniel Mack 	struct snd_pcm *pcm;
1837f2da3d7STakashi Iwai 	int ret;
18495acb005SDaniel Mack 
18595acb005SDaniel Mack 	ret = snd_pcm_new(card, "PXA2xx-PCM", 0, 1, 1, &pcm);
18695acb005SDaniel Mack 	if (ret)
18795acb005SDaniel Mack 		goto out;
18895acb005SDaniel Mack 
18995acb005SDaniel Mack 	ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
19095acb005SDaniel Mack 	if (ret)
19195acb005SDaniel Mack 		goto out;
19295acb005SDaniel Mack 
1937f2da3d7STakashi Iwai 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pxa2xx_ac97_pcm_ops);
1947f2da3d7STakashi Iwai 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pxa2xx_ac97_pcm_ops);
1957f2da3d7STakashi Iwai 	ret = pxa2xx_pcm_preallocate_dma_buffer(pcm);
19695acb005SDaniel Mack 	if (ret)
19795acb005SDaniel Mack 		goto out;
19895acb005SDaniel Mack 
19995acb005SDaniel Mack 	pxa2xx_ac97_pcm = pcm;
20095acb005SDaniel Mack 	ret = 0;
20195acb005SDaniel Mack 
20295acb005SDaniel Mack  out:
20395acb005SDaniel Mack 	return ret;
20495acb005SDaniel Mack }
20595acb005SDaniel Mack 
pxa2xx_ac97_probe(struct platform_device * dev)206e21596bbSBill Pemberton static int pxa2xx_ac97_probe(struct platform_device *dev)
2072c484df0STakashi Iwai {
208d18f8376STakashi Iwai 	struct snd_card *card;
209d18f8376STakashi Iwai 	struct snd_ac97_bus *ac97_bus;
210d18f8376STakashi Iwai 	struct snd_ac97_template ac97_template;
2112c484df0STakashi Iwai 	int ret;
2124ac0478fSMarek Vasut 	pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;
2134ac0478fSMarek Vasut 
2144ac0478fSMarek Vasut 	if (dev->id >= 0) {
2154ac0478fSMarek Vasut 		dev_err(&dev->dev, "PXA2xx has only one AC97 port.\n");
2164ac0478fSMarek Vasut 		ret = -ENXIO;
2174ac0478fSMarek Vasut 		goto err_dev;
2184ac0478fSMarek Vasut 	}
2192c484df0STakashi Iwai 
2204a875580STakashi Iwai 	ret = snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
221bd7dd77cSTakashi Iwai 			   THIS_MODULE, 0, &card);
222bd7dd77cSTakashi Iwai 	if (ret < 0)
2232c484df0STakashi Iwai 		goto err;
2242c484df0STakashi Iwai 
22575b1a8f9SJoe Perches 	strscpy(card->driver, dev->dev.driver->name, sizeof(card->driver));
2262c484df0STakashi Iwai 
22795acb005SDaniel Mack 	ret = pxa2xx_ac97_pcm_new(card);
2282c484df0STakashi Iwai 	if (ret)
2292c484df0STakashi Iwai 		goto err;
2302c484df0STakashi Iwai 
2319c636342SDmitry Baryshkov 	ret = pxa2xx_ac97_hw_probe(dev);
2329c636342SDmitry Baryshkov 	if (ret)
2332c484df0STakashi Iwai 		goto err;
2342c484df0STakashi Iwai 
2352c484df0STakashi Iwai 	ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus);
2362c484df0STakashi Iwai 	if (ret)
2379c636342SDmitry Baryshkov 		goto err_remove;
2382c484df0STakashi Iwai 	memset(&ac97_template, 0, sizeof(ac97_template));
2392c484df0STakashi Iwai 	ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97);
2402c484df0STakashi Iwai 	if (ret)
2419c636342SDmitry Baryshkov 		goto err_remove;
2422c484df0STakashi Iwai 
2432c484df0STakashi Iwai 	snprintf(card->shortname, sizeof(card->shortname),
2442c484df0STakashi Iwai 		 "%s", snd_ac97_get_short_name(pxa2xx_ac97_ac97));
2452c484df0STakashi Iwai 	snprintf(card->longname, sizeof(card->longname),
2463ae5eaecSRussell King 		 "%s (%s)", dev->dev.driver->name, card->mixername);
2472c484df0STakashi Iwai 
248367da152SRobert Schwebel 	if (pdata && pdata->codec_pdata[0])
249e2365bf3SMarek Vasut 		snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]);
2502c484df0STakashi Iwai 	ret = snd_card_register(card);
2512c484df0STakashi Iwai 	if (ret == 0) {
2523ae5eaecSRussell King 		platform_set_drvdata(dev, card);
2532c484df0STakashi Iwai 		return 0;
2542c484df0STakashi Iwai 	}
2552c484df0STakashi Iwai 
2569c636342SDmitry Baryshkov err_remove:
2579c636342SDmitry Baryshkov 	pxa2xx_ac97_hw_remove(dev);
2582c484df0STakashi Iwai err:
2592c484df0STakashi Iwai 	if (card)
2602c484df0STakashi Iwai 		snd_card_free(card);
2614ac0478fSMarek Vasut err_dev:
2622c484df0STakashi Iwai 	return ret;
2632c484df0STakashi Iwai }
2642c484df0STakashi Iwai 
pxa2xx_ac97_remove(struct platform_device * dev)265*3210e62aSUwe Kleine-König static void pxa2xx_ac97_remove(struct platform_device *dev)
2662c484df0STakashi Iwai {
267d18f8376STakashi Iwai 	struct snd_card *card = platform_get_drvdata(dev);
2682c484df0STakashi Iwai 
2692c484df0STakashi Iwai 	if (card) {
2702c484df0STakashi Iwai 		snd_card_free(card);
2719c636342SDmitry Baryshkov 		pxa2xx_ac97_hw_remove(dev);
2722c484df0STakashi Iwai 	}
2732c484df0STakashi Iwai }
2742c484df0STakashi Iwai 
2753ae5eaecSRussell King static struct platform_driver pxa2xx_ac97_driver = {
2762c484df0STakashi Iwai 	.probe		= pxa2xx_ac97_probe,
277*3210e62aSUwe Kleine-König 	.remove_new	= pxa2xx_ac97_remove,
2783ae5eaecSRussell King 	.driver		= {
2793ae5eaecSRussell King 		.name	= "pxa2xx-ac97",
280d34e4e00STakashi Iwai #ifdef CONFIG_PM_SLEEP
2812ba9fd0dSMike Rapoport 		.pm	= &pxa2xx_ac97_pm_ops,
2822ba9fd0dSMike Rapoport #endif
2833ae5eaecSRussell King 	},
2842c484df0STakashi Iwai };
2852c484df0STakashi Iwai 
286a09452eeSAxel Lin module_platform_driver(pxa2xx_ac97_driver);
2872c484df0STakashi Iwai 
2882c484df0STakashi Iwai MODULE_AUTHOR("Nicolas Pitre");
2892c484df0STakashi Iwai MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
2902c484df0STakashi Iwai MODULE_LICENSE("GPL");
2918b45a209SKay Sievers MODULE_ALIAS("platform:pxa2xx-ac97");
292