1 /* 2 * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip. 3 * 4 * Author: Nicolas Pitre 5 * Created: Dec 02, 2004 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. 11 */ 12 13 #include <linux/init.h> 14 #include <linux/io.h> 15 #include <linux/module.h> 16 #include <linux/platform_device.h> 17 #include <linux/dmaengine.h> 18 #include <linux/dma/pxa-dma.h> 19 20 #include <sound/core.h> 21 #include <sound/ac97_codec.h> 22 #include <sound/soc.h> 23 #include <sound/pxa2xx-lib.h> 24 #include <sound/dmaengine_pcm.h> 25 26 #include <mach/hardware.h> 27 #include <mach/regs-ac97.h> 28 #include <mach/audio.h> 29 30 static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) 31 { 32 pxa2xx_ac97_try_warm_reset(); 33 34 pxa2xx_ac97_finish_reset(); 35 } 36 37 static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) 38 { 39 pxa2xx_ac97_try_cold_reset(); 40 41 pxa2xx_ac97_finish_reset(); 42 } 43 44 static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97, 45 unsigned short reg) 46 { 47 int ret; 48 49 ret = pxa2xx_ac97_read(ac97->num, reg); 50 if (ret < 0) 51 return 0; 52 else 53 return (unsigned short)(ret & 0xffff); 54 } 55 56 static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97, 57 unsigned short reg, unsigned short val) 58 { 59 int ret; 60 61 ret = pxa2xx_ac97_write(ac97->num, reg, val); 62 } 63 64 static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { 65 .read = pxa2xx_ac97_legacy_read, 66 .write = pxa2xx_ac97_legacy_write, 67 .warm_reset = pxa2xx_ac97_warm_reset, 68 .reset = pxa2xx_ac97_cold_reset, 69 }; 70 71 static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { 72 .addr = __PREG(PCDR), 73 .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 74 .chan_name = "pcm_pcm_stereo_in", 75 .maxburst = 32, 76 }; 77 78 static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { 79 .addr = __PREG(PCDR), 80 .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 81 .chan_name = "pcm_pcm_stereo_out", 82 .maxburst = 32, 83 }; 84 85 static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { 86 .addr = __PREG(MODR), 87 .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, 88 .chan_name = "pcm_aux_mono_out", 89 .maxburst = 16, 90 }; 91 92 static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { 93 .addr = __PREG(MODR), 94 .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, 95 .chan_name = "pcm_aux_mono_in", 96 .maxburst = 16, 97 }; 98 99 static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { 100 .addr = __PREG(MCDR), 101 .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, 102 .chan_name = "pcm_aux_mic_mono", 103 .maxburst = 16, 104 }; 105 106 static int pxa2xx_ac97_hifi_startup(struct snd_pcm_substream *substream, 107 struct snd_soc_dai *cpu_dai) 108 { 109 struct snd_dmaengine_dai_dma_data *dma_data; 110 111 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 112 dma_data = &pxa2xx_ac97_pcm_stereo_out; 113 else 114 dma_data = &pxa2xx_ac97_pcm_stereo_in; 115 116 snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); 117 118 return 0; 119 } 120 121 static int pxa2xx_ac97_aux_startup(struct snd_pcm_substream *substream, 122 struct snd_soc_dai *cpu_dai) 123 { 124 struct snd_dmaengine_dai_dma_data *dma_data; 125 126 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 127 dma_data = &pxa2xx_ac97_pcm_aux_mono_out; 128 else 129 dma_data = &pxa2xx_ac97_pcm_aux_mono_in; 130 131 snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); 132 133 return 0; 134 } 135 136 static int pxa2xx_ac97_mic_startup(struct snd_pcm_substream *substream, 137 struct snd_soc_dai *cpu_dai) 138 { 139 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 140 return -ENODEV; 141 snd_soc_dai_set_dma_data(cpu_dai, substream, 142 &pxa2xx_ac97_pcm_mic_mono_in); 143 144 return 0; 145 } 146 147 #define PXA2XX_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ 148 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ 149 SNDRV_PCM_RATE_48000) 150 151 static const struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = { 152 .startup = pxa2xx_ac97_hifi_startup, 153 }; 154 155 static const struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = { 156 .startup = pxa2xx_ac97_aux_startup, 157 }; 158 159 static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = { 160 .startup = pxa2xx_ac97_mic_startup, 161 }; 162 163 /* 164 * There is only 1 physical AC97 interface for pxa2xx, but it 165 * has extra fifo's that can be used for aux DACs and ADCs. 166 */ 167 static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = { 168 { 169 .name = "pxa2xx-ac97", 170 .bus_control = true, 171 .playback = { 172 .stream_name = "AC97 Playback", 173 .channels_min = 2, 174 .channels_max = 2, 175 .rates = PXA2XX_AC97_RATES, 176 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 177 .capture = { 178 .stream_name = "AC97 Capture", 179 .channels_min = 2, 180 .channels_max = 2, 181 .rates = PXA2XX_AC97_RATES, 182 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 183 .ops = &pxa_ac97_hifi_dai_ops, 184 }, 185 { 186 .name = "pxa2xx-ac97-aux", 187 .bus_control = true, 188 .playback = { 189 .stream_name = "AC97 Aux Playback", 190 .channels_min = 1, 191 .channels_max = 1, 192 .rates = PXA2XX_AC97_RATES, 193 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 194 .capture = { 195 .stream_name = "AC97 Aux Capture", 196 .channels_min = 1, 197 .channels_max = 1, 198 .rates = PXA2XX_AC97_RATES, 199 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 200 .ops = &pxa_ac97_aux_dai_ops, 201 }, 202 { 203 .name = "pxa2xx-ac97-mic", 204 .bus_control = true, 205 .capture = { 206 .stream_name = "AC97 Mic Capture", 207 .channels_min = 1, 208 .channels_max = 1, 209 .rates = PXA2XX_AC97_RATES, 210 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 211 .ops = &pxa_ac97_mic_dai_ops, 212 }, 213 }; 214 215 static const struct snd_soc_component_driver pxa_ac97_component = { 216 .name = "pxa-ac97", 217 .ops = &pxa2xx_pcm_ops, 218 .pcm_new = pxa2xx_soc_pcm_new, 219 .pcm_free = pxa2xx_pcm_free_dma_buffers, 220 }; 221 222 #ifdef CONFIG_OF 223 static const struct of_device_id pxa2xx_ac97_dt_ids[] = { 224 { .compatible = "marvell,pxa250-ac97", }, 225 { .compatible = "marvell,pxa270-ac97", }, 226 { .compatible = "marvell,pxa300-ac97", }, 227 { } 228 }; 229 MODULE_DEVICE_TABLE(of, pxa2xx_ac97_dt_ids); 230 231 #endif 232 233 static int pxa2xx_ac97_dev_probe(struct platform_device *pdev) 234 { 235 int ret; 236 237 if (pdev->id != -1) { 238 dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n"); 239 return -ENXIO; 240 } 241 242 ret = pxa2xx_ac97_hw_probe(pdev); 243 if (ret) { 244 dev_err(&pdev->dev, "PXA2xx AC97 hw probe error (%d)\n", ret); 245 return ret; 246 } 247 248 ret = snd_soc_set_ac97_ops(&pxa2xx_ac97_ops); 249 if (ret != 0) 250 return ret; 251 252 /* Punt most of the init to the SoC probe; we may need the machine 253 * driver to do interesting things with the clocking to get us up 254 * and running. 255 */ 256 return snd_soc_register_component(&pdev->dev, &pxa_ac97_component, 257 pxa_ac97_dai_driver, ARRAY_SIZE(pxa_ac97_dai_driver)); 258 } 259 260 static int pxa2xx_ac97_dev_remove(struct platform_device *pdev) 261 { 262 snd_soc_unregister_component(&pdev->dev); 263 snd_soc_set_ac97_ops(NULL); 264 pxa2xx_ac97_hw_remove(pdev); 265 return 0; 266 } 267 268 #ifdef CONFIG_PM_SLEEP 269 static int pxa2xx_ac97_dev_suspend(struct device *dev) 270 { 271 return pxa2xx_ac97_hw_suspend(); 272 } 273 274 static int pxa2xx_ac97_dev_resume(struct device *dev) 275 { 276 return pxa2xx_ac97_hw_resume(); 277 } 278 279 static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, 280 pxa2xx_ac97_dev_suspend, pxa2xx_ac97_dev_resume); 281 #endif 282 283 static struct platform_driver pxa2xx_ac97_driver = { 284 .probe = pxa2xx_ac97_dev_probe, 285 .remove = pxa2xx_ac97_dev_remove, 286 .driver = { 287 .name = "pxa2xx-ac97", 288 #ifdef CONFIG_PM_SLEEP 289 .pm = &pxa2xx_ac97_pm_ops, 290 #endif 291 .of_match_table = of_match_ptr(pxa2xx_ac97_dt_ids), 292 }, 293 }; 294 295 module_platform_driver(pxa2xx_ac97_driver); 296 297 MODULE_AUTHOR("Nicolas Pitre"); 298 MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip"); 299 MODULE_LICENSE("GPL"); 300 MODULE_ALIAS("platform:pxa2xx-ac97"); 301