linux/sound/soc/blackfin/bf5xx-i2s.c
<<
>>
Prefs
   1/*
   2 * File:         sound/soc/blackfin/bf5xx-i2s.c
   3 * Author:       Cliff Cai <Cliff.Cai@analog.com>
   4 *
   5 * Created:      Tue June 06 2008
   6 * Description:  Blackfin I2S CPU DAI driver
   7 *
   8 * Modified:
   9 *               Copyright 2008 Analog Devices Inc.
  10 *
  11 * Bugs:         Enter bugs at http://blackfin.uclinux.org/
  12 *
  13 * This program is free software; you can redistribute it and/or modify
  14 * it under the terms of the GNU General Public License as published by
  15 * the Free Software Foundation; either version 2 of the License, or
  16 * (at your option) any later version.
  17 *
  18 * This program is distributed in the hope that it will be useful,
  19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21 * GNU General Public License for more details.
  22 *
  23 * You should have received a copy of the GNU General Public License
  24 * along with this program; if not, see the file COPYING, or write
  25 * to the Free Software Foundation, Inc.,
  26 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  27 */
  28
  29#include <linux/init.h>
  30#include <linux/module.h>
  31#include <linux/device.h>
  32#include <linux/delay.h>
  33#include <sound/core.h>
  34#include <sound/pcm.h>
  35#include <sound/pcm_params.h>
  36#include <sound/initval.h>
  37#include <sound/soc.h>
  38
  39#include <asm/irq.h>
  40#include <asm/portmux.h>
  41#include <linux/mutex.h>
  42#include <linux/gpio.h>
  43
  44#include "bf5xx-sport.h"
  45#include "bf5xx-i2s-pcm.h"
  46
  47struct bf5xx_i2s_port {
  48        u16 tcr1;
  49        u16 rcr1;
  50        u16 tcr2;
  51        u16 rcr2;
  52        int configured;
  53
  54        unsigned int slots;
  55        unsigned int tx_mask;
  56        unsigned int rx_mask;
  57
  58        struct bf5xx_i2s_pcm_data tx_dma_data;
  59        struct bf5xx_i2s_pcm_data rx_dma_data;
  60};
  61
  62static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
  63                unsigned int fmt)
  64{
  65        struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
  66        struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
  67        int ret = 0;
  68
  69        /* interface format:support I2S,slave mode */
  70        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
  71        case SND_SOC_DAIFMT_I2S:
  72                bf5xx_i2s->tcr1 |= TFSR | TCKFE;
  73                bf5xx_i2s->rcr1 |= RFSR | RCKFE;
  74                bf5xx_i2s->tcr2 |= TSFSE;
  75                bf5xx_i2s->rcr2 |= RSFSE;
  76                break;
  77        case SND_SOC_DAIFMT_DSP_A:
  78                bf5xx_i2s->tcr1 |= TFSR;
  79                bf5xx_i2s->rcr1 |= RFSR;
  80                break;
  81        case SND_SOC_DAIFMT_LEFT_J:
  82                ret = -EINVAL;
  83                break;
  84        default:
  85                dev_err(cpu_dai->dev, "%s: Unknown DAI format type\n",
  86                        __func__);
  87                ret = -EINVAL;
  88                break;
  89        }
  90
  91        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
  92        case SND_SOC_DAIFMT_CBM_CFM:
  93                break;
  94        case SND_SOC_DAIFMT_CBS_CFS:
  95        case SND_SOC_DAIFMT_CBM_CFS:
  96        case SND_SOC_DAIFMT_CBS_CFM:
  97                ret = -EINVAL;
  98                break;
  99        default:
 100                dev_err(cpu_dai->dev, "%s: Unknown DAI master type\n",
 101                        __func__);
 102                ret = -EINVAL;
 103                break;
 104        }
 105
 106        return ret;
 107}
 108
 109static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
 110                                struct snd_pcm_hw_params *params,
 111                                struct snd_soc_dai *dai)
 112{
 113        struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
 114        struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
 115        int ret = 0;
 116
 117        bf5xx_i2s->tcr2 &= ~0x1f;
 118        bf5xx_i2s->rcr2 &= ~0x1f;
 119        switch (params_format(params)) {
 120        case SNDRV_PCM_FORMAT_S8:
 121                bf5xx_i2s->tcr2 |= 7;
 122                bf5xx_i2s->rcr2 |= 7;
 123                sport_handle->wdsize = 1;
 124                break;
 125        case SNDRV_PCM_FORMAT_S16_LE:
 126                bf5xx_i2s->tcr2 |= 15;
 127                bf5xx_i2s->rcr2 |= 15;
 128                sport_handle->wdsize = 2;
 129                break;
 130        case SNDRV_PCM_FORMAT_S24_LE:
 131                bf5xx_i2s->tcr2 |= 23;
 132                bf5xx_i2s->rcr2 |= 23;
 133                sport_handle->wdsize = 3;
 134                break;
 135        case SNDRV_PCM_FORMAT_S32_LE:
 136                bf5xx_i2s->tcr2 |= 31;
 137                bf5xx_i2s->rcr2 |= 31;
 138                sport_handle->wdsize = 4;
 139                break;
 140        }
 141
 142        if (!bf5xx_i2s->configured) {
 143                /*
 144                 * TX and RX are not independent,they are enabled at the
 145                 * same time, even if only one side is running. So, we
 146                 * need to configure both of them at the time when the first
 147                 * stream is opened.
 148                 *
 149                 * CPU DAI:slave mode.
 150                 */
 151                bf5xx_i2s->configured = 1;
 152                ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
 153                                      bf5xx_i2s->rcr2, 0, 0);
 154                if (ret) {
 155                        dev_err(dai->dev, "SPORT is busy!\n");
 156                        return -EBUSY;
 157                }
 158
 159                ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
 160                                      bf5xx_i2s->tcr2, 0, 0);
 161                if (ret) {
 162                        dev_err(dai->dev, "SPORT is busy!\n");
 163                        return -EBUSY;
 164                }
 165        }
 166
 167        return 0;
 168}
 169
 170static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
 171                               struct snd_soc_dai *dai)
 172{
 173        struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
 174        struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
 175
 176        dev_dbg(dai->dev, "%s enter\n", __func__);
 177        /* No active stream, SPORT is allowed to be configured again. */
 178        if (!dai->active)
 179                bf5xx_i2s->configured = 0;
 180}
 181
 182static int bf5xx_i2s_set_channel_map(struct snd_soc_dai *dai,
 183                unsigned int tx_num, unsigned int *tx_slot,
 184                unsigned int rx_num, unsigned int *rx_slot)
 185{
 186        struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
 187        struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
 188        unsigned int tx_mapped = 0, rx_mapped = 0;
 189        unsigned int slot;
 190        int i;
 191
 192        if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) ||
 193                        (rx_num > BFIN_TDM_DAI_MAX_SLOTS))
 194                return -EINVAL;
 195
 196        for (i = 0; i < tx_num; i++) {
 197                slot = tx_slot[i];
 198                if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
 199                                (!(tx_mapped & (1 << slot)))) {
 200                        bf5xx_i2s->tx_dma_data.map[i] = slot;
 201                        tx_mapped |= 1 << slot;
 202                } else
 203                        return -EINVAL;
 204        }
 205        for (i = 0; i < rx_num; i++) {
 206                slot = rx_slot[i];
 207                if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
 208                                (!(rx_mapped & (1 << slot)))) {
 209                        bf5xx_i2s->rx_dma_data.map[i] = slot;
 210                        rx_mapped |= 1 << slot;
 211                } else
 212                        return -EINVAL;
 213        }
 214
 215        return 0;
 216}
 217
 218static int bf5xx_i2s_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
 219        unsigned int rx_mask, int slots, int width)
 220{
 221        struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
 222        struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
 223
 224        if (slots % 8 != 0 || slots > 8)
 225                return -EINVAL;
 226
 227        if (width != 32)
 228                return -EINVAL;
 229
 230        bf5xx_i2s->slots = slots;
 231        bf5xx_i2s->tx_mask = tx_mask;
 232        bf5xx_i2s->rx_mask = rx_mask;
 233
 234        bf5xx_i2s->tx_dma_data.tdm_mode = slots != 0;
 235        bf5xx_i2s->rx_dma_data.tdm_mode = slots != 0;
 236
 237        return sport_set_multichannel(sport_handle, slots, tx_mask, rx_mask, 0);
 238}
 239
 240#ifdef CONFIG_PM
 241static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
 242{
 243        struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
 244
 245        dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id);
 246
 247        if (dai->capture_active)
 248                sport_rx_stop(sport_handle);
 249        if (dai->playback_active)
 250                sport_tx_stop(sport_handle);
 251        return 0;
 252}
 253
 254static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
 255{
 256        struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
 257        struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
 258        int ret;
 259
 260        dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id);
 261
 262        ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
 263                                      bf5xx_i2s->rcr2, 0, 0);
 264        if (ret) {
 265                dev_err(dai->dev, "SPORT is busy!\n");
 266                return -EBUSY;
 267        }
 268
 269        ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
 270                                      bf5xx_i2s->tcr2, 0, 0);
 271        if (ret) {
 272                dev_err(dai->dev, "SPORT is busy!\n");
 273                return -EBUSY;
 274        }
 275
 276        return sport_set_multichannel(sport_handle, bf5xx_i2s->slots,
 277                        bf5xx_i2s->tx_mask, bf5xx_i2s->rx_mask, 0);
 278}
 279
 280#else
 281#define bf5xx_i2s_suspend       NULL
 282#define bf5xx_i2s_resume        NULL
 283#endif
 284
 285static int bf5xx_i2s_dai_probe(struct snd_soc_dai *dai)
 286{
 287        struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
 288        struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
 289        unsigned int i;
 290
 291        for (i = 0; i < BFIN_TDM_DAI_MAX_SLOTS; i++) {
 292                bf5xx_i2s->tx_dma_data.map[i] = i;
 293                bf5xx_i2s->rx_dma_data.map[i] = i;
 294        }
 295
 296        dai->playback_dma_data = &bf5xx_i2s->tx_dma_data;
 297        dai->capture_dma_data = &bf5xx_i2s->rx_dma_data;
 298
 299        return 0;
 300}
 301
 302#define BF5XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
 303                SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
 304                SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
 305                SNDRV_PCM_RATE_96000)
 306
 307#define BF5XX_I2S_FORMATS \
 308        (SNDRV_PCM_FMTBIT_S8 | \
 309         SNDRV_PCM_FMTBIT_S16_LE | \
 310         SNDRV_PCM_FMTBIT_S24_LE | \
 311         SNDRV_PCM_FMTBIT_S32_LE)
 312
 313static const struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
 314        .shutdown        = bf5xx_i2s_shutdown,
 315        .hw_params       = bf5xx_i2s_hw_params,
 316        .set_fmt         = bf5xx_i2s_set_dai_fmt,
 317        .set_tdm_slot    = bf5xx_i2s_set_tdm_slot,
 318        .set_channel_map = bf5xx_i2s_set_channel_map,
 319};
 320
 321static struct snd_soc_dai_driver bf5xx_i2s_dai = {
 322        .probe = bf5xx_i2s_dai_probe,
 323        .suspend = bf5xx_i2s_suspend,
 324        .resume = bf5xx_i2s_resume,
 325        .playback = {
 326                .channels_min = 2,
 327                .channels_max = 8,
 328                .rates = BF5XX_I2S_RATES,
 329                .formats = BF5XX_I2S_FORMATS,},
 330        .capture = {
 331                .channels_min = 2,
 332                .channels_max = 8,
 333                .rates = BF5XX_I2S_RATES,
 334                .formats = BF5XX_I2S_FORMATS,},
 335        .ops = &bf5xx_i2s_dai_ops,
 336};
 337
 338static const struct snd_soc_component_driver bf5xx_i2s_component = {
 339        .name           = "bf5xx-i2s",
 340};
 341
 342static int bf5xx_i2s_probe(struct platform_device *pdev)
 343{
 344        struct sport_device *sport_handle;
 345        int ret;
 346
 347        /* configure SPORT for I2S */
 348        sport_handle = sport_init(pdev, 4, 8 * sizeof(u32),
 349                sizeof(struct bf5xx_i2s_port));
 350        if (!sport_handle)
 351                return -ENODEV;
 352
 353        /* register with the ASoC layers */
 354        ret = snd_soc_register_component(&pdev->dev, &bf5xx_i2s_component,
 355                                         &bf5xx_i2s_dai, 1);
 356        if (ret) {
 357                dev_err(&pdev->dev, "Failed to register DAI: %d\n", ret);
 358                sport_done(sport_handle);
 359                return ret;
 360        }
 361
 362        return 0;
 363}
 364
 365static int bf5xx_i2s_remove(struct platform_device *pdev)
 366{
 367        struct sport_device *sport_handle = platform_get_drvdata(pdev);
 368
 369        dev_dbg(&pdev->dev, "%s enter\n", __func__);
 370
 371        snd_soc_unregister_component(&pdev->dev);
 372        sport_done(sport_handle);
 373
 374        return 0;
 375}
 376
 377static struct platform_driver bfin_i2s_driver = {
 378        .probe  = bf5xx_i2s_probe,
 379        .remove = bf5xx_i2s_remove,
 380        .driver = {
 381                .name = "bfin-i2s",
 382        },
 383};
 384
 385module_platform_driver(bfin_i2s_driver);
 386
 387/* Module information */
 388MODULE_AUTHOR("Cliff Cai");
 389MODULE_DESCRIPTION("I2S driver for ADI Blackfin");
 390MODULE_LICENSE("GPL");
 391
 392