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
  34static int *cmd_count;
  35static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
  36
  37#define SPORT_REQ(x) \
  38        [x] = {P_SPORT##x##_TFS, P_SPORT##x##_DTPRI, P_SPORT##x##_TSCLK, \
  39               P_SPORT##x##_RFS, P_SPORT##x##_DRPRI, P_SPORT##x##_RSCLK, 0}
  40static u16 sport_req[][7] = {
  41#ifdef SPORT0_TCR1
  42        SPORT_REQ(0),
  43#endif
  44#ifdef SPORT1_TCR1
  45        SPORT_REQ(1),
  46#endif
  47#ifdef SPORT2_TCR1
  48        SPORT_REQ(2),
  49#endif
  50#ifdef SPORT3_TCR1
  51        SPORT_REQ(3),
  52#endif
  53};
  54
  55#define SPORT_PARAMS(x) \
  56        [x] = { \
  57                .dma_rx_chan = CH_SPORT##x##_RX, \
  58                .dma_tx_chan = CH_SPORT##x##_TX, \
  59                .err_irq     = IRQ_SPORT##x##_ERROR, \
  60                .regs        = (struct sport_register *)SPORT##x##_TCR1, \
  61        }
  62static struct sport_param sport_params[4] = {
  63#ifdef SPORT0_TCR1
  64        SPORT_PARAMS(0),
  65#endif
  66#ifdef SPORT1_TCR1
  67        SPORT_PARAMS(1),
  68#endif
  69#ifdef SPORT2_TCR1
  70        SPORT_PARAMS(2),
  71#endif
  72#ifdef SPORT3_TCR1
  73        SPORT_PARAMS(3),
  74#endif
  75};
  76
  77void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src,
  78                size_t count, unsigned int chan_mask)
  79{
  80        while (count--) {
  81                dst->ac97_tag = TAG_VALID;
  82                if (chan_mask & SP_FL) {
  83                        dst->ac97_pcm_r = *src++;
  84                        dst->ac97_tag |= TAG_PCM_RIGHT;
  85                }
  86                if (chan_mask & SP_FR) {
  87                        dst->ac97_pcm_l = *src++;
  88                        dst->ac97_tag |= TAG_PCM_LEFT;
  89
  90                }
  91#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
  92                if (chan_mask & SP_SR) {
  93                        dst->ac97_sl = *src++;
  94                        dst->ac97_tag |= TAG_PCM_SL;
  95                }
  96                if (chan_mask & SP_SL) {
  97                        dst->ac97_sr = *src++;
  98                        dst->ac97_tag |= TAG_PCM_SR;
  99                }
 100                if (chan_mask & SP_LFE) {
 101                        dst->ac97_lfe = *src++;
 102                        dst->ac97_tag |= TAG_PCM_LFE;
 103                }
 104                if (chan_mask & SP_FC) {
 105                        dst->ac97_center = *src++;
 106                        dst->ac97_tag |= TAG_PCM_CENTER;
 107                }
 108#endif
 109                dst++;
 110        }
 111}
 112EXPORT_SYMBOL(bf5xx_pcm_to_ac97);
 113
 114void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u16 *dst,
 115                size_t count)
 116{
 117        while (count--) {
 118                *(dst++) = src->ac97_pcm_l;
 119                *(dst++) = src->ac97_pcm_r;
 120                src++;
 121        }
 122}
 123EXPORT_SYMBOL(bf5xx_ac97_to_pcm);
 124
 125static unsigned int sport_tx_curr_frag(struct sport_device *sport)
 126{
 127        return sport->tx_curr_frag = sport_curr_offset_tx(sport) /
 128                        sport->tx_fragsize;
 129}
 130
 131static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
 132{
 133        struct sport_device *sport = sport_handle;
 134        int nextfrag = sport_tx_curr_frag(sport);
 135        struct ac97_frame *nextwrite;
 136
 137        sport_incfrag(sport, &nextfrag, 1);
 138
 139        nextwrite = (struct ac97_frame *)(sport->tx_buf +
 140                        nextfrag * sport->tx_fragsize);
 141        pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n",
 142                sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]);
 143        nextwrite[cmd_count[nextfrag]].ac97_tag |= TAG_CMD;
 144        nextwrite[cmd_count[nextfrag]].ac97_addr = addr;
 145        nextwrite[cmd_count[nextfrag]].ac97_data = data;
 146        ++cmd_count[nextfrag];
 147        pr_debug("ac97_sport: Inserting %02x/%04x into fragment %d\n",
 148                        addr >> 8, data, nextfrag);
 149}
 150
 151static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
 152        unsigned short reg)
 153{
 154        struct ac97_frame out_frame[2], in_frame[2];
 155
 156        pr_debug("%s enter 0x%x\n", __func__, reg);
 157
 158        /* When dma descriptor is enabled, the register should not be read */
 159        if (sport_handle->tx_run || sport_handle->rx_run) {
 160                pr_err("Could you send a mail to cliff.cai@analog.com "
 161                                "to report this?\n");
 162                return -EFAULT;
 163        }
 164
 165        memset(&out_frame, 0, 2 * sizeof(struct ac97_frame));
 166        memset(&in_frame, 0, 2 * sizeof(struct ac97_frame));
 167        out_frame[0].ac97_tag = TAG_VALID | TAG_CMD;
 168        out_frame[0].ac97_addr = ((reg << 8) | 0x8000);
 169        sport_send_and_recv(sport_handle, (unsigned char *)&out_frame,
 170                        (unsigned char *)&in_frame,
 171                        2 * sizeof(struct ac97_frame));
 172        return in_frame[1].ac97_data;
 173}
 174
 175void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
 176        unsigned short val)
 177{
 178        pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val);
 179
 180        if (sport_handle->tx_run) {
 181                enqueue_cmd(ac97, (reg << 8), val); /* write */
 182                enqueue_cmd(ac97, (reg << 8) | 0x8000, 0); /* read back */
 183        } else {
 184                struct ac97_frame frame;
 185                memset(&frame, 0, sizeof(struct ac97_frame));
 186                frame.ac97_tag = TAG_VALID | TAG_CMD;
 187                frame.ac97_addr = (reg << 8);
 188                frame.ac97_data = val;
 189                sport_send_and_recv(sport_handle, (unsigned char *)&frame, \
 190                                NULL, sizeof(struct ac97_frame));
 191        }
 192}
 193
 194static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97)
 195{
 196#if defined(CONFIG_BF54x) || defined(CONFIG_BF561) || \
 197 (defined(BF537_FAMILY) && (CONFIG_SND_BF5XX_SPORT_NUM == 1))
 198
 199#define CONCAT(a, b, c) a ## b ## c
 200#define BFIN_SPORT_RFS(x) CONCAT(P_SPORT, x, _RFS)
 201
 202        u16 per = BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM);
 203        u16 gpio = P_IDENT(BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM));
 204
 205        pr_debug("%s enter\n", __func__);
 206
 207        peripheral_free(per);
 208        gpio_request(gpio, "bf5xx-ac97");
 209        gpio_direction_output(gpio, 1);
 210        udelay(2);
 211        gpio_set_value(gpio, 0);
 212        udelay(1);
 213        gpio_free(gpio);
 214        peripheral_request(per, "soc-audio");
 215#else
 216        pr_info("%s: Not implemented\n", __func__);
 217#endif
 218}
 219
 220static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97)
 221{
 222#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
 223        pr_debug("%s enter\n", __func__);
 224
 225        /* It is specified for bf548-ezkit */
 226        gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 0);
 227        /* Keep reset pin low for 1 ms */
 228        mdelay(1);
 229        gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
 230        /* Wait for bit clock recover */
 231        mdelay(1);
 232#else
 233        pr_info("%s: Not implemented\n", __func__);
 234#endif
 235}
 236
 237struct snd_ac97_bus_ops soc_ac97_ops = {
 238        .read   = bf5xx_ac97_read,
 239        .write  = bf5xx_ac97_write,
 240        .warm_reset     = bf5xx_ac97_warm_reset,
 241        .reset  = bf5xx_ac97_cold_reset,
 242};
 243EXPORT_SYMBOL_GPL(soc_ac97_ops);
 244
 245#ifdef CONFIG_PM
 246static int bf5xx_ac97_suspend(struct snd_soc_dai *dai)
 247{
 248        struct sport_device *sport =
 249                (struct sport_device *)dai->private_data;
 250
 251        pr_debug("%s : sport %d\n", __func__, dai->id);
 252        if (!dai->active)
 253                return 0;
 254        if (dai->capture.active)
 255                sport_rx_stop(sport);
 256        if (dai->playback.active)
 257                sport_tx_stop(sport);
 258        return 0;
 259}
 260
 261static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
 262{
 263        int ret;
 264        struct sport_device *sport =
 265                (struct sport_device *)dai->private_data;
 266
 267        pr_debug("%s : sport %d\n", __func__, dai->id);
 268        if (!dai->active)
 269                return 0;
 270
 271        ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
 272        if (ret) {
 273                pr_err("SPORT is busy!\n");
 274                return -EBUSY;
 275        }
 276
 277        ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
 278        if (ret) {
 279                pr_err("SPORT is busy!\n");
 280                return -EBUSY;
 281        }
 282
 283        ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
 284        if (ret) {
 285                pr_err("SPORT is busy!\n");
 286                return -EBUSY;
 287        }
 288
 289        if (dai->capture.active)
 290                sport_rx_start(sport);
 291        if (dai->playback.active)
 292                sport_tx_start(sport);
 293        return 0;
 294}
 295
 296#else
 297#define bf5xx_ac97_suspend      NULL
 298#define bf5xx_ac97_resume       NULL
 299#endif
 300
 301static int bf5xx_ac97_probe(struct platform_device *pdev,
 302                            struct snd_soc_dai *dai)
 303{
 304        int ret = 0;
 305        cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
 306        if (cmd_count == NULL)
 307                return -ENOMEM;
 308
 309        if (peripheral_request_list(sport_req[sport_num], "soc-audio")) {
 310                pr_err("Requesting Peripherals failed\n");
 311                ret =  -EFAULT;
 312                goto peripheral_err;
 313        }
 314
 315#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
 316        /* Request PB3 as reset pin */
 317        if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) {
 318                pr_err("Failed to request GPIO_%d for reset\n",
 319                                CONFIG_SND_BF5XX_RESET_GPIO_NUM);
 320                ret =  -1;
 321                goto gpio_err;
 322        }
 323        gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
 324#endif
 325        sport_handle = sport_init(&sport_params[sport_num], 2, \
 326                        sizeof(struct ac97_frame), NULL);
 327        if (!sport_handle) {
 328                ret = -ENODEV;
 329                goto sport_err;
 330        }
 331        /*SPORT works in TDM mode to simulate AC97 transfers*/
 332        ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
 333        if (ret) {
 334                pr_err("SPORT is busy!\n");
 335                ret = -EBUSY;
 336                goto sport_config_err;
 337        }
 338
 339        ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
 340        if (ret) {
 341                pr_err("SPORT is busy!\n");
 342                ret = -EBUSY;
 343                goto sport_config_err;
 344        }
 345
 346        ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
 347        if (ret) {
 348                pr_err("SPORT is busy!\n");
 349                ret = -EBUSY;
 350                goto sport_config_err;
 351        }
 352
 353        return 0;
 354
 355sport_config_err:
 356        kfree(sport_handle);
 357sport_err:
 358#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
 359        gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
 360gpio_err:
 361#endif
 362        peripheral_free_list(sport_req[sport_num]);
 363peripheral_err:
 364        free_page((unsigned long)cmd_count);
 365        cmd_count = NULL;
 366
 367        return ret;
 368}
 369
 370static void bf5xx_ac97_remove(struct platform_device *pdev,
 371                              struct snd_soc_dai *dai)
 372{
 373        free_page((unsigned long)cmd_count);
 374        cmd_count = NULL;
 375        peripheral_free_list(sport_req[sport_num]);
 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        .ac97_control = 1,
 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#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
 393                .channels_max = 6,
 394#else
 395                .channels_max = 2,
 396#endif
 397                .rates = SNDRV_PCM_RATE_48000,
 398                .formats = SNDRV_PCM_FMTBIT_S16_LE, },
 399        .capture = {
 400                .stream_name = "AC97 Capture",
 401                .channels_min = 2,
 402                .channels_max = 2,
 403                .rates = SNDRV_PCM_RATE_48000,
 404                .formats = SNDRV_PCM_FMTBIT_S16_LE, },
 405};
 406EXPORT_SYMBOL_GPL(bfin_ac97_dai);
 407
 408static int __init bfin_ac97_init(void)
 409{
 410        return snd_soc_register_dai(&bfin_ac97_dai);
 411}
 412module_init(bfin_ac97_init);
 413
 414static void __exit bfin_ac97_exit(void)
 415{
 416        snd_soc_unregister_dai(&bfin_ac97_dai);
 417}
 418module_exit(bfin_ac97_exit);
 419
 420MODULE_AUTHOR("Roy Huang");
 421MODULE_DESCRIPTION("AC97 driver for ADI Blackfin");
 422MODULE_LICENSE("GPL");
 423
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.