Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.9 437 lines 11 kB view raw
1/* sound/soc/samsung/ac97.c 2 * 3 * ALSA SoC Audio Layer - S3C AC97 Controller driver 4 * Evolved from s3c2443-ac97.c 5 * 6 * Copyright (c) 2010 Samsung Electronics Co. Ltd 7 * Author: Jaswinder Singh <jassisinghbrar@gmail.com> 8 * Credits: Graeme Gregory, Sean Choi 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 */ 14 15#include <linux/io.h> 16#include <linux/delay.h> 17#include <linux/clk.h> 18#include <linux/module.h> 19 20#include <sound/soc.h> 21 22#include "regs-ac97.h" 23#include <linux/platform_data/asoc-s3c.h> 24 25#include "dma.h" 26 27#define AC_CMD_ADDR(x) (x << 16) 28#define AC_CMD_DATA(x) (x & 0xffff) 29 30#define S3C_AC97_DAI_PCM 0 31#define S3C_AC97_DAI_MIC 1 32 33struct s3c_ac97_info { 34 struct clk *ac97_clk; 35 void __iomem *regs; 36 struct mutex lock; 37 struct completion done; 38}; 39static struct s3c_ac97_info s3c_ac97; 40 41static struct snd_dmaengine_dai_dma_data s3c_ac97_pcm_out = { 42 .addr_width = 4, 43}; 44 45static struct snd_dmaengine_dai_dma_data s3c_ac97_pcm_in = { 46 .addr_width = 4, 47}; 48 49static struct snd_dmaengine_dai_dma_data s3c_ac97_mic_in = { 50 .addr_width = 4, 51}; 52 53static void s3c_ac97_activate(struct snd_ac97 *ac97) 54{ 55 u32 ac_glbctrl, stat; 56 57 stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7; 58 if (stat == S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE) 59 return; /* Return if already active */ 60 61 reinit_completion(&s3c_ac97.done); 62 63 ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); 64 ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON; 65 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); 66 msleep(1); 67 68 ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE; 69 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); 70 msleep(1); 71 72 ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); 73 ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE; 74 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); 75 76 if (!wait_for_completion_timeout(&s3c_ac97.done, HZ)) 77 pr_err("AC97: Unable to activate!\n"); 78} 79 80static unsigned short s3c_ac97_read(struct snd_ac97 *ac97, 81 unsigned short reg) 82{ 83 u32 ac_glbctrl, ac_codec_cmd; 84 u32 stat, addr, data; 85 86 mutex_lock(&s3c_ac97.lock); 87 88 s3c_ac97_activate(ac97); 89 90 reinit_completion(&s3c_ac97.done); 91 92 ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD); 93 ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg); 94 writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD); 95 96 udelay(50); 97 98 ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); 99 ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE; 100 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); 101 102 if (!wait_for_completion_timeout(&s3c_ac97.done, HZ)) 103 pr_err("AC97: Unable to read!\n"); 104 105 stat = readl(s3c_ac97.regs + S3C_AC97_STAT); 106 addr = (stat >> 16) & 0x7f; 107 data = (stat & 0xffff); 108 109 if (addr != reg) 110 pr_err("ac97: req addr = %02x, rep addr = %02x\n", 111 reg, addr); 112 113 mutex_unlock(&s3c_ac97.lock); 114 115 return (unsigned short)data; 116} 117 118static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg, 119 unsigned short val) 120{ 121 u32 ac_glbctrl, ac_codec_cmd; 122 123 mutex_lock(&s3c_ac97.lock); 124 125 s3c_ac97_activate(ac97); 126 127 reinit_completion(&s3c_ac97.done); 128 129 ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD); 130 ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val); 131 writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD); 132 133 udelay(50); 134 135 ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); 136 ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE; 137 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); 138 139 if (!wait_for_completion_timeout(&s3c_ac97.done, HZ)) 140 pr_err("AC97: Unable to write!\n"); 141 142 ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD); 143 ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ; 144 writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD); 145 146 mutex_unlock(&s3c_ac97.lock); 147} 148 149static void s3c_ac97_cold_reset(struct snd_ac97 *ac97) 150{ 151 pr_debug("AC97: Cold reset\n"); 152 writel(S3C_AC97_GLBCTRL_COLDRESET, 153 s3c_ac97.regs + S3C_AC97_GLBCTRL); 154 msleep(1); 155 156 writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL); 157 msleep(1); 158} 159 160static void s3c_ac97_warm_reset(struct snd_ac97 *ac97) 161{ 162 u32 stat; 163 164 stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7; 165 if (stat == S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE) 166 return; /* Return if already active */ 167 168 pr_debug("AC97: Warm reset\n"); 169 170 writel(S3C_AC97_GLBCTRL_WARMRESET, s3c_ac97.regs + S3C_AC97_GLBCTRL); 171 msleep(1); 172 173 writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL); 174 msleep(1); 175 176 s3c_ac97_activate(ac97); 177} 178 179static irqreturn_t s3c_ac97_irq(int irq, void *dev_id) 180{ 181 u32 ac_glbctrl, ac_glbstat; 182 183 ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT); 184 185 if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) { 186 187 ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); 188 ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE; 189 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); 190 191 complete(&s3c_ac97.done); 192 } 193 194 ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); 195 ac_glbctrl |= (1<<30); /* Clear interrupt */ 196 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); 197 198 return IRQ_HANDLED; 199} 200 201static struct snd_ac97_bus_ops s3c_ac97_ops = { 202 .read = s3c_ac97_read, 203 .write = s3c_ac97_write, 204 .warm_reset = s3c_ac97_warm_reset, 205 .reset = s3c_ac97_cold_reset, 206}; 207 208static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd, 209 struct snd_soc_dai *dai) 210{ 211 u32 ac_glbctrl; 212 213 ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); 214 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 215 ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK; 216 else 217 ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK; 218 219 switch (cmd) { 220 case SNDRV_PCM_TRIGGER_START: 221 case SNDRV_PCM_TRIGGER_RESUME: 222 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 223 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 224 ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA; 225 else 226 ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA; 227 break; 228 229 case SNDRV_PCM_TRIGGER_STOP: 230 case SNDRV_PCM_TRIGGER_SUSPEND: 231 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 232 break; 233 } 234 235 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); 236 237 return 0; 238} 239 240static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream, 241 int cmd, struct snd_soc_dai *dai) 242{ 243 u32 ac_glbctrl; 244 245 ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL); 246 ac_glbctrl &= ~S3C_AC97_GLBCTRL_MICINTM_MASK; 247 248 switch (cmd) { 249 case SNDRV_PCM_TRIGGER_START: 250 case SNDRV_PCM_TRIGGER_RESUME: 251 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 252 ac_glbctrl |= S3C_AC97_GLBCTRL_MICINTM_DMA; 253 break; 254 255 case SNDRV_PCM_TRIGGER_STOP: 256 case SNDRV_PCM_TRIGGER_SUSPEND: 257 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 258 break; 259 } 260 261 writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); 262 263 return 0; 264} 265 266static const struct snd_soc_dai_ops s3c_ac97_dai_ops = { 267 .trigger = s3c_ac97_trigger, 268}; 269 270static const struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = { 271 .trigger = s3c_ac97_mic_trigger, 272}; 273 274static int s3c_ac97_dai_probe(struct snd_soc_dai *dai) 275{ 276 snd_soc_dai_init_dma_data(dai, &s3c_ac97_pcm_out, &s3c_ac97_pcm_in); 277 278 return 0; 279} 280 281static int s3c_ac97_mic_dai_probe(struct snd_soc_dai *dai) 282{ 283 snd_soc_dai_init_dma_data(dai, NULL, &s3c_ac97_mic_in); 284 285 return 0; 286} 287 288static struct snd_soc_dai_driver s3c_ac97_dai[] = { 289 [S3C_AC97_DAI_PCM] = { 290 .name = "samsung-ac97", 291 .bus_control = true, 292 .playback = { 293 .stream_name = "AC97 Playback", 294 .channels_min = 2, 295 .channels_max = 2, 296 .rates = SNDRV_PCM_RATE_8000_48000, 297 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 298 .capture = { 299 .stream_name = "AC97 Capture", 300 .channels_min = 2, 301 .channels_max = 2, 302 .rates = SNDRV_PCM_RATE_8000_48000, 303 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 304 .probe = s3c_ac97_dai_probe, 305 .ops = &s3c_ac97_dai_ops, 306 }, 307 [S3C_AC97_DAI_MIC] = { 308 .name = "samsung-ac97-mic", 309 .bus_control = true, 310 .capture = { 311 .stream_name = "AC97 Mic Capture", 312 .channels_min = 1, 313 .channels_max = 1, 314 .rates = SNDRV_PCM_RATE_8000_48000, 315 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 316 .probe = s3c_ac97_mic_dai_probe, 317 .ops = &s3c_ac97_mic_dai_ops, 318 }, 319}; 320 321static const struct snd_soc_component_driver s3c_ac97_component = { 322 .name = "s3c-ac97", 323}; 324 325static int s3c_ac97_probe(struct platform_device *pdev) 326{ 327 struct resource *mem_res, *irq_res; 328 struct s3c_audio_pdata *ac97_pdata; 329 int ret; 330 331 ac97_pdata = pdev->dev.platform_data; 332 if (!ac97_pdata || !ac97_pdata->cfg_gpio) { 333 dev_err(&pdev->dev, "cfg_gpio callback not provided!\n"); 334 return -EINVAL; 335 } 336 337 /* Check for availability of necessary resource */ 338 irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 339 if (!irq_res) { 340 dev_err(&pdev->dev, "AC97 IRQ not provided!\n"); 341 return -ENXIO; 342 } 343 344 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 345 s3c_ac97.regs = devm_ioremap_resource(&pdev->dev, mem_res); 346 if (IS_ERR(s3c_ac97.regs)) 347 return PTR_ERR(s3c_ac97.regs); 348 349 s3c_ac97_pcm_out.filter_data = ac97_pdata->dma_playback; 350 s3c_ac97_pcm_out.addr = mem_res->start + S3C_AC97_PCM_DATA; 351 s3c_ac97_pcm_in.filter_data = ac97_pdata->dma_capture; 352 s3c_ac97_pcm_in.addr = mem_res->start + S3C_AC97_PCM_DATA; 353 s3c_ac97_mic_in.filter_data = ac97_pdata->dma_capture_mic; 354 s3c_ac97_mic_in.addr = mem_res->start + S3C_AC97_MIC_DATA; 355 356 init_completion(&s3c_ac97.done); 357 mutex_init(&s3c_ac97.lock); 358 359 s3c_ac97.ac97_clk = devm_clk_get(&pdev->dev, "ac97"); 360 if (IS_ERR(s3c_ac97.ac97_clk)) { 361 dev_err(&pdev->dev, "ac97 failed to get ac97_clock\n"); 362 ret = -ENODEV; 363 goto err2; 364 } 365 clk_prepare_enable(s3c_ac97.ac97_clk); 366 367 if (ac97_pdata->cfg_gpio(pdev)) { 368 dev_err(&pdev->dev, "Unable to configure gpio\n"); 369 ret = -EINVAL; 370 goto err3; 371 } 372 373 ret = request_irq(irq_res->start, s3c_ac97_irq, 374 0, "AC97", NULL); 375 if (ret < 0) { 376 dev_err(&pdev->dev, "ac97: interrupt request failed.\n"); 377 goto err4; 378 } 379 380 ret = snd_soc_set_ac97_ops(&s3c_ac97_ops); 381 if (ret != 0) { 382 dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret); 383 goto err4; 384 } 385 386 ret = samsung_asoc_dma_platform_register(&pdev->dev, 387 ac97_pdata->dma_filter, 388 NULL, NULL); 389 if (ret) { 390 dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret); 391 goto err5; 392 } 393 394 ret = devm_snd_soc_register_component(&pdev->dev, &s3c_ac97_component, 395 s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai)); 396 if (ret) 397 goto err5; 398 399 return 0; 400err5: 401 free_irq(irq_res->start, NULL); 402err4: 403err3: 404 clk_disable_unprepare(s3c_ac97.ac97_clk); 405err2: 406 snd_soc_set_ac97_ops(NULL); 407 return ret; 408} 409 410static int s3c_ac97_remove(struct platform_device *pdev) 411{ 412 struct resource *irq_res; 413 414 irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 415 if (irq_res) 416 free_irq(irq_res->start, NULL); 417 418 clk_disable_unprepare(s3c_ac97.ac97_clk); 419 snd_soc_set_ac97_ops(NULL); 420 421 return 0; 422} 423 424static struct platform_driver s3c_ac97_driver = { 425 .probe = s3c_ac97_probe, 426 .remove = s3c_ac97_remove, 427 .driver = { 428 .name = "samsung-ac97", 429 }, 430}; 431 432module_platform_driver(s3c_ac97_driver); 433 434MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>"); 435MODULE_DESCRIPTION("AC97 driver for the Samsung SoC"); 436MODULE_LICENSE("GPL"); 437MODULE_ALIAS("platform:samsung-ac97");