162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Nicolas Pitre 662306a36Sopenharmony_ci * Created: Dec 02, 2004 762306a36Sopenharmony_ci * Copyright: MontaVista Software Inc. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/dmaengine.h> 1562306a36Sopenharmony_ci#include <linux/dma/pxa-dma.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <sound/ac97/controller.h> 1862306a36Sopenharmony_ci#include <sound/core.h> 1962306a36Sopenharmony_ci#include <sound/ac97_codec.h> 2062306a36Sopenharmony_ci#include <sound/soc.h> 2162306a36Sopenharmony_ci#include <sound/pxa2xx-lib.h> 2262306a36Sopenharmony_ci#include <sound/dmaengine_pcm.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/platform_data/asoc-pxa.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define PCDR 0x0040 /* PCM FIFO Data Register */ 2762306a36Sopenharmony_ci#define MODR 0x0140 /* Modem FIFO Data Register */ 2862306a36Sopenharmony_ci#define MCDR 0x0060 /* Mic-in FIFO Data Register */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic void pxa2xx_ac97_warm_reset(struct ac97_controller *adrv) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci pxa2xx_ac97_try_warm_reset(); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci pxa2xx_ac97_finish_reset(); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void pxa2xx_ac97_cold_reset(struct ac97_controller *adrv) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci pxa2xx_ac97_try_cold_reset(); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci pxa2xx_ac97_finish_reset(); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic int pxa2xx_ac97_read_actrl(struct ac97_controller *adrv, int slot, 4562306a36Sopenharmony_ci unsigned short reg) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci return pxa2xx_ac97_read(slot, reg); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int pxa2xx_ac97_write_actrl(struct ac97_controller *adrv, int slot, 5162306a36Sopenharmony_ci unsigned short reg, unsigned short val) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci return pxa2xx_ac97_write(slot, reg, val); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic struct ac97_controller_ops pxa2xx_ac97_ops = { 5762306a36Sopenharmony_ci .read = pxa2xx_ac97_read_actrl, 5862306a36Sopenharmony_ci .write = pxa2xx_ac97_write_actrl, 5962306a36Sopenharmony_ci .warm_reset = pxa2xx_ac97_warm_reset, 6062306a36Sopenharmony_ci .reset = pxa2xx_ac97_cold_reset, 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { 6462306a36Sopenharmony_ci .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 6562306a36Sopenharmony_ci .chan_name = "pcm_pcm_stereo_in", 6662306a36Sopenharmony_ci .maxburst = 32, 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { 7062306a36Sopenharmony_ci .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 7162306a36Sopenharmony_ci .chan_name = "pcm_pcm_stereo_out", 7262306a36Sopenharmony_ci .maxburst = 32, 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { 7662306a36Sopenharmony_ci .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, 7762306a36Sopenharmony_ci .chan_name = "pcm_aux_mono_out", 7862306a36Sopenharmony_ci .maxburst = 16, 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { 8262306a36Sopenharmony_ci .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, 8362306a36Sopenharmony_ci .chan_name = "pcm_aux_mono_in", 8462306a36Sopenharmony_ci .maxburst = 16, 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { 8862306a36Sopenharmony_ci .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, 8962306a36Sopenharmony_ci .chan_name = "pcm_aux_mic_mono", 9062306a36Sopenharmony_ci .maxburst = 16, 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic int pxa2xx_ac97_hifi_startup(struct snd_pcm_substream *substream, 9462306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_data; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 9962306a36Sopenharmony_ci dma_data = &pxa2xx_ac97_pcm_stereo_out; 10062306a36Sopenharmony_ci else 10162306a36Sopenharmony_ci dma_data = &pxa2xx_ac97_pcm_stereo_in; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int pxa2xx_ac97_aux_startup(struct snd_pcm_substream *substream, 10962306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_data; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 11462306a36Sopenharmony_ci dma_data = &pxa2xx_ac97_pcm_aux_mono_out; 11562306a36Sopenharmony_ci else 11662306a36Sopenharmony_ci dma_data = &pxa2xx_ac97_pcm_aux_mono_in; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int pxa2xx_ac97_mic_startup(struct snd_pcm_substream *substream, 12462306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 12762306a36Sopenharmony_ci return -ENODEV; 12862306a36Sopenharmony_ci snd_soc_dai_set_dma_data(cpu_dai, substream, 12962306a36Sopenharmony_ci &pxa2xx_ac97_pcm_mic_mono_in); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci#define PXA2XX_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ 13562306a36Sopenharmony_ci SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ 13662306a36Sopenharmony_ci SNDRV_PCM_RATE_48000) 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic const struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = { 13962306a36Sopenharmony_ci .startup = pxa2xx_ac97_hifi_startup, 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic const struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = { 14362306a36Sopenharmony_ci .startup = pxa2xx_ac97_aux_startup, 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = { 14762306a36Sopenharmony_ci .startup = pxa2xx_ac97_mic_startup, 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* 15162306a36Sopenharmony_ci * There is only 1 physical AC97 interface for pxa2xx, but it 15262306a36Sopenharmony_ci * has extra fifo's that can be used for aux DACs and ADCs. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_cistatic struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci .name = "pxa2xx-ac97", 15762306a36Sopenharmony_ci .playback = { 15862306a36Sopenharmony_ci .stream_name = "AC97 Playback", 15962306a36Sopenharmony_ci .channels_min = 2, 16062306a36Sopenharmony_ci .channels_max = 2, 16162306a36Sopenharmony_ci .rates = PXA2XX_AC97_RATES, 16262306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 16362306a36Sopenharmony_ci .capture = { 16462306a36Sopenharmony_ci .stream_name = "AC97 Capture", 16562306a36Sopenharmony_ci .channels_min = 2, 16662306a36Sopenharmony_ci .channels_max = 2, 16762306a36Sopenharmony_ci .rates = PXA2XX_AC97_RATES, 16862306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 16962306a36Sopenharmony_ci .ops = &pxa_ac97_hifi_dai_ops, 17062306a36Sopenharmony_ci}, 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci .name = "pxa2xx-ac97-aux", 17362306a36Sopenharmony_ci .playback = { 17462306a36Sopenharmony_ci .stream_name = "AC97 Aux Playback", 17562306a36Sopenharmony_ci .channels_min = 1, 17662306a36Sopenharmony_ci .channels_max = 1, 17762306a36Sopenharmony_ci .rates = PXA2XX_AC97_RATES, 17862306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 17962306a36Sopenharmony_ci .capture = { 18062306a36Sopenharmony_ci .stream_name = "AC97 Aux Capture", 18162306a36Sopenharmony_ci .channels_min = 1, 18262306a36Sopenharmony_ci .channels_max = 1, 18362306a36Sopenharmony_ci .rates = PXA2XX_AC97_RATES, 18462306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 18562306a36Sopenharmony_ci .ops = &pxa_ac97_aux_dai_ops, 18662306a36Sopenharmony_ci}, 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci .name = "pxa2xx-ac97-mic", 18962306a36Sopenharmony_ci .capture = { 19062306a36Sopenharmony_ci .stream_name = "AC97 Mic Capture", 19162306a36Sopenharmony_ci .channels_min = 1, 19262306a36Sopenharmony_ci .channels_max = 1, 19362306a36Sopenharmony_ci .rates = PXA2XX_AC97_RATES, 19462306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 19562306a36Sopenharmony_ci .ops = &pxa_ac97_mic_dai_ops, 19662306a36Sopenharmony_ci}, 19762306a36Sopenharmony_ci}; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic const struct snd_soc_component_driver pxa_ac97_component = { 20062306a36Sopenharmony_ci .name = "pxa-ac97", 20162306a36Sopenharmony_ci .pcm_construct = pxa2xx_soc_pcm_new, 20262306a36Sopenharmony_ci .open = pxa2xx_soc_pcm_open, 20362306a36Sopenharmony_ci .close = pxa2xx_soc_pcm_close, 20462306a36Sopenharmony_ci .hw_params = pxa2xx_soc_pcm_hw_params, 20562306a36Sopenharmony_ci .prepare = pxa2xx_soc_pcm_prepare, 20662306a36Sopenharmony_ci .trigger = pxa2xx_soc_pcm_trigger, 20762306a36Sopenharmony_ci .pointer = pxa2xx_soc_pcm_pointer, 20862306a36Sopenharmony_ci}; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci#ifdef CONFIG_OF 21162306a36Sopenharmony_cistatic const struct of_device_id pxa2xx_ac97_dt_ids[] = { 21262306a36Sopenharmony_ci { .compatible = "marvell,pxa250-ac97", }, 21362306a36Sopenharmony_ci { .compatible = "marvell,pxa270-ac97", }, 21462306a36Sopenharmony_ci { .compatible = "marvell,pxa300-ac97", }, 21562306a36Sopenharmony_ci { } 21662306a36Sopenharmony_ci}; 21762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pxa2xx_ac97_dt_ids); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci#endif 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic int pxa2xx_ac97_dev_probe(struct platform_device *pdev) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci int ret; 22462306a36Sopenharmony_ci struct ac97_controller *ctrl; 22562306a36Sopenharmony_ci pxa2xx_audio_ops_t *pdata = pdev->dev.platform_data; 22662306a36Sopenharmony_ci struct resource *regs; 22762306a36Sopenharmony_ci void **codecs_pdata; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (pdev->id != -1) { 23062306a36Sopenharmony_ci dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n"); 23162306a36Sopenharmony_ci return -ENXIO; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 23562306a36Sopenharmony_ci if (!regs) 23662306a36Sopenharmony_ci return -ENXIO; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci pxa2xx_ac97_pcm_stereo_in.addr = regs->start + PCDR; 23962306a36Sopenharmony_ci pxa2xx_ac97_pcm_stereo_out.addr = regs->start + PCDR; 24062306a36Sopenharmony_ci pxa2xx_ac97_pcm_aux_mono_out.addr = regs->start + MODR; 24162306a36Sopenharmony_ci pxa2xx_ac97_pcm_aux_mono_in.addr = regs->start + MODR; 24262306a36Sopenharmony_ci pxa2xx_ac97_pcm_mic_mono_in.addr = regs->start + MCDR; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci ret = pxa2xx_ac97_hw_probe(pdev); 24562306a36Sopenharmony_ci if (ret) { 24662306a36Sopenharmony_ci dev_err(&pdev->dev, "PXA2xx AC97 hw probe error (%d)\n", ret); 24762306a36Sopenharmony_ci return ret; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci codecs_pdata = pdata ? pdata->codec_pdata : NULL; 25162306a36Sopenharmony_ci ctrl = snd_ac97_controller_register(&pxa2xx_ac97_ops, &pdev->dev, 25262306a36Sopenharmony_ci AC97_SLOTS_AVAILABLE_ALL, 25362306a36Sopenharmony_ci codecs_pdata); 25462306a36Sopenharmony_ci if (IS_ERR(ctrl)) 25562306a36Sopenharmony_ci return PTR_ERR(ctrl); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci platform_set_drvdata(pdev, ctrl); 25862306a36Sopenharmony_ci /* Punt most of the init to the SoC probe; we may need the machine 25962306a36Sopenharmony_ci * driver to do interesting things with the clocking to get us up 26062306a36Sopenharmony_ci * and running. 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_ci return devm_snd_soc_register_component(&pdev->dev, &pxa_ac97_component, 26362306a36Sopenharmony_ci pxa_ac97_dai_driver, ARRAY_SIZE(pxa_ac97_dai_driver)); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic void pxa2xx_ac97_dev_remove(struct platform_device *pdev) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct ac97_controller *ctrl = platform_get_drvdata(pdev); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci snd_ac97_controller_unregister(ctrl); 27162306a36Sopenharmony_ci pxa2xx_ac97_hw_remove(pdev); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 27562306a36Sopenharmony_cistatic int pxa2xx_ac97_dev_suspend(struct device *dev) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci return pxa2xx_ac97_hw_suspend(); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int pxa2xx_ac97_dev_resume(struct device *dev) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci return pxa2xx_ac97_hw_resume(); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, 28662306a36Sopenharmony_ci pxa2xx_ac97_dev_suspend, pxa2xx_ac97_dev_resume); 28762306a36Sopenharmony_ci#endif 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic struct platform_driver pxa2xx_ac97_driver = { 29062306a36Sopenharmony_ci .probe = pxa2xx_ac97_dev_probe, 29162306a36Sopenharmony_ci .remove_new = pxa2xx_ac97_dev_remove, 29262306a36Sopenharmony_ci .driver = { 29362306a36Sopenharmony_ci .name = "pxa2xx-ac97", 29462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 29562306a36Sopenharmony_ci .pm = &pxa2xx_ac97_pm_ops, 29662306a36Sopenharmony_ci#endif 29762306a36Sopenharmony_ci .of_match_table = of_match_ptr(pxa2xx_ac97_dt_ids), 29862306a36Sopenharmony_ci }, 29962306a36Sopenharmony_ci}; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cimodule_platform_driver(pxa2xx_ac97_driver); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ciMODULE_AUTHOR("Nicolas Pitre"); 30462306a36Sopenharmony_ciMODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip"); 30562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 30662306a36Sopenharmony_ciMODULE_ALIAS("platform:pxa2xx-ac97"); 307