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 71d65a1458SDaniel Mack static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { 72d65a1458SDaniel Mack .addr = __PREG(PCDR), 73d65a1458SDaniel Mack .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 748f54061dSRobert Jarzmik .chan_name = "pcm_pcm_stereo_in", 75d65a1458SDaniel Mack .maxburst = 32, 7658ceb57eSDaniel Mack }; 7758ceb57eSDaniel Mack 78d65a1458SDaniel Mack static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { 79d65a1458SDaniel Mack .addr = __PREG(PCDR), 80d65a1458SDaniel Mack .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 818f54061dSRobert Jarzmik .chan_name = "pcm_pcm_stereo_out", 82d65a1458SDaniel Mack .maxburst = 32, 8375b41027SLiam Girdwood }; 8475b41027SLiam Girdwood 85d65a1458SDaniel Mack static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { 86d65a1458SDaniel Mack .addr = __PREG(MODR), 87d65a1458SDaniel Mack .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, 888f54061dSRobert Jarzmik .chan_name = "pcm_aux_mono_out", 89d65a1458SDaniel Mack .maxburst = 16, 9075b41027SLiam Girdwood }; 9175b41027SLiam Girdwood 92d65a1458SDaniel Mack static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { 93d65a1458SDaniel Mack .addr = __PREG(MODR), 94d65a1458SDaniel Mack .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, 958f54061dSRobert Jarzmik .chan_name = "pcm_aux_mono_in", 96d65a1458SDaniel Mack .maxburst = 16, 9775b41027SLiam Girdwood }; 9875b41027SLiam Girdwood 99d65a1458SDaniel Mack static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { 100d65a1458SDaniel Mack .addr = __PREG(MCDR), 101d65a1458SDaniel Mack .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, 1028f54061dSRobert Jarzmik .chan_name = "pcm_aux_mic_mono", 103d65a1458SDaniel Mack .maxburst = 16, 10475b41027SLiam Girdwood }; 10575b41027SLiam Girdwood 10658ceb57eSDaniel Mack static int pxa2xx_ac97_hifi_startup(struct snd_pcm_substream *substream, 107f0fba2adSLiam Girdwood struct snd_soc_dai *cpu_dai) 10875b41027SLiam Girdwood { 109d65a1458SDaniel Mack struct snd_dmaengine_dai_dma_data *dma_data; 11075b41027SLiam Girdwood 11175b41027SLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1125f712b2bSDaniel Mack dma_data = &pxa2xx_ac97_pcm_stereo_out; 11375b41027SLiam Girdwood else 1145f712b2bSDaniel Mack dma_data = &pxa2xx_ac97_pcm_stereo_in; 1155f712b2bSDaniel Mack 1165f712b2bSDaniel Mack snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); 11775b41027SLiam Girdwood 11875b41027SLiam Girdwood return 0; 11975b41027SLiam Girdwood } 12075b41027SLiam Girdwood 12158ceb57eSDaniel Mack static int pxa2xx_ac97_aux_startup(struct snd_pcm_substream *substream, 122f0fba2adSLiam Girdwood struct snd_soc_dai *cpu_dai) 12375b41027SLiam Girdwood { 124d65a1458SDaniel Mack struct snd_dmaengine_dai_dma_data *dma_data; 12575b41027SLiam Girdwood 12675b41027SLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1275f712b2bSDaniel Mack dma_data = &pxa2xx_ac97_pcm_aux_mono_out; 12875b41027SLiam Girdwood else 1295f712b2bSDaniel Mack dma_data = &pxa2xx_ac97_pcm_aux_mono_in; 1305f712b2bSDaniel Mack 1315f712b2bSDaniel Mack snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); 13275b41027SLiam Girdwood 13375b41027SLiam Girdwood return 0; 13475b41027SLiam Girdwood } 13575b41027SLiam Girdwood 13658ceb57eSDaniel Mack static int pxa2xx_ac97_mic_startup(struct snd_pcm_substream *substream, 137f0fba2adSLiam Girdwood struct snd_soc_dai *cpu_dai) 13875b41027SLiam Girdwood { 13975b41027SLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 14075b41027SLiam Girdwood return -ENODEV; 1415f712b2bSDaniel Mack snd_soc_dai_set_dma_data(cpu_dai, substream, 1425f712b2bSDaniel Mack &pxa2xx_ac97_pcm_mic_mono_in); 14375b41027SLiam Girdwood 14475b41027SLiam Girdwood return 0; 14575b41027SLiam Girdwood } 14675b41027SLiam Girdwood 147596ce32bSLiam Girdwood #define PXA2XX_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ 148596ce32bSLiam Girdwood SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ 149596ce32bSLiam Girdwood SNDRV_PCM_RATE_48000) 150596ce32bSLiam Girdwood 15185e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = { 15258ceb57eSDaniel Mack .startup = pxa2xx_ac97_hifi_startup, 1536335d055SEric Miao }; 1546335d055SEric Miao 15585e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = { 15658ceb57eSDaniel Mack .startup = pxa2xx_ac97_aux_startup, 157852fd9e5SMark Brown }; 158852fd9e5SMark Brown 15985e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = { 16058ceb57eSDaniel Mack .startup = pxa2xx_ac97_mic_startup, 161852fd9e5SMark Brown }; 162852fd9e5SMark Brown 16375b41027SLiam Girdwood /* 16475b41027SLiam Girdwood * There is only 1 physical AC97 interface for pxa2xx, but it 16575b41027SLiam Girdwood * has extra fifo's that can be used for aux DACs and ADCs. 16675b41027SLiam Girdwood */ 167a3874196SAxel Lin static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { 16875b41027SLiam Girdwood { 16975b41027SLiam Girdwood .name = "pxa2xx-ac97", 170bc263214SLars-Peter Clausen .bus_control = true, 17175b41027SLiam Girdwood .playback = { 17275b41027SLiam Girdwood .stream_name = "AC97 Playback", 17375b41027SLiam Girdwood .channels_min = 2, 174596ce32bSLiam Girdwood .channels_max = 2, 175596ce32bSLiam Girdwood .rates = PXA2XX_AC97_RATES, 176596ce32bSLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 17775b41027SLiam Girdwood .capture = { 17875b41027SLiam Girdwood .stream_name = "AC97 Capture", 17975b41027SLiam Girdwood .channels_min = 2, 180596ce32bSLiam Girdwood .channels_max = 2, 181596ce32bSLiam Girdwood .rates = PXA2XX_AC97_RATES, 182596ce32bSLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 183852fd9e5SMark Brown .ops = &pxa_ac97_hifi_dai_ops, 18475b41027SLiam Girdwood }, 18575b41027SLiam Girdwood { 18675b41027SLiam Girdwood .name = "pxa2xx-ac97-aux", 187bc263214SLars-Peter Clausen .bus_control = true, 18875b41027SLiam Girdwood .playback = { 18975b41027SLiam Girdwood .stream_name = "AC97 Aux Playback", 19075b41027SLiam Girdwood .channels_min = 1, 191596ce32bSLiam Girdwood .channels_max = 1, 192596ce32bSLiam Girdwood .rates = PXA2XX_AC97_RATES, 193596ce32bSLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 19475b41027SLiam Girdwood .capture = { 19575b41027SLiam Girdwood .stream_name = "AC97 Aux Capture", 19675b41027SLiam Girdwood .channels_min = 1, 197596ce32bSLiam Girdwood .channels_max = 1, 198596ce32bSLiam Girdwood .rates = PXA2XX_AC97_RATES, 199596ce32bSLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 200852fd9e5SMark Brown .ops = &pxa_ac97_aux_dai_ops, 20175b41027SLiam Girdwood }, 20275b41027SLiam Girdwood { 20375b41027SLiam Girdwood .name = "pxa2xx-ac97-mic", 204bc263214SLars-Peter Clausen .bus_control = true, 20575b41027SLiam Girdwood .capture = { 20675b41027SLiam Girdwood .stream_name = "AC97 Mic Capture", 20775b41027SLiam Girdwood .channels_min = 1, 208596ce32bSLiam Girdwood .channels_max = 1, 209596ce32bSLiam Girdwood .rates = PXA2XX_AC97_RATES, 210596ce32bSLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 211852fd9e5SMark Brown .ops = &pxa_ac97_mic_dai_ops, 212596ce32bSLiam Girdwood }, 21375b41027SLiam Girdwood }; 21475b41027SLiam Girdwood 215ad53232cSKuninori Morimoto static const struct snd_soc_component_driver pxa_ac97_component = { 216ad53232cSKuninori Morimoto .name = "pxa-ac97", 217ad53232cSKuninori Morimoto }; 218ad53232cSKuninori Morimoto 219a4519526SRobert Jarzmik #ifdef CONFIG_OF 220a4519526SRobert Jarzmik static const struct of_device_id pxa2xx_ac97_dt_ids[] = { 221a4519526SRobert Jarzmik { .compatible = "marvell,pxa250-ac97", }, 222a4519526SRobert Jarzmik { .compatible = "marvell,pxa270-ac97", }, 223a4519526SRobert Jarzmik { .compatible = "marvell,pxa300-ac97", }, 224a4519526SRobert Jarzmik { } 225a4519526SRobert Jarzmik }; 226a4519526SRobert Jarzmik MODULE_DEVICE_TABLE(of, pxa2xx_ac97_dt_ids); 227a4519526SRobert Jarzmik 228a4519526SRobert Jarzmik #endif 229a4519526SRobert Jarzmik 230570f6fe1SBill Pemberton static int pxa2xx_ac97_dev_probe(struct platform_device *pdev) 2316b849bcfSMark Brown { 2327685e016SKevin Hilman int ret; 2337685e016SKevin Hilman 234f0fba2adSLiam Girdwood if (pdev->id != -1) { 2354ac0478fSMarek Vasut dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n"); 2364ac0478fSMarek Vasut return -ENXIO; 2374ac0478fSMarek Vasut } 2384ac0478fSMarek Vasut 239f62aa9b6SDmitry Eremin-Solenikov ret = pxa2xx_ac97_hw_probe(pdev); 240f62aa9b6SDmitry Eremin-Solenikov if (ret) { 241f62aa9b6SDmitry Eremin-Solenikov dev_err(&pdev->dev, "PXA2xx AC97 hw probe error (%d)\n", ret); 242f62aa9b6SDmitry Eremin-Solenikov return ret; 243f62aa9b6SDmitry Eremin-Solenikov } 244f62aa9b6SDmitry Eremin-Solenikov 245b047e1ccSMark Brown ret = snd_soc_set_ac97_ops(&pxa2xx_ac97_ops); 246b047e1ccSMark Brown if (ret != 0) 247b047e1ccSMark Brown return ret; 248b047e1ccSMark Brown 2496b849bcfSMark Brown /* Punt most of the init to the SoC probe; we may need the machine 2506b849bcfSMark Brown * driver to do interesting things with the clocking to get us up 2516b849bcfSMark Brown * and running. 2526b849bcfSMark Brown */ 253ad53232cSKuninori Morimoto return snd_soc_register_component(&pdev->dev, &pxa_ac97_component, 254ad53232cSKuninori Morimoto pxa_ac97_dai_driver, ARRAY_SIZE(pxa_ac97_dai_driver)); 2556b849bcfSMark Brown } 2566b849bcfSMark Brown 257570f6fe1SBill Pemberton static int pxa2xx_ac97_dev_remove(struct platform_device *pdev) 2586b849bcfSMark Brown { 259ad53232cSKuninori Morimoto snd_soc_unregister_component(&pdev->dev); 260b047e1ccSMark Brown snd_soc_set_ac97_ops(NULL); 261f62aa9b6SDmitry Eremin-Solenikov pxa2xx_ac97_hw_remove(pdev); 2626b849bcfSMark Brown return 0; 2636b849bcfSMark Brown } 2646b849bcfSMark Brown 265f62aa9b6SDmitry Eremin-Solenikov #ifdef CONFIG_PM_SLEEP 266f62aa9b6SDmitry Eremin-Solenikov static int pxa2xx_ac97_dev_suspend(struct device *dev) 267f62aa9b6SDmitry Eremin-Solenikov { 268f62aa9b6SDmitry Eremin-Solenikov return pxa2xx_ac97_hw_suspend(); 269f62aa9b6SDmitry Eremin-Solenikov } 270f62aa9b6SDmitry Eremin-Solenikov 271f62aa9b6SDmitry Eremin-Solenikov static int pxa2xx_ac97_dev_resume(struct device *dev) 272f62aa9b6SDmitry Eremin-Solenikov { 273f62aa9b6SDmitry Eremin-Solenikov return pxa2xx_ac97_hw_resume(); 274f62aa9b6SDmitry Eremin-Solenikov } 275f62aa9b6SDmitry Eremin-Solenikov 276f62aa9b6SDmitry Eremin-Solenikov static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, 277f62aa9b6SDmitry Eremin-Solenikov pxa2xx_ac97_dev_suspend, pxa2xx_ac97_dev_resume); 278f62aa9b6SDmitry Eremin-Solenikov #endif 279f62aa9b6SDmitry Eremin-Solenikov 2806b849bcfSMark Brown static struct platform_driver pxa2xx_ac97_driver = { 2816b849bcfSMark Brown .probe = pxa2xx_ac97_dev_probe, 282570f6fe1SBill Pemberton .remove = pxa2xx_ac97_dev_remove, 2836b849bcfSMark Brown .driver = { 2846b849bcfSMark Brown .name = "pxa2xx-ac97", 285f62aa9b6SDmitry Eremin-Solenikov #ifdef CONFIG_PM_SLEEP 286f62aa9b6SDmitry Eremin-Solenikov .pm = &pxa2xx_ac97_pm_ops, 287f62aa9b6SDmitry Eremin-Solenikov #endif 288a4519526SRobert Jarzmik .of_match_table = of_match_ptr(pxa2xx_ac97_dt_ids), 2896b849bcfSMark Brown }, 2906b849bcfSMark Brown }; 2916b849bcfSMark Brown 2922f702a19SAxel Lin module_platform_driver(pxa2xx_ac97_driver); 2933f4b783cSMark Brown 29475b41027SLiam Girdwood MODULE_AUTHOR("Nicolas Pitre"); 29575b41027SLiam Girdwood MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip"); 29675b41027SLiam Girdwood MODULE_LICENSE("GPL"); 297e5b7d71aSAndrea Adami MODULE_ALIAS("platform:pxa2xx-ac97"); 298