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        case SNDRV_PCM_FORMAT_S16_LE:
 125                bf5xx_i2s->tcr2 |= 15;
 126                bf5xx_i2s->rcr2 |= 15;
 127                sport_handle->wdsize = 2;
 128                break;
 129        case SNDRV_PCM_FORMAT_S24_LE:
 130                bf5xx_i2s->tcr2 |= 23;
 131                bf5xx_i2s->rcr2 |= 23;
 132                sport_handle->wdsize = 3;
 133                break;
 134        case SNDRV_PCM_FORMAT_S32_LE:
 135                bf5xx_i2s->tcr2 |= 31;
 136                bf5xx_i2s->rcr2 |= 31;
 137                sport_handle->wdsize = 4;
 138                break;
 139        }
 140
 141        if (!bf5xx_i2s->configured) {
 142                /*
 143                 * TX and RX are not independent,they are enabled at the
 144                 * same time, even if only one side is running. So, we
 145                 * need to configure both of them at the time when the first
 146                 * stream is opened.
 147                 *
 148                 * CPU DAI:slave mode.
 149                 */
 150                bf5xx_i2s->configured = 1;
 151                ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
 152                                      bf5xx_i2s->rcr2, 0, 0);
 153                if (ret) {
 154                        dev_err(dai->dev, "SPORT is busy!\n");
 155                        return -EBUSY;
 156                }
 157
 158                ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
 159                                      bf5xx_i2s->tcr2, 0, 0);
 160                if (ret) {
 161                        dev_err(dai->dev, "SPORT is busy!\n");
 162                        return -EBUSY;
 163                }
 164        }
 165
 166        return 0;
 167}
 168
 169static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
 170                               struct snd_soc_dai *dai)
 171{
 172        struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
 173        struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
 174
 175        dev_dbg(dai->dev, "%s enter\n", __func__);
 176        /* No active stream, SPORT is allowed to be configured again. */
 177        if (!dai->active)
 178                bf5xx_i2s->configured = 0;
 179}
 180
 181static int bf5xx_i2s_set_channel_map(struct snd_soc_dai *dai,
 182                unsigned int tx_num, unsigned int *tx_slot,
 183                unsigned int rx_num, unsigned int *rx_slot)
 184{
 185        struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
 186        struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
 187        unsigned int tx_mapped = 0, rx_mapped = 0;
 188        unsigned int slot;
 189        int i;
 190
 191        if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) ||
 192                        (rx_num > BFIN_TDM_DAI_MAX_SLOTS))
 193                return -EINVAL;
 194
 195        for (i = 0; i < tx_num; i++) {
 196                slot = tx_slot[i];
 197                if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
 198                                (!(tx_mapped & (1 << slot)))) {
 199                        bf5xx_i2s->tx_dma_data.map[i] = slot;
 200                        tx_mapped |= 1 << slot;
 201                } else
 202                        return -EINVAL;
 203        }
 204        for (i = 0; i < rx_num; i++) {
 205                slot = rx_slot[i];
 206                if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
 207                                (!(rx_mapped & (1 << slot)))) {
 208                        bf5xx_i2s->rx_dma_data.map[i] = slot;
 209                        rx_mapped |= 1 << slot;
 210                } else
 211                        return -EINVAL;
 212        }
 213
 214        return 0;
 215}
 216
 217static int bf5xx_i2s_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
 218        unsigned int rx_mask, int slots, int width)
 219{
 220        struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
 221        struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
 222
 223        if (slots % 8 != 0 || slots > 8)
 224                return -EINVAL;
 225
 226        if (width != 32)
 227                return -EINVAL;
 228
 229        bf5xx_i2s->slots = slots;
 230        bf5xx_i2s->tx_mask = tx_mask;
 231        bf5xx_i2s->rx_mask = rx_mask;
 232
 233        bf5xx_i2s->tx_dma_data.tdm_mode = slots != 0;
 234        bf5xx_i2s->rx_dma_data.tdm_mode = slots != 0;
 235
 236        return sport_set_multichannel(sport_handle, slots, tx_mask, rx_mask, 0);
 237}
 238
 239#ifdef CONFIG_PM
 240static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
 241{
 242        struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
 243
 244        dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id);
 245
 246        if (dai->capture_active)
 247                sport_rx_stop(sport_handle);
 248        if (dai->playback_active)
 249                sport_tx_stop(sport_handle);
 250        return 0;
 251}
 252
 253static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
 254{
 255        struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
 256        struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
 257        int ret;
 258
 259        dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id);
 260
 261        ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
 262                                      bf5xx_i2s->rcr2, 0, 0);
 263        if (ret) {
 264                dev_err(dai->dev, "SPORT is busy!\n");
 265                return -EBUSY;
 266        }
 267
 268        ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
 269                                      bf5xx_i2s->tcr2, 0, 0);
 270        if (ret) {
 271                dev_err(dai->dev, "SPORT is busy!\n");
 272                return -EBUSY;
 273        }
 274
 275        return sport_set_multichannel(sport_handle, bf5xx_i2s->slots,
 276                        bf5xx_i2s->tx_mask, bf5xx_i2s->rx_mask, 0);
 277}
 278
 279#else
 280#define bf5xx_i2s_suspend       NULL
 281#define bf5xx_i2s_resume        NULL
 282#endif
 283
 284static int bf5xx_i2s_dai_probe(struct snd_soc_dai *dai)
 285{
 286        struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
 287        struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
 288        unsigned int i;
 289
 290        for (i = 0; i < BFIN_TDM_DAI_MAX_SLOTS; i++) {
 291                bf5xx_i2s->tx_dma_data.map[i] = i;
 292                bf5xx_i2s->rx_dma_data.map[i] = i;
 293        }
 294
 295        dai->playback_dma_data = &bf5xx_i2s->tx_dma_data;
 296        dai->capture_dma_data = &bf5xx_i2s->rx_dma_data;
 297
 298        return 0;
 299}
 300
 301#define BF5XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
 302                SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
 303                SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
 304                SNDRV_PCM_RATE_96000)
 305
 306#define BF5XX_I2S_FORMATS \
 307        (SNDRV_PCM_FMTBIT_S8 | \
 308         SNDRV_PCM_FMTBIT_S16_LE | \
 309         SNDRV_PCM_FMTBIT_S24_LE | \
 310         SNDRV_PCM_FMTBIT_S32_LE)
 311
 312static const struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
 313        .shutdown        = bf5xx_i2s_shutdown,
 314        .hw_params       = bf5xx_i2s_hw_params,
 315        .set_fmt         = bf5xx_i2s_set_dai_fmt,
 316        .set_tdm_slot    = bf5xx_i2s_set_tdm_slot,
 317        .set_channel_map = bf5xx_i2s_set_channel_map,
 318};
 319
 320static struct snd_soc_dai_driver bf5xx_i2s_dai = {
 321        .probe = bf5xx_i2s_dai_probe,
 322        .suspend = bf5xx_i2s_suspend,
 323        .resume = bf5xx_i2s_resume,
 324        .playback = {
 325                .channels_min = 2,
 326                .channels_max = 8,
 327                .rates = BF5XX_I2S_RATES,
 328                .formats = BF5XX_I2S_FORMATS,},
 329        .capture = {
 330                .channels_min = 2,
 331                .channels_max = 8,
 332                .rates = BF5XX_I2S_RATES,
 333                .formats = BF5XX_I2S_FORMATS,},
 334        .ops = &bf5xx_i2s_dai_ops,
 335};
 336
 337static const struct snd_soc_component_driver bf5xx_i2s_component = {
 338        .name           = "bf5xx-i2s",
 339};
 340
 341static int bf5xx_i2s_probe(struct platform_device *pdev)
 342{
 343        struct sport_device *sport_handle;
 344        int ret;
 345
 346        /* configure SPORT for I2S */
 347        sport_handle = sport_init(pdev, 4, 8 * sizeof(u32),
 348                sizeof(struct bf5xx_i2s_port));
 349        if (!sport_handle)
 350                return -ENODEV;
 351
 352        /* register with the ASoC layers */
 353        ret = snd_soc_register_component(&pdev->dev, &bf5xx_i2s_component,
 354                                         &bf5xx_i2s_dai, 1);
 355        if (ret) {
 356                dev_err(&pdev->dev, "Failed to register DAI: %d\n", ret);
 357                sport_done(sport_handle);
 358                return ret;
 359        }
 360
 361        return 0;
 362}
 363
 364static int bf5xx_i2s_remove(struct platform_device *pdev)
 365{
 366        struct sport_device *sport_handle = platform_get_drvdata(pdev);
 367
 368        dev_dbg(&pdev->dev, "%s enter\n", __func__);
 369
 370        snd_soc_unregister_component(&pdev->dev);
 371        sport_done(sport_handle);
 372
 373        return 0;
 374}
 375
 376static struct platform_driver bfin_i2s_driver = {
 377        .probe  = bf5xx_i2s_probe,
 378        .remove = bf5xx_i2s_remove,
 379        .driver = {
 380                .name = "bfin-i2s",
 381                .owner = THIS_MODULE,
 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