1 // SPDX-License-Identifier: GPL-2.0-only << 2 /* 1 /* 3 * linux/sound/pxa2xx-ac97.c -- AC97 support f 2 * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip. 4 * 3 * 5 * Author: Nicolas Pitre 4 * Author: Nicolas Pitre 6 * Created: Dec 02, 2004 5 * Created: Dec 02, 2004 7 * Copyright: MontaVista Software Inc. 6 * Copyright: MontaVista Software Inc. >> 7 * >> 8 * This program is free software; you can redistribute it and/or modify >> 9 * it under the terms of the GNU General Public License version 2 as >> 10 * published by the Free Software Foundation. 8 */ 11 */ 9 12 10 #include <linux/init.h> 13 #include <linux/init.h> 11 #include <linux/io.h> << 12 #include <linux/module.h> 14 #include <linux/module.h> 13 #include <linux/platform_device.h> 15 #include <linux/platform_device.h> 14 #include <linux/dmaengine.h> << 15 #include <linux/dma/pxa-dma.h> << 16 16 17 #include <sound/ac97/controller.h> << 18 #include <sound/core.h> 17 #include <sound/core.h> 19 #include <sound/ac97_codec.h> 18 #include <sound/ac97_codec.h> 20 #include <sound/soc.h> 19 #include <sound/soc.h> 21 #include <sound/pxa2xx-lib.h> 20 #include <sound/pxa2xx-lib.h> 22 #include <sound/dmaengine_pcm.h> << 23 << 24 #include <linux/platform_data/asoc-pxa.h> << 25 << 26 #define PCDR 0x0040 /* PCM FIFO Data Regis << 27 #define MODR 0x0140 /* Modem FIFO Data Reg << 28 #define MCDR 0x0060 /* Mic-in FIFO Data Re << 29 21 30 static void pxa2xx_ac97_warm_reset(struct ac97 !! 22 #include <mach/hardware.h> 31 { !! 23 #include <mach/regs-ac97.h> 32 pxa2xx_ac97_try_warm_reset(); !! 24 #include <mach/dma.h> >> 25 #include <mach/audio.h> 33 26 34 pxa2xx_ac97_finish_reset(); !! 27 #include "pxa2xx-pcm.h" 35 } !! 28 #include "pxa2xx-ac97.h" 36 29 37 static void pxa2xx_ac97_cold_reset(struct ac97 !! 30 static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) 38 { 31 { 39 pxa2xx_ac97_try_cold_reset(); !! 32 pxa2xx_ac97_try_warm_reset(ac97); 40 33 41 pxa2xx_ac97_finish_reset(); !! 34 pxa2xx_ac97_finish_reset(ac97); 42 } 35 } 43 36 44 static int pxa2xx_ac97_read_actrl(struct ac97_ !! 37 static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) 45 unsigned sho << 46 { 38 { 47 return pxa2xx_ac97_read(slot, reg); !! 39 pxa2xx_ac97_try_cold_reset(ac97); 48 } << 49 40 50 static int pxa2xx_ac97_write_actrl(struct ac97 !! 41 pxa2xx_ac97_finish_reset(ac97); 51 unsigned sh << 52 { << 53 return pxa2xx_ac97_write(slot, reg, va << 54 } 42 } 55 43 56 static struct ac97_controller_ops pxa2xx_ac97_ !! 44 struct snd_ac97_bus_ops soc_ac97_ops = { 57 .read = pxa2xx_ac97_read_actrl, !! 45 .read = pxa2xx_ac97_read, 58 .write = pxa2xx_ac97_write_actrl, !! 46 .write = pxa2xx_ac97_write, 59 .warm_reset = pxa2xx_ac97_warm_res 47 .warm_reset = pxa2xx_ac97_warm_reset, 60 .reset = pxa2xx_ac97_cold_reset, 48 .reset = pxa2xx_ac97_cold_reset, 61 }; 49 }; 62 50 63 static struct snd_dmaengine_dai_dma_data pxa2x !! 51 static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = { 64 .addr_width = DMA_SLAVE_BUSWIDTH_4 !! 52 .name = "AC97 PCM Stereo out", 65 .chan_name = "pcm_pcm_stereo_in", !! 53 .dev_addr = __PREG(PCDR), 66 .maxburst = 32, !! 54 .drcmr = &DRCMR(12), >> 55 .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | >> 56 DCMD_BURST32 | DCMD_WIDTH4, >> 57 }; >> 58 >> 59 static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = { >> 60 .name = "AC97 PCM Stereo in", >> 61 .dev_addr = __PREG(PCDR), >> 62 .drcmr = &DRCMR(11), >> 63 .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | >> 64 DCMD_BURST32 | DCMD_WIDTH4, >> 65 }; >> 66 >> 67 static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = { >> 68 .name = "AC97 Aux PCM (Slot 5) Mono out", >> 69 .dev_addr = __PREG(MODR), >> 70 .drcmr = &DRCMR(10), >> 71 .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | >> 72 DCMD_BURST16 | DCMD_WIDTH2, >> 73 }; >> 74 >> 75 static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = { >> 76 .name = "AC97 Aux PCM (Slot 5) Mono in", >> 77 .dev_addr = __PREG(MODR), >> 78 .drcmr = &DRCMR(9), >> 79 .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | >> 80 DCMD_BURST16 | DCMD_WIDTH2, >> 81 }; >> 82 >> 83 static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = { >> 84 .name = "AC97 Mic PCM (Slot 6) Mono in", >> 85 .dev_addr = __PREG(MCDR), >> 86 .drcmr = &DRCMR(8), >> 87 .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | >> 88 DCMD_BURST16 | DCMD_WIDTH2, 67 }; 89 }; 68 90 69 static struct snd_dmaengine_dai_dma_data pxa2x !! 91 #ifdef CONFIG_PM 70 .addr_width = DMA_SLAVE_BUSWIDTH_4 !! 92 static int pxa2xx_ac97_suspend(struct snd_soc_dai *dai) 71 .chan_name = "pcm_pcm_stereo_out" !! 93 { 72 .maxburst = 32, !! 94 return pxa2xx_ac97_hw_suspend(); 73 }; !! 95 } 74 96 75 static struct snd_dmaengine_dai_dma_data pxa2x !! 97 static int pxa2xx_ac97_resume(struct snd_soc_dai *dai) 76 .addr_width = DMA_SLAVE_BUSWIDTH_2 !! 98 { 77 .chan_name = "pcm_aux_mono_out", !! 99 return pxa2xx_ac97_hw_resume(); 78 .maxburst = 16, !! 100 } 79 }; << 80 101 81 static struct snd_dmaengine_dai_dma_data pxa2x !! 102 #else 82 .addr_width = DMA_SLAVE_BUSWIDTH_2 !! 103 #define pxa2xx_ac97_suspend NULL 83 .chan_name = "pcm_aux_mono_in", !! 104 #define pxa2xx_ac97_resume NULL 84 .maxburst = 16, !! 105 #endif 85 }; << 86 106 87 static struct snd_dmaengine_dai_dma_data pxa2x !! 107 static int pxa2xx_ac97_probe(struct platform_device *pdev, 88 .addr_width = DMA_SLAVE_BUSWIDTH_2 !! 108 struct snd_soc_dai *dai) 89 .chan_name = "pcm_aux_mic_mono", !! 109 { 90 .maxburst = 16, !! 110 return pxa2xx_ac97_hw_probe(to_platform_device(dai->dev)); 91 }; !! 111 } >> 112 >> 113 static void pxa2xx_ac97_remove(struct platform_device *pdev, >> 114 struct snd_soc_dai *dai) >> 115 { >> 116 pxa2xx_ac97_hw_remove(to_platform_device(dai->dev)); >> 117 } 92 118 93 static int pxa2xx_ac97_hifi_startup(struct snd !! 119 static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, 94 struct snd !! 120 struct snd_pcm_hw_params *params, >> 121 struct snd_soc_dai *dai) 95 { 122 { 96 struct snd_dmaengine_dai_dma_data *dma !! 123 struct snd_soc_pcm_runtime *rtd = substream->private_data; >> 124 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 97 125 98 if (substream->stream == SNDRV_PCM_STR 126 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 99 dma_data = &pxa2xx_ac97_pcm_st !! 127 cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_out; 100 else 128 else 101 dma_data = &pxa2xx_ac97_pcm_st !! 129 cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_in; 102 << 103 snd_soc_dai_set_dma_data(cpu_dai, subs << 104 130 105 return 0; 131 return 0; 106 } 132 } 107 133 108 static int pxa2xx_ac97_aux_startup(struct snd_ !! 134 static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, 109 struct snd_ !! 135 struct snd_pcm_hw_params *params, >> 136 struct snd_soc_dai *dai) 110 { 137 { 111 struct snd_dmaengine_dai_dma_data *dma !! 138 struct snd_soc_pcm_runtime *rtd = substream->private_data; >> 139 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 112 140 113 if (substream->stream == SNDRV_PCM_STR 141 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 114 dma_data = &pxa2xx_ac97_pcm_au !! 142 cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_out; 115 else 143 else 116 dma_data = &pxa2xx_ac97_pcm_au !! 144 cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_in; 117 << 118 snd_soc_dai_set_dma_data(cpu_dai, subs << 119 145 120 return 0; 146 return 0; 121 } 147 } 122 148 123 static int pxa2xx_ac97_mic_startup(struct snd_ !! 149 static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream, 124 struct snd_ !! 150 struct snd_pcm_hw_params *params, >> 151 struct snd_soc_dai *dai) 125 { 152 { >> 153 struct snd_soc_pcm_runtime *rtd = substream->private_data; >> 154 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; >> 155 126 if (substream->stream == SNDRV_PCM_STR 156 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 127 return -ENODEV; 157 return -ENODEV; 128 snd_soc_dai_set_dma_data(cpu_dai, subs !! 158 else 129 &pxa2xx_ac97_ !! 159 cpu_dai->dma_data = &pxa2xx_ac97_pcm_mic_mono_in; 130 160 131 return 0; 161 return 0; 132 } 162 } 133 163 134 #define PXA2XX_AC97_RATES (SNDRV_PCM_RATE_8000 164 #define PXA2XX_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ 135 SNDRV_PCM_RATE_16000 | SNDRV_P 165 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ 136 SNDRV_PCM_RATE_48000) 166 SNDRV_PCM_RATE_48000) 137 167 138 static const struct snd_soc_dai_ops pxa_ac97_h !! 168 static struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = { 139 .startup = pxa2xx_ac97_hifi_sta !! 169 .hw_params = pxa2xx_ac97_hw_params, 140 }; 170 }; 141 171 142 static const struct snd_soc_dai_ops pxa_ac97_a !! 172 static struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = { 143 .startup = pxa2xx_ac97_aux_star !! 173 .hw_params = pxa2xx_ac97_hw_aux_params, 144 }; 174 }; 145 175 146 static const struct snd_soc_dai_ops pxa_ac97_m !! 176 static struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = { 147 .startup = pxa2xx_ac97_mic_star !! 177 .hw_params = pxa2xx_ac97_hw_mic_params, 148 }; 178 }; 149 179 150 /* 180 /* 151 * There is only 1 physical AC97 interface for 181 * There is only 1 physical AC97 interface for pxa2xx, but it 152 * has extra fifo's that can be used for aux D 182 * has extra fifo's that can be used for aux DACs and ADCs. 153 */ 183 */ 154 static struct snd_soc_dai_driver pxa_ac97_dai_ !! 184 struct snd_soc_dai pxa_ac97_dai[] = { 155 { 185 { 156 .name = "pxa2xx-ac97", 186 .name = "pxa2xx-ac97", >> 187 .id = 0, >> 188 .ac97_control = 1, >> 189 .probe = pxa2xx_ac97_probe, >> 190 .remove = pxa2xx_ac97_remove, >> 191 .suspend = pxa2xx_ac97_suspend, >> 192 .resume = pxa2xx_ac97_resume, 157 .playback = { 193 .playback = { 158 .stream_name = "AC97 Playback" 194 .stream_name = "AC97 Playback", 159 .channels_min = 2, 195 .channels_min = 2, 160 .channels_max = 2, 196 .channels_max = 2, 161 .rates = PXA2XX_AC97_RATES, 197 .rates = PXA2XX_AC97_RATES, 162 .formats = SNDRV_PCM_FMTBIT_S1 198 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 163 .capture = { 199 .capture = { 164 .stream_name = "AC97 Capture", 200 .stream_name = "AC97 Capture", 165 .channels_min = 2, 201 .channels_min = 2, 166 .channels_max = 2, 202 .channels_max = 2, 167 .rates = PXA2XX_AC97_RATES, 203 .rates = PXA2XX_AC97_RATES, 168 .formats = SNDRV_PCM_FMTBIT_S1 204 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 169 .ops = &pxa_ac97_hifi_dai_ops, 205 .ops = &pxa_ac97_hifi_dai_ops, 170 }, 206 }, 171 { 207 { 172 .name = "pxa2xx-ac97-aux", 208 .name = "pxa2xx-ac97-aux", >> 209 .id = 1, >> 210 .ac97_control = 1, 173 .playback = { 211 .playback = { 174 .stream_name = "AC97 Aux Playb 212 .stream_name = "AC97 Aux Playback", 175 .channels_min = 1, 213 .channels_min = 1, 176 .channels_max = 1, 214 .channels_max = 1, 177 .rates = PXA2XX_AC97_RATES, 215 .rates = PXA2XX_AC97_RATES, 178 .formats = SNDRV_PCM_FMTBIT_S1 216 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 179 .capture = { 217 .capture = { 180 .stream_name = "AC97 Aux Captu 218 .stream_name = "AC97 Aux Capture", 181 .channels_min = 1, 219 .channels_min = 1, 182 .channels_max = 1, 220 .channels_max = 1, 183 .rates = PXA2XX_AC97_RATES, 221 .rates = PXA2XX_AC97_RATES, 184 .formats = SNDRV_PCM_FMTBIT_S1 222 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 185 .ops = &pxa_ac97_aux_dai_ops, 223 .ops = &pxa_ac97_aux_dai_ops, 186 }, 224 }, 187 { 225 { 188 .name = "pxa2xx-ac97-mic", 226 .name = "pxa2xx-ac97-mic", >> 227 .id = 2, >> 228 .ac97_control = 1, 189 .capture = { 229 .capture = { 190 .stream_name = "AC97 Mic Captu 230 .stream_name = "AC97 Mic Capture", 191 .channels_min = 1, 231 .channels_min = 1, 192 .channels_max = 1, 232 .channels_max = 1, 193 .rates = PXA2XX_AC97_RATES, 233 .rates = PXA2XX_AC97_RATES, 194 .formats = SNDRV_PCM_FMTBIT_S1 234 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 195 .ops = &pxa_ac97_mic_dai_ops, 235 .ops = &pxa_ac97_mic_dai_ops, 196 }, 236 }, 197 }; 237 }; 198 238 199 static const struct snd_soc_component_driver p !! 239 EXPORT_SYMBOL_GPL(pxa_ac97_dai); 200 .name = "pxa-ac97", !! 240 EXPORT_SYMBOL_GPL(soc_ac97_ops); 201 .pcm_construct = pxa2xx_soc_pcm_new, << 202 .open = pxa2xx_soc_pcm_open, << 203 .close = pxa2xx_soc_pcm_close << 204 .hw_params = pxa2xx_soc_pcm_hw_pa << 205 .prepare = pxa2xx_soc_pcm_prepa << 206 .trigger = pxa2xx_soc_pcm_trigg << 207 .pointer = pxa2xx_soc_pcm_point << 208 }; << 209 << 210 #ifdef CONFIG_OF << 211 static const struct of_device_id pxa2xx_ac97_d << 212 { .compatible = "marvell,pxa250-ac97", << 213 { .compatible = "marvell,pxa270-ac97", << 214 { .compatible = "marvell,pxa300-ac97", << 215 { } << 216 }; << 217 MODULE_DEVICE_TABLE(of, pxa2xx_ac97_dt_ids); << 218 << 219 #endif << 220 241 221 static int pxa2xx_ac97_dev_probe(struct platfo !! 242 static int __devinit pxa2xx_ac97_dev_probe(struct platform_device *pdev) 222 { 243 { 223 int ret; !! 244 int i; 224 struct ac97_controller *ctrl; << 225 pxa2xx_audio_ops_t *pdata = pdev->dev. 245 pxa2xx_audio_ops_t *pdata = pdev->dev.platform_data; 226 struct resource *regs; << 227 void **codecs_pdata; << 228 246 229 if (pdev->id != -1) { !! 247 if (pdev->id >= 0) { 230 dev_err(&pdev->dev, "PXA2xx ha 248 dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n"); 231 return -ENXIO; 249 return -ENXIO; 232 } 250 } 233 251 234 regs = platform_get_resource(pdev, IOR !! 252 for (i = 0; i < ARRAY_SIZE(pxa_ac97_dai); i++) { 235 if (!regs) !! 253 pxa_ac97_dai[i].dev = &pdev->dev; 236 return -ENXIO; !! 254 if (pdata && pdata->codec_pdata[0]) 237 !! 255 pxa_ac97_dai[i].ac97_pdata = pdata->codec_pdata[0]; 238 pxa2xx_ac97_pcm_stereo_in.addr = regs- << 239 pxa2xx_ac97_pcm_stereo_out.addr = regs << 240 pxa2xx_ac97_pcm_aux_mono_out.addr = re << 241 pxa2xx_ac97_pcm_aux_mono_in.addr = reg << 242 pxa2xx_ac97_pcm_mic_mono_in.addr = reg << 243 << 244 ret = pxa2xx_ac97_hw_probe(pdev); << 245 if (ret) { << 246 dev_err(&pdev->dev, "PXA2xx AC << 247 return ret; << 248 } 256 } 249 257 250 codecs_pdata = pdata ? pdata->codec_pd << 251 ctrl = snd_ac97_controller_register(&p << 252 AC << 253 co << 254 if (IS_ERR(ctrl)) << 255 return PTR_ERR(ctrl); << 256 << 257 platform_set_drvdata(pdev, ctrl); << 258 /* Punt most of the init to the SoC pr 258 /* Punt most of the init to the SoC probe; we may need the machine 259 * driver to do interesting things wit 259 * driver to do interesting things with the clocking to get us up 260 * and running. 260 * and running. 261 */ 261 */ 262 return devm_snd_soc_register_component !! 262 return snd_soc_register_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai)); 263 pxa_ << 264 } << 265 << 266 static void pxa2xx_ac97_dev_remove(struct plat << 267 { << 268 struct ac97_controller *ctrl = platfor << 269 << 270 snd_ac97_controller_unregister(ctrl); << 271 pxa2xx_ac97_hw_remove(pdev); << 272 } 263 } 273 264 274 static int pxa2xx_ac97_dev_suspend(struct devi !! 265 static int __devexit pxa2xx_ac97_dev_remove(struct platform_device *pdev) 275 { 266 { 276 return pxa2xx_ac97_hw_suspend(); !! 267 snd_soc_unregister_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai)); 277 } << 278 268 279 static int pxa2xx_ac97_dev_resume(struct devic !! 269 return 0; 280 { << 281 return pxa2xx_ac97_hw_resume(); << 282 } 270 } 283 271 284 static DEFINE_SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm << 285 pxa2xx_ac97_dev_suspend, pxa2x << 286 << 287 static struct platform_driver pxa2xx_ac97_driv 272 static struct platform_driver pxa2xx_ac97_driver = { 288 .probe = pxa2xx_ac97_dev_prob 273 .probe = pxa2xx_ac97_dev_probe, 289 .remove = pxa2xx_ac97_dev_remo !! 274 .remove = __devexit_p(pxa2xx_ac97_dev_remove), 290 .driver = { 275 .driver = { 291 .name = "pxa2xx-ac97", 276 .name = "pxa2xx-ac97", 292 .pm = &pxa2xx_ac97_pm_ops, !! 277 .owner = THIS_MODULE, 293 .of_match_table = of_match_ptr << 294 }, 278 }, 295 }; 279 }; 296 280 297 module_platform_driver(pxa2xx_ac97_driver); !! 281 static int __init pxa_ac97_init(void) >> 282 { >> 283 return platform_driver_register(&pxa2xx_ac97_driver); >> 284 } >> 285 module_init(pxa_ac97_init); >> 286 >> 287 static void __exit pxa_ac97_exit(void) >> 288 { >> 289 platform_driver_unregister(&pxa2xx_ac97_driver); >> 290 } >> 291 module_exit(pxa_ac97_exit); 298 292 299 MODULE_AUTHOR("Nicolas Pitre"); 293 MODULE_AUTHOR("Nicolas Pitre"); 300 MODULE_DESCRIPTION("AC97 driver for the Intel 294 MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip"); 301 MODULE_LICENSE("GPL"); 295 MODULE_LICENSE("GPL"); 302 MODULE_ALIAS("platform:pxa2xx-ac97"); << 303 296
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.