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 s3c_dma_params s3c_ac97_pcm_out = { 42 .dma_size = 4, 43}; 44 45static struct s3c_dma_params s3c_ac97_pcm_in = { 46 .dma_size = 4, 47}; 48 49static struct s3c_dma_params s3c_ac97_mic_in = { 50 .dma_size = 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!"); 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!"); 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!"); 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 samsung_asoc_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 samsung_asoc_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, *dmatx_res, *dmarx_res, *dmamic_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 dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0); 339 if (!dmatx_res) { 340 dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n"); 341 return -ENXIO; 342 } 343 344 dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1); 345 if (!dmarx_res) { 346 dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n"); 347 return -ENXIO; 348 } 349 350 dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2); 351 if (!dmamic_res) { 352 dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n"); 353 return -ENXIO; 354 } 355 356 irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 357 if (!irq_res) { 358 dev_err(&pdev->dev, "AC97 IRQ not provided!\n"); 359 return -ENXIO; 360 } 361 362 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 363 s3c_ac97.regs = devm_ioremap_resource(&pdev->dev, mem_res); 364 if (IS_ERR(s3c_ac97.regs)) 365 return PTR_ERR(s3c_ac97.regs); 366 367 s3c_ac97_pcm_out.channel = dmatx_res->start; 368 s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA; 369 s3c_ac97_pcm_in.channel = dmarx_res->start; 370 s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA; 371 s3c_ac97_mic_in.channel = dmamic_res->start; 372 s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA; 373 374 init_completion(&s3c_ac97.done); 375 mutex_init(&s3c_ac97.lock); 376 377 s3c_ac97.ac97_clk = devm_clk_get(&pdev->dev, "ac97"); 378 if (IS_ERR(s3c_ac97.ac97_clk)) { 379 dev_err(&pdev->dev, "ac97 failed to get ac97_clock\n"); 380 ret = -ENODEV; 381 goto err2; 382 } 383 clk_prepare_enable(s3c_ac97.ac97_clk); 384 385 if (ac97_pdata->cfg_gpio(pdev)) { 386 dev_err(&pdev->dev, "Unable to configure gpio\n"); 387 ret = -EINVAL; 388 goto err3; 389 } 390 391 ret = request_irq(irq_res->start, s3c_ac97_irq, 392 0, "AC97", NULL); 393 if (ret < 0) { 394 dev_err(&pdev->dev, "ac97: interrupt request failed.\n"); 395 goto err4; 396 } 397 398 ret = snd_soc_set_ac97_ops(&s3c_ac97_ops); 399 if (ret != 0) { 400 dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret); 401 goto err4; 402 } 403 404 ret = devm_snd_soc_register_component(&pdev->dev, &s3c_ac97_component, 405 s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai)); 406 if (ret) 407 goto err5; 408 409 ret = samsung_asoc_dma_platform_register(&pdev->dev); 410 if (ret) { 411 dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret); 412 goto err5; 413 } 414 415 return 0; 416err5: 417 free_irq(irq_res->start, NULL); 418err4: 419err3: 420 clk_disable_unprepare(s3c_ac97.ac97_clk); 421err2: 422 snd_soc_set_ac97_ops(NULL); 423 return ret; 424} 425 426static int s3c_ac97_remove(struct platform_device *pdev) 427{ 428 struct resource *irq_res; 429 430 irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 431 if (irq_res) 432 free_irq(irq_res->start, NULL); 433 434 clk_disable_unprepare(s3c_ac97.ac97_clk); 435 snd_soc_set_ac97_ops(NULL); 436 437 return 0; 438} 439 440static struct platform_driver s3c_ac97_driver = { 441 .probe = s3c_ac97_probe, 442 .remove = s3c_ac97_remove, 443 .driver = { 444 .name = "samsung-ac97", 445 }, 446}; 447 448module_platform_driver(s3c_ac97_driver); 449 450MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>"); 451MODULE_DESCRIPTION("AC97 driver for the Samsung SoC"); 452MODULE_LICENSE("GPL"); 453MODULE_ALIAS("platform:samsung-ac97"); 454