175b41027SLiam Girdwood /* 275b41027SLiam Girdwood * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip. 375b41027SLiam Girdwood * 475b41027SLiam Girdwood * Author: Nicolas Pitre 575b41027SLiam Girdwood * Created: Dec 02, 2004 675b41027SLiam Girdwood * Copyright: MontaVista Software Inc. 775b41027SLiam Girdwood * 875b41027SLiam Girdwood * This program is free software; you can redistribute it and/or modify 975b41027SLiam Girdwood * it under the terms of the GNU General Public License version 2 as 1075b41027SLiam Girdwood * published by the Free Software Foundation. 1175b41027SLiam Girdwood */ 1275b41027SLiam Girdwood 1375b41027SLiam Girdwood #include <linux/init.h> 1423019a73SRob Herring #include <linux/io.h> 1575b41027SLiam Girdwood #include <linux/module.h> 1675b41027SLiam Girdwood #include <linux/platform_device.h> 17d65a1458SDaniel Mack #include <linux/dmaengine.h> 1858ceb57eSDaniel Mack #include <linux/dma/pxa-dma.h> 1975b41027SLiam Girdwood 2075b41027SLiam Girdwood #include <sound/core.h> 2175b41027SLiam Girdwood #include <sound/ac97_codec.h> 2275b41027SLiam Girdwood #include <sound/soc.h> 239c636342SDmitry Baryshkov #include <sound/pxa2xx-lib.h> 24d65a1458SDaniel Mack #include <sound/dmaengine_pcm.h> 2575b41027SLiam Girdwood 26a09e64fbSRussell King #include <mach/hardware.h> 271f017a99SEric Miao #include <mach/regs-ac97.h> 284ac0478fSMarek Vasut #include <mach/audio.h> 2975b41027SLiam Girdwood 3075b41027SLiam Girdwood static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) 3175b41027SLiam Girdwood { 326f8acad6SRobert Jarzmik pxa2xx_ac97_try_warm_reset(); 3375b41027SLiam Girdwood 346f8acad6SRobert Jarzmik pxa2xx_ac97_finish_reset(); 3575b41027SLiam Girdwood } 3675b41027SLiam Girdwood 3775b41027SLiam Girdwood static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) 3875b41027SLiam Girdwood { 396f8acad6SRobert Jarzmik pxa2xx_ac97_try_cold_reset(); 407a22323bSMark Brown 416f8acad6SRobert Jarzmik pxa2xx_ac97_finish_reset(); 426f8acad6SRobert Jarzmik } 436f8acad6SRobert Jarzmik 446f8acad6SRobert Jarzmik static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97, 456f8acad6SRobert Jarzmik unsigned short reg) 466f8acad6SRobert Jarzmik { 476f8acad6SRobert Jarzmik int ret; 486f8acad6SRobert Jarzmik 496f8acad6SRobert Jarzmik ret = pxa2xx_ac97_read(ac97->num, reg); 506f8acad6SRobert Jarzmik if (ret < 0) 516f8acad6SRobert Jarzmik return 0; 526f8acad6SRobert Jarzmik else 536f8acad6SRobert Jarzmik return (unsigned short)(ret & 0xffff); 546f8acad6SRobert Jarzmik } 556f8acad6SRobert Jarzmik 566f8acad6SRobert Jarzmik static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97, 576f8acad6SRobert Jarzmik unsigned short reg, unsigned short val) 586f8acad6SRobert Jarzmik { 596f8acad6SRobert Jarzmik int ret; 606f8acad6SRobert Jarzmik 616f8acad6SRobert Jarzmik ret = pxa2xx_ac97_write(ac97->num, reg, val); 6275b41027SLiam Girdwood } 6375b41027SLiam Girdwood 64b047e1ccSMark Brown static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { 656f8acad6SRobert Jarzmik .read = pxa2xx_ac97_legacy_read, 666f8acad6SRobert Jarzmik .write = pxa2xx_ac97_legacy_write, 6775b41027SLiam Girdwood .warm_reset = pxa2xx_ac97_warm_reset, 6875b41027SLiam Girdwood .reset = pxa2xx_ac97_cold_reset, 6975b41027SLiam Girdwood }; 7075b41027SLiam Girdwood 7158ceb57eSDaniel Mack static struct pxad_param pxa2xx_ac97_pcm_stereo_in_req = { 7258ceb57eSDaniel Mack .prio = PXAD_PRIO_LOWEST, 7358ceb57eSDaniel Mack .drcmr = 11, 7458ceb57eSDaniel Mack }; 7558ceb57eSDaniel Mack 76d65a1458SDaniel Mack static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { 77d65a1458SDaniel Mack .addr = __PREG(PCDR), 78d65a1458SDaniel Mack .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 79d65a1458SDaniel Mack .maxburst = 32, 80d65a1458SDaniel Mack .filter_data = &pxa2xx_ac97_pcm_stereo_in_req, 8175b41027SLiam Girdwood }; 8275b41027SLiam Girdwood 8358ceb57eSDaniel Mack static struct pxad_param pxa2xx_ac97_pcm_stereo_out_req = { 8458ceb57eSDaniel Mack .prio = PXAD_PRIO_LOWEST, 8558ceb57eSDaniel Mack .drcmr = 12, 8658ceb57eSDaniel Mack }; 8758ceb57eSDaniel Mack 88d65a1458SDaniel Mack static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { 89d65a1458SDaniel Mack .addr = __PREG(PCDR), 90d65a1458SDaniel Mack .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 91d65a1458SDaniel Mack .maxburst = 32, 92d65a1458SDaniel Mack .filter_data = &pxa2xx_ac97_pcm_stereo_out_req, 9375b41027SLiam Girdwood }; 9475b41027SLiam Girdwood 9558ceb57eSDaniel Mack static struct pxad_param pxa2xx_ac97_pcm_aux_mono_out_req = { 9658ceb57eSDaniel Mack .prio = PXAD_PRIO_LOWEST, 9758ceb57eSDaniel Mack .drcmr = 10, 9858ceb57eSDaniel Mack }; 99d65a1458SDaniel Mack static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { 100d65a1458SDaniel Mack .addr = __PREG(MODR), 101d65a1458SDaniel Mack .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, 102d65a1458SDaniel Mack .maxburst = 16, 103d65a1458SDaniel Mack .filter_data = &pxa2xx_ac97_pcm_aux_mono_out_req, 10475b41027SLiam Girdwood }; 10575b41027SLiam Girdwood 10658ceb57eSDaniel Mack static struct pxad_param pxa2xx_ac97_pcm_aux_mono_in_req = { 10758ceb57eSDaniel Mack .prio = PXAD_PRIO_LOWEST, 10858ceb57eSDaniel Mack .drcmr = 9, 10958ceb57eSDaniel Mack }; 110d65a1458SDaniel Mack static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { 111d65a1458SDaniel Mack .addr = __PREG(MODR), 112d65a1458SDaniel Mack .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, 113d65a1458SDaniel Mack .maxburst = 16, 114d65a1458SDaniel Mack .filter_data = &pxa2xx_ac97_pcm_aux_mono_in_req, 11575b41027SLiam Girdwood }; 11675b41027SLiam Girdwood 11758ceb57eSDaniel Mack static struct pxad_param pxa2xx_ac97_pcm_aux_mic_mono_req = { 11858ceb57eSDaniel Mack .prio = PXAD_PRIO_LOWEST, 11958ceb57eSDaniel Mack .drcmr = 8, 12058ceb57eSDaniel Mack }; 121d65a1458SDaniel Mack static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { 122d65a1458SDaniel Mack .addr = __PREG(MCDR), 123d65a1458SDaniel Mack .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, 124d65a1458SDaniel Mack .maxburst = 16, 125d65a1458SDaniel Mack .filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req, 12675b41027SLiam Girdwood }; 12775b41027SLiam Girdwood 12858ceb57eSDaniel Mack static int pxa2xx_ac97_hifi_startup(struct snd_pcm_substream *substream, 129f0fba2adSLiam Girdwood struct snd_soc_dai *cpu_dai) 13075b41027SLiam Girdwood { 131d65a1458SDaniel Mack struct snd_dmaengine_dai_dma_data *dma_data; 13275b41027SLiam Girdwood 13375b41027SLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1345f712b2bSDaniel Mack dma_data = &pxa2xx_ac97_pcm_stereo_out; 13575b41027SLiam Girdwood else 1365f712b2bSDaniel Mack dma_data = &pxa2xx_ac97_pcm_stereo_in; 1375f712b2bSDaniel Mack 1385f712b2bSDaniel Mack snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); 13975b41027SLiam Girdwood 14075b41027SLiam Girdwood return 0; 14175b41027SLiam Girdwood } 14275b41027SLiam Girdwood 14358ceb57eSDaniel Mack static int pxa2xx_ac97_aux_startup(struct snd_pcm_substream *substream, 144f0fba2adSLiam Girdwood struct snd_soc_dai *cpu_dai) 14575b41027SLiam Girdwood { 146d65a1458SDaniel Mack struct snd_dmaengine_dai_dma_data *dma_data; 14775b41027SLiam Girdwood 14875b41027SLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1495f712b2bSDaniel Mack dma_data = &pxa2xx_ac97_pcm_aux_mono_out; 15075b41027SLiam Girdwood else 1515f712b2bSDaniel Mack dma_data = &pxa2xx_ac97_pcm_aux_mono_in; 1525f712b2bSDaniel Mack 1535f712b2bSDaniel Mack snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); 15475b41027SLiam Girdwood 15575b41027SLiam Girdwood return 0; 15675b41027SLiam Girdwood } 15775b41027SLiam Girdwood 15858ceb57eSDaniel Mack static int pxa2xx_ac97_mic_startup(struct snd_pcm_substream *substream, 159f0fba2adSLiam Girdwood struct snd_soc_dai *cpu_dai) 16075b41027SLiam Girdwood { 16175b41027SLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 16275b41027SLiam Girdwood return -ENODEV; 1635f712b2bSDaniel Mack snd_soc_dai_set_dma_data(cpu_dai, substream, 1645f712b2bSDaniel Mack &pxa2xx_ac97_pcm_mic_mono_in); 16575b41027SLiam Girdwood 16675b41027SLiam Girdwood return 0; 16775b41027SLiam Girdwood } 16875b41027SLiam Girdwood 169596ce32bSLiam Girdwood #define PXA2XX_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ 170596ce32bSLiam Girdwood SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ 171596ce32bSLiam Girdwood SNDRV_PCM_RATE_48000) 172596ce32bSLiam Girdwood 17385e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = { 17458ceb57eSDaniel Mack .startup = pxa2xx_ac97_hifi_startup, 1756335d055SEric Miao }; 1766335d055SEric Miao 17785e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = { 17858ceb57eSDaniel Mack .startup = pxa2xx_ac97_aux_startup, 179852fd9e5SMark Brown }; 180852fd9e5SMark Brown 18185e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = { 18258ceb57eSDaniel Mack .startup = pxa2xx_ac97_mic_startup, 183852fd9e5SMark Brown }; 184852fd9e5SMark Brown 18575b41027SLiam Girdwood /* 18675b41027SLiam Girdwood * There is only 1 physical AC97 interface for pxa2xx, but it 18775b41027SLiam Girdwood * has extra fifo's that can be used for aux DACs and ADCs. 18875b41027SLiam Girdwood */ 189a3874196SAxel Lin static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { 19075b41027SLiam Girdwood { 19175b41027SLiam Girdwood .name = "pxa2xx-ac97", 192bc263214SLars-Peter Clausen .bus_control = true, 19375b41027SLiam Girdwood .playback = { 19475b41027SLiam Girdwood .stream_name = "AC97 Playback", 19575b41027SLiam Girdwood .channels_min = 2, 196596ce32bSLiam Girdwood .channels_max = 2, 197596ce32bSLiam Girdwood .rates = PXA2XX_AC97_RATES, 198596ce32bSLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 19975b41027SLiam Girdwood .capture = { 20075b41027SLiam Girdwood .stream_name = "AC97 Capture", 20175b41027SLiam Girdwood .channels_min = 2, 202596ce32bSLiam Girdwood .channels_max = 2, 203596ce32bSLiam Girdwood .rates = PXA2XX_AC97_RATES, 204596ce32bSLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 205852fd9e5SMark Brown .ops = &pxa_ac97_hifi_dai_ops, 20675b41027SLiam Girdwood }, 20775b41027SLiam Girdwood { 20875b41027SLiam Girdwood .name = "pxa2xx-ac97-aux", 209bc263214SLars-Peter Clausen .bus_control = true, 21075b41027SLiam Girdwood .playback = { 21175b41027SLiam Girdwood .stream_name = "AC97 Aux Playback", 21275b41027SLiam Girdwood .channels_min = 1, 213596ce32bSLiam Girdwood .channels_max = 1, 214596ce32bSLiam Girdwood .rates = PXA2XX_AC97_RATES, 215596ce32bSLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 21675b41027SLiam Girdwood .capture = { 21775b41027SLiam Girdwood .stream_name = "AC97 Aux Capture", 21875b41027SLiam Girdwood .channels_min = 1, 219596ce32bSLiam Girdwood .channels_max = 1, 220596ce32bSLiam Girdwood .rates = PXA2XX_AC97_RATES, 221596ce32bSLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 222852fd9e5SMark Brown .ops = &pxa_ac97_aux_dai_ops, 22375b41027SLiam Girdwood }, 22475b41027SLiam Girdwood { 22575b41027SLiam Girdwood .name = "pxa2xx-ac97-mic", 226bc263214SLars-Peter Clausen .bus_control = true, 22775b41027SLiam Girdwood .capture = { 22875b41027SLiam Girdwood .stream_name = "AC97 Mic Capture", 22975b41027SLiam Girdwood .channels_min = 1, 230596ce32bSLiam Girdwood .channels_max = 1, 231596ce32bSLiam Girdwood .rates = PXA2XX_AC97_RATES, 232596ce32bSLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 233852fd9e5SMark Brown .ops = &pxa_ac97_mic_dai_ops, 234596ce32bSLiam Girdwood }, 23575b41027SLiam Girdwood }; 23675b41027SLiam Girdwood 237ad53232cSKuninori Morimoto static const struct snd_soc_component_driver pxa_ac97_component = { 238ad53232cSKuninori Morimoto .name = "pxa-ac97", 239ad53232cSKuninori Morimoto }; 240ad53232cSKuninori Morimoto 241a4519526SRobert Jarzmik #ifdef CONFIG_OF 242a4519526SRobert Jarzmik static const struct of_device_id pxa2xx_ac97_dt_ids[] = { 243a4519526SRobert Jarzmik { .compatible = "marvell,pxa250-ac97", }, 244a4519526SRobert Jarzmik { .compatible = "marvell,pxa270-ac97", }, 245a4519526SRobert Jarzmik { .compatible = "marvell,pxa300-ac97", }, 246a4519526SRobert Jarzmik { } 247a4519526SRobert Jarzmik }; 248a4519526SRobert Jarzmik MODULE_DEVICE_TABLE(of, pxa2xx_ac97_dt_ids); 249a4519526SRobert Jarzmik 250a4519526SRobert Jarzmik #endif 251a4519526SRobert Jarzmik 252570f6fe1SBill Pemberton static int pxa2xx_ac97_dev_probe(struct platform_device *pdev) 2536b849bcfSMark Brown { 2547685e016SKevin Hilman int ret; 2557685e016SKevin Hilman 256f0fba2adSLiam Girdwood if (pdev->id != -1) { 2574ac0478fSMarek Vasut dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n"); 2584ac0478fSMarek Vasut return -ENXIO; 2594ac0478fSMarek Vasut } 2604ac0478fSMarek Vasut 261f62aa9b6SDmitry Eremin-Solenikov ret = pxa2xx_ac97_hw_probe(pdev); 262f62aa9b6SDmitry Eremin-Solenikov if (ret) { 263f62aa9b6SDmitry Eremin-Solenikov dev_err(&pdev->dev, "PXA2xx AC97 hw probe error (%d)\n", ret); 264f62aa9b6SDmitry Eremin-Solenikov return ret; 265f62aa9b6SDmitry Eremin-Solenikov } 266f62aa9b6SDmitry Eremin-Solenikov 267b047e1ccSMark Brown ret = snd_soc_set_ac97_ops(&pxa2xx_ac97_ops); 268b047e1ccSMark Brown if (ret != 0) 269b047e1ccSMark Brown return ret; 270b047e1ccSMark Brown 2716b849bcfSMark Brown /* Punt most of the init to the SoC probe; we may need the machine 2726b849bcfSMark Brown * driver to do interesting things with the clocking to get us up 2736b849bcfSMark Brown * and running. 2746b849bcfSMark Brown */ 275ad53232cSKuninori Morimoto return snd_soc_register_component(&pdev->dev, &pxa_ac97_component, 276ad53232cSKuninori Morimoto pxa_ac97_dai_driver, ARRAY_SIZE(pxa_ac97_dai_driver)); 2776b849bcfSMark Brown } 2786b849bcfSMark Brown 279570f6fe1SBill Pemberton static int pxa2xx_ac97_dev_remove(struct platform_device *pdev) 2806b849bcfSMark Brown { 281ad53232cSKuninori Morimoto snd_soc_unregister_component(&pdev->dev); 282b047e1ccSMark Brown snd_soc_set_ac97_ops(NULL); 283f62aa9b6SDmitry Eremin-Solenikov pxa2xx_ac97_hw_remove(pdev); 2846b849bcfSMark Brown return 0; 2856b849bcfSMark Brown } 2866b849bcfSMark Brown 287f62aa9b6SDmitry Eremin-Solenikov #ifdef CONFIG_PM_SLEEP 288f62aa9b6SDmitry Eremin-Solenikov static int pxa2xx_ac97_dev_suspend(struct device *dev) 289f62aa9b6SDmitry Eremin-Solenikov { 290f62aa9b6SDmitry Eremin-Solenikov return pxa2xx_ac97_hw_suspend(); 291f62aa9b6SDmitry Eremin-Solenikov } 292f62aa9b6SDmitry Eremin-Solenikov 293f62aa9b6SDmitry Eremin-Solenikov static int pxa2xx_ac97_dev_resume(struct device *dev) 294f62aa9b6SDmitry Eremin-Solenikov { 295f62aa9b6SDmitry Eremin-Solenikov return pxa2xx_ac97_hw_resume(); 296f62aa9b6SDmitry Eremin-Solenikov } 297f62aa9b6SDmitry Eremin-Solenikov 298f62aa9b6SDmitry Eremin-Solenikov static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, 299f62aa9b6SDmitry Eremin-Solenikov pxa2xx_ac97_dev_suspend, pxa2xx_ac97_dev_resume); 300f62aa9b6SDmitry Eremin-Solenikov #endif 301f62aa9b6SDmitry Eremin-Solenikov 3026b849bcfSMark Brown static struct platform_driver pxa2xx_ac97_driver = { 3036b849bcfSMark Brown .probe = pxa2xx_ac97_dev_probe, 304570f6fe1SBill Pemberton .remove = pxa2xx_ac97_dev_remove, 3056b849bcfSMark Brown .driver = { 3066b849bcfSMark Brown .name = "pxa2xx-ac97", 307f62aa9b6SDmitry Eremin-Solenikov #ifdef CONFIG_PM_SLEEP 308f62aa9b6SDmitry Eremin-Solenikov .pm = &pxa2xx_ac97_pm_ops, 309f62aa9b6SDmitry Eremin-Solenikov #endif 310a4519526SRobert Jarzmik .of_match_table = of_match_ptr(pxa2xx_ac97_dt_ids), 3116b849bcfSMark Brown }, 3126b849bcfSMark Brown }; 3136b849bcfSMark Brown 3142f702a19SAxel Lin module_platform_driver(pxa2xx_ac97_driver); 3153f4b783cSMark Brown 31675b41027SLiam Girdwood MODULE_AUTHOR("Nicolas Pitre"); 31775b41027SLiam Girdwood MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip"); 31875b41027SLiam Girdwood MODULE_LICENSE("GPL"); 319e5b7d71aSAndrea Adami MODULE_ALIAS("platform:pxa2xx-ac97"); 320