1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 275b41027SLiam Girdwood /* 375b41027SLiam Girdwood * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip. 475b41027SLiam Girdwood * 575b41027SLiam Girdwood * Author: Nicolas Pitre 675b41027SLiam Girdwood * Created: Dec 02, 2004 775b41027SLiam Girdwood * Copyright: MontaVista Software Inc. 875b41027SLiam Girdwood */ 975b41027SLiam Girdwood 1075b41027SLiam Girdwood #include <linux/init.h> 1123019a73SRob Herring #include <linux/io.h> 1275b41027SLiam Girdwood #include <linux/module.h> 1375b41027SLiam Girdwood #include <linux/platform_device.h> 14d65a1458SDaniel Mack #include <linux/dmaengine.h> 1558ceb57eSDaniel Mack #include <linux/dma/pxa-dma.h> 1675b41027SLiam Girdwood 171c8bc7b3SRobert Jarzmik #include <sound/ac97/controller.h> 1875b41027SLiam Girdwood #include <sound/core.h> 1975b41027SLiam Girdwood #include <sound/ac97_codec.h> 2075b41027SLiam Girdwood #include <sound/soc.h> 219c636342SDmitry Baryshkov #include <sound/pxa2xx-lib.h> 22d65a1458SDaniel Mack #include <sound/dmaengine_pcm.h> 2375b41027SLiam Girdwood 2408d3df8cSArnd Bergmann #include <mach/pxa-regs.h> 251f017a99SEric Miao #include <mach/regs-ac97.h> 26*22f08665SArnd Bergmann #include <linux/platform_data/asoc-pxa.h> 2775b41027SLiam Girdwood 281c8bc7b3SRobert Jarzmik static void pxa2xx_ac97_warm_reset(struct ac97_controller *adrv) 2975b41027SLiam Girdwood { 306f8acad6SRobert Jarzmik pxa2xx_ac97_try_warm_reset(); 3175b41027SLiam Girdwood 326f8acad6SRobert Jarzmik pxa2xx_ac97_finish_reset(); 3375b41027SLiam Girdwood } 3475b41027SLiam Girdwood 351c8bc7b3SRobert Jarzmik static void pxa2xx_ac97_cold_reset(struct ac97_controller *adrv) 3675b41027SLiam Girdwood { 376f8acad6SRobert Jarzmik pxa2xx_ac97_try_cold_reset(); 387a22323bSMark Brown 396f8acad6SRobert Jarzmik pxa2xx_ac97_finish_reset(); 406f8acad6SRobert Jarzmik } 416f8acad6SRobert Jarzmik 421c8bc7b3SRobert Jarzmik static int pxa2xx_ac97_read_actrl(struct ac97_controller *adrv, int slot, 436f8acad6SRobert Jarzmik unsigned short reg) 446f8acad6SRobert Jarzmik { 451c8bc7b3SRobert Jarzmik return pxa2xx_ac97_read(slot, reg); 466f8acad6SRobert Jarzmik } 476f8acad6SRobert Jarzmik 481c8bc7b3SRobert Jarzmik static int pxa2xx_ac97_write_actrl(struct ac97_controller *adrv, int slot, 496f8acad6SRobert Jarzmik unsigned short reg, unsigned short val) 506f8acad6SRobert Jarzmik { 511c8bc7b3SRobert Jarzmik return pxa2xx_ac97_write(slot, reg, val); 5275b41027SLiam Girdwood } 5375b41027SLiam Girdwood 541c8bc7b3SRobert Jarzmik static struct ac97_controller_ops pxa2xx_ac97_ops = { 551c8bc7b3SRobert Jarzmik .read = pxa2xx_ac97_read_actrl, 561c8bc7b3SRobert Jarzmik .write = pxa2xx_ac97_write_actrl, 5775b41027SLiam Girdwood .warm_reset = pxa2xx_ac97_warm_reset, 5875b41027SLiam Girdwood .reset = pxa2xx_ac97_cold_reset, 5975b41027SLiam Girdwood }; 6075b41027SLiam Girdwood 61d65a1458SDaniel Mack static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { 62d65a1458SDaniel Mack .addr = __PREG(PCDR), 63d65a1458SDaniel Mack .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 648f54061dSRobert Jarzmik .chan_name = "pcm_pcm_stereo_in", 65d65a1458SDaniel Mack .maxburst = 32, 6658ceb57eSDaniel Mack }; 6758ceb57eSDaniel Mack 68d65a1458SDaniel Mack static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { 69d65a1458SDaniel Mack .addr = __PREG(PCDR), 70d65a1458SDaniel Mack .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 718f54061dSRobert Jarzmik .chan_name = "pcm_pcm_stereo_out", 72d65a1458SDaniel Mack .maxburst = 32, 7375b41027SLiam Girdwood }; 7475b41027SLiam Girdwood 75d65a1458SDaniel Mack static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { 76d65a1458SDaniel Mack .addr = __PREG(MODR), 77d65a1458SDaniel Mack .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, 788f54061dSRobert Jarzmik .chan_name = "pcm_aux_mono_out", 79d65a1458SDaniel Mack .maxburst = 16, 8075b41027SLiam Girdwood }; 8175b41027SLiam Girdwood 82d65a1458SDaniel Mack static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { 83d65a1458SDaniel Mack .addr = __PREG(MODR), 84d65a1458SDaniel Mack .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, 858f54061dSRobert Jarzmik .chan_name = "pcm_aux_mono_in", 86d65a1458SDaniel Mack .maxburst = 16, 8775b41027SLiam Girdwood }; 8875b41027SLiam Girdwood 89d65a1458SDaniel Mack static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { 90d65a1458SDaniel Mack .addr = __PREG(MCDR), 91d65a1458SDaniel Mack .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, 928f54061dSRobert Jarzmik .chan_name = "pcm_aux_mic_mono", 93d65a1458SDaniel Mack .maxburst = 16, 9475b41027SLiam Girdwood }; 9575b41027SLiam Girdwood 9658ceb57eSDaniel Mack static int pxa2xx_ac97_hifi_startup(struct snd_pcm_substream *substream, 97f0fba2adSLiam Girdwood struct snd_soc_dai *cpu_dai) 9875b41027SLiam Girdwood { 99d65a1458SDaniel Mack struct snd_dmaengine_dai_dma_data *dma_data; 10075b41027SLiam Girdwood 10175b41027SLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1025f712b2bSDaniel Mack dma_data = &pxa2xx_ac97_pcm_stereo_out; 10375b41027SLiam Girdwood else 1045f712b2bSDaniel Mack dma_data = &pxa2xx_ac97_pcm_stereo_in; 1055f712b2bSDaniel Mack 1065f712b2bSDaniel Mack snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); 10775b41027SLiam Girdwood 10875b41027SLiam Girdwood return 0; 10975b41027SLiam Girdwood } 11075b41027SLiam Girdwood 11158ceb57eSDaniel Mack static int pxa2xx_ac97_aux_startup(struct snd_pcm_substream *substream, 112f0fba2adSLiam Girdwood struct snd_soc_dai *cpu_dai) 11375b41027SLiam Girdwood { 114d65a1458SDaniel Mack struct snd_dmaengine_dai_dma_data *dma_data; 11575b41027SLiam Girdwood 11675b41027SLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1175f712b2bSDaniel Mack dma_data = &pxa2xx_ac97_pcm_aux_mono_out; 11875b41027SLiam Girdwood else 1195f712b2bSDaniel Mack dma_data = &pxa2xx_ac97_pcm_aux_mono_in; 1205f712b2bSDaniel Mack 1215f712b2bSDaniel Mack snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); 12275b41027SLiam Girdwood 12375b41027SLiam Girdwood return 0; 12475b41027SLiam Girdwood } 12575b41027SLiam Girdwood 12658ceb57eSDaniel Mack static int pxa2xx_ac97_mic_startup(struct snd_pcm_substream *substream, 127f0fba2adSLiam Girdwood struct snd_soc_dai *cpu_dai) 12875b41027SLiam Girdwood { 12975b41027SLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 13075b41027SLiam Girdwood return -ENODEV; 1315f712b2bSDaniel Mack snd_soc_dai_set_dma_data(cpu_dai, substream, 1325f712b2bSDaniel Mack &pxa2xx_ac97_pcm_mic_mono_in); 13375b41027SLiam Girdwood 13475b41027SLiam Girdwood return 0; 13575b41027SLiam Girdwood } 13675b41027SLiam Girdwood 137596ce32bSLiam Girdwood #define PXA2XX_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ 138596ce32bSLiam Girdwood SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ 139596ce32bSLiam Girdwood SNDRV_PCM_RATE_48000) 140596ce32bSLiam Girdwood 14185e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = { 14258ceb57eSDaniel Mack .startup = pxa2xx_ac97_hifi_startup, 1436335d055SEric Miao }; 1446335d055SEric Miao 14585e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = { 14658ceb57eSDaniel Mack .startup = pxa2xx_ac97_aux_startup, 147852fd9e5SMark Brown }; 148852fd9e5SMark Brown 14985e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = { 15058ceb57eSDaniel Mack .startup = pxa2xx_ac97_mic_startup, 151852fd9e5SMark Brown }; 152852fd9e5SMark Brown 15375b41027SLiam Girdwood /* 15475b41027SLiam Girdwood * There is only 1 physical AC97 interface for pxa2xx, but it 15575b41027SLiam Girdwood * has extra fifo's that can be used for aux DACs and ADCs. 15675b41027SLiam Girdwood */ 157a3874196SAxel Lin static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { 15875b41027SLiam Girdwood { 15975b41027SLiam Girdwood .name = "pxa2xx-ac97", 16075b41027SLiam Girdwood .playback = { 16175b41027SLiam Girdwood .stream_name = "AC97 Playback", 16275b41027SLiam Girdwood .channels_min = 2, 163596ce32bSLiam Girdwood .channels_max = 2, 164596ce32bSLiam Girdwood .rates = PXA2XX_AC97_RATES, 165596ce32bSLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 16675b41027SLiam Girdwood .capture = { 16775b41027SLiam Girdwood .stream_name = "AC97 Capture", 16875b41027SLiam Girdwood .channels_min = 2, 169596ce32bSLiam Girdwood .channels_max = 2, 170596ce32bSLiam Girdwood .rates = PXA2XX_AC97_RATES, 171596ce32bSLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 172852fd9e5SMark Brown .ops = &pxa_ac97_hifi_dai_ops, 17375b41027SLiam Girdwood }, 17475b41027SLiam Girdwood { 17575b41027SLiam Girdwood .name = "pxa2xx-ac97-aux", 17675b41027SLiam Girdwood .playback = { 17775b41027SLiam Girdwood .stream_name = "AC97 Aux Playback", 17875b41027SLiam Girdwood .channels_min = 1, 179596ce32bSLiam Girdwood .channels_max = 1, 180596ce32bSLiam Girdwood .rates = PXA2XX_AC97_RATES, 181596ce32bSLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 18275b41027SLiam Girdwood .capture = { 18375b41027SLiam Girdwood .stream_name = "AC97 Aux Capture", 18475b41027SLiam Girdwood .channels_min = 1, 185596ce32bSLiam Girdwood .channels_max = 1, 186596ce32bSLiam Girdwood .rates = PXA2XX_AC97_RATES, 187596ce32bSLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 188852fd9e5SMark Brown .ops = &pxa_ac97_aux_dai_ops, 18975b41027SLiam Girdwood }, 19075b41027SLiam Girdwood { 19175b41027SLiam Girdwood .name = "pxa2xx-ac97-mic", 19275b41027SLiam Girdwood .capture = { 19375b41027SLiam Girdwood .stream_name = "AC97 Mic Capture", 19475b41027SLiam Girdwood .channels_min = 1, 195596ce32bSLiam Girdwood .channels_max = 1, 196596ce32bSLiam Girdwood .rates = PXA2XX_AC97_RATES, 197596ce32bSLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 198852fd9e5SMark Brown .ops = &pxa_ac97_mic_dai_ops, 199596ce32bSLiam Girdwood }, 20075b41027SLiam Girdwood }; 20175b41027SLiam Girdwood 202ad53232cSKuninori Morimoto static const struct snd_soc_component_driver pxa_ac97_component = { 203ad53232cSKuninori Morimoto .name = "pxa-ac97", 204f8772e17SKuninori Morimoto .pcm_construct = pxa2xx_soc_pcm_new, 205f8772e17SKuninori Morimoto .open = pxa2xx_soc_pcm_open, 206f8772e17SKuninori Morimoto .close = pxa2xx_soc_pcm_close, 207f8772e17SKuninori Morimoto .hw_params = pxa2xx_soc_pcm_hw_params, 208f8772e17SKuninori Morimoto .prepare = pxa2xx_soc_pcm_prepare, 209f8772e17SKuninori Morimoto .trigger = pxa2xx_soc_pcm_trigger, 210f8772e17SKuninori Morimoto .pointer = pxa2xx_soc_pcm_pointer, 211ad53232cSKuninori Morimoto }; 212ad53232cSKuninori Morimoto 213a4519526SRobert Jarzmik #ifdef CONFIG_OF 214a4519526SRobert Jarzmik static const struct of_device_id pxa2xx_ac97_dt_ids[] = { 215a4519526SRobert Jarzmik { .compatible = "marvell,pxa250-ac97", }, 216a4519526SRobert Jarzmik { .compatible = "marvell,pxa270-ac97", }, 217a4519526SRobert Jarzmik { .compatible = "marvell,pxa300-ac97", }, 218a4519526SRobert Jarzmik { } 219a4519526SRobert Jarzmik }; 220a4519526SRobert Jarzmik MODULE_DEVICE_TABLE(of, pxa2xx_ac97_dt_ids); 221a4519526SRobert Jarzmik 222a4519526SRobert Jarzmik #endif 223a4519526SRobert Jarzmik 224570f6fe1SBill Pemberton static int pxa2xx_ac97_dev_probe(struct platform_device *pdev) 2256b849bcfSMark Brown { 2267685e016SKevin Hilman int ret; 2271c8bc7b3SRobert Jarzmik struct ac97_controller *ctrl; 2281c8bc7b3SRobert Jarzmik pxa2xx_audio_ops_t *pdata = pdev->dev.platform_data; 2291c8bc7b3SRobert Jarzmik void **codecs_pdata; 2307685e016SKevin Hilman 231f0fba2adSLiam Girdwood if (pdev->id != -1) { 2324ac0478fSMarek Vasut dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n"); 2334ac0478fSMarek Vasut return -ENXIO; 2344ac0478fSMarek Vasut } 2354ac0478fSMarek Vasut 236f62aa9b6SDmitry Eremin-Solenikov ret = pxa2xx_ac97_hw_probe(pdev); 237f62aa9b6SDmitry Eremin-Solenikov if (ret) { 238f62aa9b6SDmitry Eremin-Solenikov dev_err(&pdev->dev, "PXA2xx AC97 hw probe error (%d)\n", ret); 239f62aa9b6SDmitry Eremin-Solenikov return ret; 240f62aa9b6SDmitry Eremin-Solenikov } 241f62aa9b6SDmitry Eremin-Solenikov 2421c8bc7b3SRobert Jarzmik codecs_pdata = pdata ? pdata->codec_pdata : NULL; 2431c8bc7b3SRobert Jarzmik ctrl = snd_ac97_controller_register(&pxa2xx_ac97_ops, &pdev->dev, 2441c8bc7b3SRobert Jarzmik AC97_SLOTS_AVAILABLE_ALL, 2451c8bc7b3SRobert Jarzmik codecs_pdata); 2461c8bc7b3SRobert Jarzmik if (IS_ERR(ctrl)) 2471c8bc7b3SRobert Jarzmik return PTR_ERR(ctrl); 248b047e1ccSMark Brown 2491c8bc7b3SRobert Jarzmik platform_set_drvdata(pdev, ctrl); 2506b849bcfSMark Brown /* Punt most of the init to the SoC probe; we may need the machine 2516b849bcfSMark Brown * driver to do interesting things with the clocking to get us up 2526b849bcfSMark Brown * and running. 2536b849bcfSMark Brown */ 2544b9b73b8SKuninori Morimoto return devm_snd_soc_register_component(&pdev->dev, &pxa_ac97_component, 255ad53232cSKuninori Morimoto pxa_ac97_dai_driver, ARRAY_SIZE(pxa_ac97_dai_driver)); 2566b849bcfSMark Brown } 2576b849bcfSMark Brown 258570f6fe1SBill Pemberton static int pxa2xx_ac97_dev_remove(struct platform_device *pdev) 2596b849bcfSMark Brown { 2601c8bc7b3SRobert Jarzmik struct ac97_controller *ctrl = platform_get_drvdata(pdev); 2611c8bc7b3SRobert Jarzmik 2621c8bc7b3SRobert Jarzmik snd_ac97_controller_unregister(ctrl); 263f62aa9b6SDmitry Eremin-Solenikov pxa2xx_ac97_hw_remove(pdev); 2646b849bcfSMark Brown return 0; 2656b849bcfSMark Brown } 2666b849bcfSMark Brown 267f62aa9b6SDmitry Eremin-Solenikov #ifdef CONFIG_PM_SLEEP 268f62aa9b6SDmitry Eremin-Solenikov static int pxa2xx_ac97_dev_suspend(struct device *dev) 269f62aa9b6SDmitry Eremin-Solenikov { 270f62aa9b6SDmitry Eremin-Solenikov return pxa2xx_ac97_hw_suspend(); 271f62aa9b6SDmitry Eremin-Solenikov } 272f62aa9b6SDmitry Eremin-Solenikov 273f62aa9b6SDmitry Eremin-Solenikov static int pxa2xx_ac97_dev_resume(struct device *dev) 274f62aa9b6SDmitry Eremin-Solenikov { 275f62aa9b6SDmitry Eremin-Solenikov return pxa2xx_ac97_hw_resume(); 276f62aa9b6SDmitry Eremin-Solenikov } 277f62aa9b6SDmitry Eremin-Solenikov 278f62aa9b6SDmitry Eremin-Solenikov static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, 279f62aa9b6SDmitry Eremin-Solenikov pxa2xx_ac97_dev_suspend, pxa2xx_ac97_dev_resume); 280f62aa9b6SDmitry Eremin-Solenikov #endif 281f62aa9b6SDmitry Eremin-Solenikov 2826b849bcfSMark Brown static struct platform_driver pxa2xx_ac97_driver = { 2836b849bcfSMark Brown .probe = pxa2xx_ac97_dev_probe, 284570f6fe1SBill Pemberton .remove = pxa2xx_ac97_dev_remove, 2856b849bcfSMark Brown .driver = { 2866b849bcfSMark Brown .name = "pxa2xx-ac97", 287f62aa9b6SDmitry Eremin-Solenikov #ifdef CONFIG_PM_SLEEP 288f62aa9b6SDmitry Eremin-Solenikov .pm = &pxa2xx_ac97_pm_ops, 289f62aa9b6SDmitry Eremin-Solenikov #endif 290a4519526SRobert Jarzmik .of_match_table = of_match_ptr(pxa2xx_ac97_dt_ids), 2916b849bcfSMark Brown }, 2926b849bcfSMark Brown }; 2936b849bcfSMark Brown 2942f702a19SAxel Lin module_platform_driver(pxa2xx_ac97_driver); 2953f4b783cSMark Brown 29675b41027SLiam Girdwood MODULE_AUTHOR("Nicolas Pitre"); 29775b41027SLiam Girdwood MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip"); 29875b41027SLiam Girdwood MODULE_LICENSE("GPL"); 299e5b7d71aSAndrea Adami MODULE_ALIAS("platform:pxa2xx-ac97"); 300