linux/sound/soc/blackfin/bf5xx-ac97.c
<<
>>
Prefs
   1/*
   2 * bf5xx-ac97.c -- AC97 support for the ADI blackfin chip.
   3 *
   4 * Author:      Roy Huang
   5 * Created:     11th. June 2007
   6 * Copyright:   Analog Device 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/module.h>
  15#include <linux/platform_device.h>
  16#include <linux/interrupt.h>
  17#include <linux/wait.h>
  18#include <linux/delay.h>
  19
  20#include <sound/core.h>
  21#include <sound/pcm.h>
  22#include <sound/ac97_codec.h>
  23#include <sound/initval.h>
  24#include <sound/soc.h>
  25
  26#include <asm/irq.h>
  27#include <asm/portmux.h>
  28#include <linux/mutex.h>
  29#include <linux/gpio.h>
  30
  31#include "bf5xx-sport.h"
  32#include "bf5xx-ac97.h"
  33
  34#if defined(CONFIG_BF54x)
  35#define PIN_REQ_SPORT_0 {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, \
  36                P_SPORT0_RFS, P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0}
  37
  38#define PIN_REQ_SPORT_1 {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, \
  39                P_SPORT1_RFS, P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0}
  40
  41#define PIN_REQ_SPORT_2 {P_SPORT2_TFS, P_SPORT2_DTPRI, P_SPORT2_TSCLK, \
  42                P_SPORT2_RFS, P_SPORT2_DRPRI, P_SPORT2_RSCLK, 0}
  43
  44#define PIN_REQ_SPORT_3 {P_SPORT3_TFS, P_SPORT3_DTPRI, P_SPORT3_TSCLK, \
  45                P_SPORT3_RFS, P_SPORT3_DRPRI, P_SPORT3_RSCLK, 0}
  46#else
  47#define PIN_REQ_SPORT_0 {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \
  48                 P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0}
  49
  50#define PIN_REQ_SPORT_1 {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \
  51                 P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0}
  52#endif
  53
  54static int *cmd_count;
  55static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
  56
  57#if defined(CONFIG_BF54x)
  58static struct sport_param sport_params[4] = {
  59        {
  60                .dma_rx_chan    = CH_SPORT0_RX,
  61                .dma_tx_chan    = CH_SPORT0_TX,
  62                .err_irq        = IRQ_SPORT0_ERR,
  63                .regs           = (struct sport_register *)SPORT0_TCR1,
  64        },
  65        {
  66                .dma_rx_chan    = CH_SPORT1_RX,
  67                .dma_tx_chan    = CH_SPORT1_TX,
  68                .err_irq        = IRQ_SPORT1_ERR,
  69                .regs           = (struct sport_register *)SPORT1_TCR1,
  70        },
  71        {
  72                .dma_rx_chan    = CH_SPORT2_RX,
  73                .dma_tx_chan    = CH_SPORT2_TX,
  74                .err_irq        = IRQ_SPORT2_ERR,
  75                .regs           = (struct sport_register *)SPORT2_TCR1,
  76        },
  77        {
  78                .dma_rx_chan    = CH_SPORT3_RX,
  79                .dma_tx_chan    = CH_SPORT3_TX,
  80                .err_irq        = IRQ_SPORT3_ERR,
  81                .regs           = (struct sport_register *)SPORT3_TCR1,
  82        }
  83};
  84#else
  85static struct sport_param sport_params[2] = {
  86        {
  87                .dma_rx_chan    = CH_SPORT0_RX,
  88                .dma_tx_chan    = CH_SPORT0_TX,
  89                .err_irq        = IRQ_SPORT0_ERROR,
  90                .regs           = (struct sport_register *)SPORT0_TCR1,
  91        },
  92        {
  93                .dma_rx_chan    = CH_SPORT1_RX,
  94                .dma_tx_chan    = CH_SPORT1_TX,
  95                .err_irq        = IRQ_SPORT1_ERROR,
  96                .regs           = (struct sport_register *)SPORT1_TCR1,
  97        }
  98};
  99#endif
 100
 101void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \
 102                size_t count)
 103{
 104        while (count--) {
 105                dst->ac97_tag = TAG_VALID | TAG_PCM;
 106                (dst++)->ac97_pcm = *src++;
 107        }
 108}
 109EXPORT_SYMBOL(bf5xx_pcm_to_ac97);
 110
 111void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \
 112                size_t count)
 113{
 114        while (count--)
 115                *(dst++) = (src++)->ac97_pcm;
 116}
 117EXPORT_SYMBOL(bf5xx_ac97_to_pcm);
 118
 119static unsigned int sport_tx_curr_frag(struct sport_device *sport)
 120{
 121        return sport->tx_curr_frag = sport_curr_offset_tx(sport) / \
 122                        sport->tx_fragsize;
 123}
 124
 125static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
 126{
 127        struct sport_device *sport = sport_handle;
 128        int nextfrag = sport_tx_curr_frag(sport);
 129        struct ac97_frame *nextwrite;
 130
 131        sport_incfrag(sport, &nextfrag, 1);
 132
 133        nextwrite = (struct ac97_frame *)(sport->tx_buf + \
 134                        nextfrag * sport->tx_fragsize);
 135        pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n",
 136                sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]);
 137        nextwrite[cmd_count[nextfrag]].ac97_tag |= TAG_CMD;
 138        nextwrite[cmd_count[nextfrag]].ac97_addr = addr;
 139        nextwrite[cmd_count[nextfrag]].ac97_data = data;
 140        ++cmd_count[nextfrag];
 141        pr_debug("ac97_sport: Inserting %02x/%04x into fragment %d\n",
 142                        addr >> 8, data, nextfrag);
 143}
 144
 145static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
 146        unsigned short reg)
 147{
 148        struct ac97_frame out_frame[2], in_frame[2];
 149
 150        pr_debug("%s enter 0x%x\n", __func__, reg);
 151
 152        /* When dma descriptor is enabled, the register should not be read */
 153        if (sport_handle->tx_run || sport_handle->rx_run) {
 154                pr_err("Could you send a mail to cliff.cai@analog.com "
 155                                "to report this?\n");
 156                return -EFAULT;
 157        }
 158
 159        memset(&out_frame, 0, 2 * sizeof(struct ac97_frame));
 160        memset(&in_frame, 0, 2 * sizeof(struct ac97_frame));
 161        out_frame[0].ac97_tag = TAG_VALID | TAG_CMD;
 162        out_frame[0].ac97_addr = ((reg << 8) | 0x8000);
 163        sport_send_and_recv(sport_handle, (unsigned char *)&out_frame,
 164                        (unsigned char *)&in_frame,
 165                        2 * sizeof(struct ac97_frame));
 166        return in_frame[1].ac97_data;
 167}
 168
 169void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
 170        unsigned short val)
 171{
 172        pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val);
 173
 174        if (sport_handle->tx_run) {
 175                enqueue_cmd(ac97, (reg << 8), val); /* write */
 176                enqueue_cmd(ac97, (reg << 8) | 0x8000, 0); /* read back */
 177        } else {
 178                struct ac97_frame frame;
 179                memset(&frame, 0, sizeof(struct ac97_frame));
 180                frame.ac97_tag = TAG_VALID | TAG_CMD;
 181                frame.ac97_addr = (reg << 8);
 182                frame.ac97_data = val;
 183                sport_send_and_recv(sport_handle, (unsigned char *)&frame, \
 184                                NULL, sizeof(struct ac97_frame));
 185        }
 186}
 187
 188static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97)
 189{
 190#if defined(CONFIG_BF54x) || defined(CONFIG_BF561) || \
 191 (defined(BF537_FAMILY) && (CONFIG_SND_BF5XX_SPORT_NUM == 1))
 192
 193#define CONCAT(a, b, c) a ## b ## c
 194#define BFIN_SPORT_RFS(x) CONCAT(P_SPORT, x, _RFS)
 195
 196        u16 per = BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM);
 197        u16 gpio = P_IDENT(BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM));
 198
 199        pr_debug("%s enter\n", __func__);
 200
 201        peripheral_free(per);
 202        gpio_request(gpio, "bf5xx-ac97");
 203        gpio_direction_output(gpio, 1);
 204        udelay(2);
 205        gpio_set_value(gpio, 0);
 206        udelay(1);
 207        gpio_free(gpio);
 208        peripheral_request(per, "soc-audio");
 209#else
 210        pr_info("%s: Not implemented\n", __func__);
 211#endif
 212}
 213
 214static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97)
 215{
 216#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
 217        pr_debug("%s enter\n", __func__);
 218
 219        /* It is specified for bf548-ezkit */
 220        gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 0);
 221        /* Keep reset pin low for 1 ms */
 222        mdelay(1);
 223        gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
 224        /* Wait for bit clock recover */
 225        mdelay(1);
 226#else
 227        pr_info("%s: Not implemented\n", __func__);
 228#endif
 229}
 230
 231struct snd_ac97_bus_ops soc_ac97_ops = {
 232        .read   = bf5xx_ac97_read,
 233        .write  = bf5xx_ac97_write,
 234        .warm_reset     = bf5xx_ac97_warm_reset,
 235        .reset  = bf5xx_ac97_cold_reset,
 236};
 237EXPORT_SYMBOL_GPL(soc_ac97_ops);
 238
 239#ifdef CONFIG_PM
 240static int bf5xx_ac97_suspend(struct platform_device *pdev,
 241        struct snd_soc_dai *dai)
 242{
 243        struct sport_device *sport =
 244                (struct sport_device *)dai->private_data;
 245
 246        pr_debug("%s : sport %d\n", __func__, dai->id);
 247        if (!dai->active)
 248                return 0;
 249        if (dai->capture.active)
 250                sport_rx_stop(sport);
 251        if (dai->playback.active)
 252                sport_tx_stop(sport);
 253        return 0;
 254}
 255
 256static int bf5xx_ac97_resume(struct platform_device *pdev,
 257        struct snd_soc_dai *dai)
 258{
 259        int ret;
 260        struct sport_device *sport =
 261                (struct sport_device *)dai->private_data;
 262
 263        pr_debug("%s : sport %d\n", __func__, dai->id);
 264        if (!dai->active)
 265                return 0;
 266
 267        ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
 268        if (ret) {
 269                pr_err("SPORT is busy!\n");
 270                return -EBUSY;
 271        }
 272
 273        ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
 274        if (ret) {
 275                pr_err("SPORT is busy!\n");
 276                return -EBUSY;
 277        }
 278
 279        ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
 280        if (ret) {
 281                pr_err("SPORT is busy!\n");
 282                return -EBUSY;
 283        }
 284
 285        if (dai->capture.active)
 286                sport_rx_start(sport);
 287        if (dai->playback.active)
 288                sport_tx_start(sport);
 289        return 0;
 290}
 291
 292#else
 293#define bf5xx_ac97_suspend      NULL
 294#define bf5xx_ac97_resume       NULL
 295#endif
 296
 297static int bf5xx_ac97_probe(struct platform_device *pdev,
 298                            struct snd_soc_dai *dai)
 299{
 300        int ret;
 301#if defined(CONFIG_BF54x)
 302        u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1,
 303                                 PIN_REQ_SPORT_2, PIN_REQ_SPORT_3};
 304#else
 305        u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1};
 306#endif
 307        cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
 308        if (cmd_count == NULL)
 309                return -ENOMEM;
 310
 311        if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
 312                pr_err("Requesting Peripherals failed\n");
 313                return -EFAULT;
 314                }
 315
 316#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
 317        /* Request PB3 as reset pin */
 318        if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) {
 319                pr_err("Failed to request GPIO_%d for reset\n",
 320                                CONFIG_SND_BF5XX_RESET_GPIO_NUM);
 321                peripheral_free_list(&sport_req[sport_num][0]);
 322                return -1;
 323        }
 324        gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
 325#endif
 326        sport_handle = sport_init(&sport_params[sport_num], 2, \
 327                        sizeof(struct ac97_frame), NULL);
 328        if (!sport_handle) {
 329                peripheral_free_list(&sport_req[sport_num][0]);
 330#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
 331                gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
 332#endif
 333                return -ENODEV;
 334        }
 335        /*SPORT works in TDM mode to simulate AC97 transfers*/
 336        ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
 337        if (ret) {
 338                pr_err("SPORT is busy!\n");
 339                kfree(sport_handle);
 340                peripheral_free_list(&sport_req[sport_num][0]);
 341#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
 342                gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
 343#endif
 344                return -EBUSY;
 345        }
 346
 347        ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
 348        if (ret) {
 349                pr_err("SPORT is busy!\n");
 350                kfree(sport_handle);
 351                peripheral_free_list(&sport_req[sport_num][0]);
 352#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
 353                gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
 354#endif
 355                return -EBUSY;
 356        }
 357
 358        ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
 359        if (ret) {
 360                pr_err("SPORT is busy!\n");
 361                kfree(sport_handle);
 362                peripheral_free_list(&sport_req[sport_num][0]);
 363#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
 364                gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
 365#endif
 366                return -EBUSY;
 367        }
 368        return 0;
 369}
 370
 371static void bf5xx_ac97_remove(struct platform_device *pdev,
 372                              struct snd_soc_dai *dai)
 373{
 374        free_page((unsigned long)cmd_count);
 375        cmd_count = NULL;
 376#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
 377        gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
 378#endif
 379}
 380
 381struct snd_soc_dai bfin_ac97_dai = {
 382        .name = "bf5xx-ac97",
 383        .id = 0,
 384        .type = SND_SOC_DAI_AC97,
 385        .probe = bf5xx_ac97_probe,
 386        .remove = bf5xx_ac97_remove,
 387        .suspend = bf5xx_ac97_suspend,
 388        .resume = bf5xx_ac97_resume,
 389        .playback = {
 390                .stream_name = "AC97 Playback",
 391                .channels_min = 2,
 392                .channels_max = 2,
 393                .rates = SNDRV_PCM_RATE_48000,
 394                .formats = SNDRV_PCM_FMTBIT_S16_LE, },
 395        .capture = {
 396                .stream_name = "AC97 Capture",
 397                .channels_min = 2,
 398                .channels_max = 2,
 399                .rates = SNDRV_PCM_RATE_48000,
 400                .formats = SNDRV_PCM_FMTBIT_S16_LE, },
 401};
 402EXPORT_SYMBOL_GPL(bfin_ac97_dai);
 403
 404MODULE_AUTHOR("Roy Huang");
 405MODULE_DESCRIPTION("AC97 driver for ADI Blackfin");
 406MODULE_LICENSE("GPL");
 407
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.