linux/sound/soc/s3c24xx/s3c2443-ac97.c
<<
>>
Prefs
   1/*
   2 * s3c2443-ac97.c  --  ALSA Soc Audio Layer
   3 *
   4 * (c) 2007 Wolfson Microelectronics PLC.
   5 * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
   6 *
   7 *  Copyright (C) 2005, Sean Choi <sh428.choi@samsung.com>
   8 *  All rights reserved.
   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 *  Revision history
  15 *      21st Mar 2007   Initial Version
  16 */
  17
  18#include <linux/init.h>
  19#include <linux/module.h>
  20#include <linux/platform_device.h>
  21#include <linux/interrupt.h>
  22#include <linux/wait.h>
  23#include <linux/delay.h>
  24#include <linux/clk.h>
  25
  26#include <sound/driver.h>
  27#include <sound/core.h>
  28#include <sound/pcm.h>
  29#include <sound/ac97_codec.h>
  30#include <sound/initval.h>
  31#include <sound/soc.h>
  32
  33#include <asm/hardware.h>
  34#include <asm/io.h>
  35#include <asm/plat-s3c/regs-ac97.h>
  36#include <asm/arch/regs-gpio.h>
  37#include <asm/arch/regs-clock.h>
  38#include <asm/arch/audio.h>
  39#include <asm/dma.h>
  40#include <asm/arch/dma.h>
  41
  42#include "s3c24xx-pcm.h"
  43#include "s3c24xx-ac97.h"
  44
  45struct s3c24xx_ac97_info {
  46        void __iomem    *regs;
  47        struct clk      *ac97_clk;
  48};
  49static struct s3c24xx_ac97_info s3c24xx_ac97;
  50
  51DECLARE_COMPLETION(ac97_completion);
  52static u32 codec_ready;
  53static DECLARE_MUTEX(ac97_mutex);
  54
  55static unsigned short s3c2443_ac97_read(struct snd_ac97 *ac97,
  56        unsigned short reg)
  57{
  58        u32 ac_glbctrl;
  59        u32 ac_codec_cmd;
  60        u32 stat, addr, data;
  61
  62        down(&ac97_mutex);
  63
  64        codec_ready = S3C_AC97_GLBSTAT_CODECREADY;
  65        ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
  66        ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
  67        writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
  68
  69        udelay(50);
  70
  71        ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
  72        ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
  73        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
  74
  75        wait_for_completion(&ac97_completion);
  76
  77        stat = readl(s3c24xx_ac97.regs + S3C_AC97_STAT);
  78        addr = (stat >> 16) & 0x7f;
  79        data = (stat & 0xffff);
  80
  81        if (addr != reg)
  82                printk(KERN_ERR "s3c24xx-ac97: req addr = %02x,"
  83                                " rep addr = %02x\n", reg, addr);
  84
  85        up(&ac97_mutex);
  86
  87        return (unsigned short)data;
  88}
  89
  90static void s3c2443_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
  91        unsigned short val)
  92{
  93        u32 ac_glbctrl;
  94        u32 ac_codec_cmd;
  95
  96        down(&ac97_mutex);
  97
  98        codec_ready = S3C_AC97_GLBSTAT_CODECREADY;
  99        ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
 100        ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
 101        writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
 102
 103        udelay(50);
 104
 105        ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 106        ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
 107        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 108
 109        wait_for_completion(&ac97_completion);
 110
 111        ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
 112        ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
 113        writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
 114
 115        up(&ac97_mutex);
 116
 117}
 118
 119static void s3c2443_ac97_warm_reset(struct snd_ac97 *ac97)
 120{
 121        u32 ac_glbctrl;
 122
 123        ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 124        ac_glbctrl = S3C_AC97_GLBCTRL_WARMRESET;
 125        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 126        msleep(1);
 127
 128        ac_glbctrl = 0;
 129        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 130        msleep(1);
 131}
 132
 133static void s3c2443_ac97_cold_reset(struct snd_ac97 *ac97)
 134{
 135        u32 ac_glbctrl;
 136
 137        ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 138        ac_glbctrl = S3C_AC97_GLBCTRL_COLDRESET;
 139        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 140        msleep(1);
 141
 142        ac_glbctrl = 0;
 143        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 144        msleep(1);
 145
 146        ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 147        ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
 148        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 149        msleep(1);
 150
 151        ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
 152        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 153        msleep(1);
 154
 155        ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA |
 156                S3C_AC97_GLBCTRL_PCMINTM_DMA | S3C_AC97_GLBCTRL_MICINTM_DMA;
 157        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 158}
 159
 160static irqreturn_t s3c2443_ac97_irq(int irq, void *dev_id)
 161{
 162        int status;
 163        u32 ac_glbctrl;
 164
 165        status = readl(s3c24xx_ac97.regs + S3C_AC97_GLBSTAT) & codec_ready;
 166
 167        if (status) {
 168                ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 169                ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
 170                writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 171                complete(&ac97_completion);
 172        }
 173        return IRQ_HANDLED;
 174}
 175
 176struct snd_ac97_bus_ops soc_ac97_ops = {
 177        .read   = s3c2443_ac97_read,
 178        .write  = s3c2443_ac97_write,
 179        .warm_reset     = s3c2443_ac97_warm_reset,
 180        .reset  = s3c2443_ac97_cold_reset,
 181};
 182
 183static struct s3c2410_dma_client s3c2443_dma_client_out = {
 184        .name = "AC97 PCM Stereo out"
 185};
 186
 187static struct s3c2410_dma_client s3c2443_dma_client_in = {
 188        .name = "AC97 PCM Stereo in"
 189};
 190
 191static struct s3c2410_dma_client s3c2443_dma_client_micin = {
 192        .name = "AC97 Mic Mono in"
 193};
 194
 195static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_out = {
 196        .client         = &s3c2443_dma_client_out,
 197        .channel        = DMACH_PCM_OUT,
 198        .dma_addr       = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
 199        .dma_size       = 4,
 200};
 201
 202static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_in = {
 203        .client         = &s3c2443_dma_client_in,
 204        .channel        = DMACH_PCM_IN,
 205        .dma_addr       = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
 206        .dma_size       = 4,
 207};
 208
 209static struct s3c24xx_pcm_dma_params s3c2443_ac97_mic_mono_in = {
 210        .client         = &s3c2443_dma_client_micin,
 211        .channel        = DMACH_MIC_IN,
 212        .dma_addr       = S3C2440_PA_AC97 + S3C_AC97_MIC_DATA,
 213        .dma_size       = 4,
 214};
 215
 216static int s3c2443_ac97_probe(struct platform_device *pdev)
 217{
 218        int ret;
 219        u32 ac_glbctrl;
 220
 221        s3c24xx_ac97.regs = ioremap(S3C2440_PA_AC97, 0x100);
 222        if (s3c24xx_ac97.regs == NULL)
 223                return -ENXIO;
 224
 225        s3c24xx_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
 226        if (s3c24xx_ac97.ac97_clk == NULL) {
 227                printk(KERN_ERR "s3c2443-ac97 failed to get ac97_clock\n");
 228                iounmap(s3c24xx_ac97.regs);
 229                return -ENODEV;
 230        }
 231        clk_enable(s3c24xx_ac97.ac97_clk);
 232
 233        s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2443_GPE0_AC_nRESET);
 234        s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2443_GPE1_AC_SYNC);
 235        s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2443_GPE2_AC_BITCLK);
 236        s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2443_GPE3_AC_SDI);
 237        s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2443_GPE4_AC_SDO);
 238
 239        ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 240        ac_glbctrl = S3C_AC97_GLBCTRL_COLDRESET;
 241        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 242        msleep(1);
 243
 244        ac_glbctrl = 0;
 245        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 246        msleep(1);
 247
 248        ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 249        ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
 250        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 251        msleep(1);
 252
 253        ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
 254        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 255
 256        ret = request_irq(IRQ_S3C2443_AC97, s3c2443_ac97_irq,
 257                IRQF_DISABLED, "AC97", NULL);
 258        if (ret < 0) {
 259                printk(KERN_ERR "s3c24xx-ac97: interrupt request failed.\n");
 260                clk_disable(s3c24xx_ac97.ac97_clk);
 261                clk_put(s3c24xx_ac97.ac97_clk);
 262                iounmap(s3c24xx_ac97.regs);
 263        }
 264        return ret;
 265}
 266
 267static void s3c2443_ac97_remove(struct platform_device *pdev)
 268{
 269        free_irq(IRQ_S3C2443_AC97, NULL);
 270        clk_disable(s3c24xx_ac97.ac97_clk);
 271        clk_put(s3c24xx_ac97.ac97_clk);
 272        iounmap(s3c24xx_ac97.regs);
 273}
 274
 275static int s3c2443_ac97_hw_params(struct snd_pcm_substream *substream,
 276                                struct snd_pcm_hw_params *params)
 277{
 278        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 279        struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
 280
 281        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 282                cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_out;
 283        else
 284                cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_in;
 285
 286        return 0;
 287}
 288
 289static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd)
 290{
 291        u32 ac_glbctrl;
 292
 293        ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 294        switch(cmd) {
 295        case SNDRV_PCM_TRIGGER_START:
 296        case SNDRV_PCM_TRIGGER_RESUME:
 297        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 298                if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 299                        ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
 300                else
 301                        ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
 302                break;
 303        case SNDRV_PCM_TRIGGER_STOP:
 304        case SNDRV_PCM_TRIGGER_SUSPEND:
 305        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 306                if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 307                        ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
 308                else
 309                        ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
 310                break;
 311        }
 312        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 313
 314        return 0;
 315}
 316
 317static int s3c2443_ac97_hw_mic_params(struct snd_pcm_substream *substream,
 318        struct snd_pcm_hw_params *params)
 319{
 320        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 321        struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
 322
 323        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 324                return -ENODEV;
 325        else
 326                cpu_dai->dma_data = &s3c2443_ac97_mic_mono_in;
 327
 328        return 0;
 329}
 330
 331static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
 332        int cmd)
 333{
 334        u32 ac_glbctrl;
 335
 336        ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 337        switch(cmd) {
 338        case SNDRV_PCM_TRIGGER_START:
 339        case SNDRV_PCM_TRIGGER_RESUME:
 340        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 341                ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
 342                break;
 343        case SNDRV_PCM_TRIGGER_STOP:
 344        case SNDRV_PCM_TRIGGER_SUSPEND:
 345        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 346                ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
 347        }
 348        writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
 349
 350        return 0;
 351}
 352
 353#define s3c2443_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
 354                SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
 355                SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
 356
 357struct snd_soc_cpu_dai s3c2443_ac97_dai[] = {
 358{
 359        .name = "s3c2443-ac97",
 360        .id = 0,
 361        .type = SND_SOC_DAI_AC97,
 362        .probe = s3c2443_ac97_probe,
 363        .remove = s3c2443_ac97_remove,
 364        .playback = {
 365                .stream_name = "AC97 Playback",
 366                .channels_min = 2,
 367                .channels_max = 2,
 368                .rates = s3c2443_AC97_RATES,
 369                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
 370        .capture = {
 371                .stream_name = "AC97 Capture",
 372                .channels_min = 2,
 373                .channels_max = 2,
 374                .rates = s3c2443_AC97_RATES,
 375                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
 376        .ops = {
 377                .hw_params = s3c2443_ac97_hw_params,
 378                .trigger = s3c2443_ac97_trigger},
 379},
 380{
 381        .name = "pxa2xx-ac97-mic",
 382        .id = 1,
 383        .type = SND_SOC_DAI_AC97,
 384        .capture = {
 385                .stream_name = "AC97 Mic Capture",
 386                .channels_min = 1,
 387                .channels_max = 1,
 388                .rates = s3c2443_AC97_RATES,
 389                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
 390        .ops = {
 391                .hw_params = s3c2443_ac97_hw_mic_params,
 392                .trigger = s3c2443_ac97_mic_trigger,},
 393},
 394};
 395
 396EXPORT_SYMBOL_GPL(s3c2443_ac97_dai);
 397EXPORT_SYMBOL_GPL(soc_ac97_ops);
 398
 399MODULE_AUTHOR("Graeme Gregory");
 400MODULE_DESCRIPTION("AC97 driver for the Samsung s3c2443 chip");
 401MODULE_LICENSE("GPL");
 402