linux/sound/soc/omap/omap-mcbsp.c
<<
>>
Prefs
   1/*
   2 * omap-mcbsp.c  --  OMAP ALSA SoC DAI driver using McBSP port
   3 *
   4 * Copyright (C) 2008 Nokia Corporation
   5 *
   6 * Contact: Jarkko Nikula <jhnikula@gmail.com>
   7 *          Peter Ujfalusi <peter.ujfalusi@nokia.com>
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License
  11 * version 2 as published by the Free Software Foundation.
  12 *
  13 * This program is distributed in the hope that it will be useful, but
  14 * WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16 * General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  21 * 02110-1301 USA
  22 *
  23 */
  24
  25#include <linux/init.h>
  26#include <linux/module.h>
  27#include <linux/device.h>
  28#include <sound/core.h>
  29#include <sound/pcm.h>
  30#include <sound/pcm_params.h>
  31#include <sound/initval.h>
  32#include <sound/soc.h>
  33
  34#include <mach/control.h>
  35#include <mach/dma.h>
  36#include <mach/mcbsp.h>
  37#include "omap-mcbsp.h"
  38#include "omap-pcm.h"
  39
  40#define OMAP_MCBSP_RATES        (SNDRV_PCM_RATE_8000_96000)
  41
  42struct omap_mcbsp_data {
  43        unsigned int                    bus_id;
  44        struct omap_mcbsp_reg_cfg       regs;
  45        unsigned int                    fmt;
  46        /*
  47         * Flags indicating is the bus already activated and configured by
  48         * another substream
  49         */
  50        int                             active;
  51        int                             configured;
  52};
  53
  54#define to_mcbsp(priv)  container_of((priv), struct omap_mcbsp_data, bus_id)
  55
  56static struct omap_mcbsp_data mcbsp_data[NUM_LINKS];
  57
  58/*
  59 * Stream DMA parameters. DMA request line and port address are set runtime
  60 * since they are different between OMAP1 and later OMAPs
  61 */
  62static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2];
  63
  64#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX)
  65static const int omap1_dma_reqs[][2] = {
  66        { OMAP_DMA_MCBSP1_TX, OMAP_DMA_MCBSP1_RX },
  67        { OMAP_DMA_MCBSP2_TX, OMAP_DMA_MCBSP2_RX },
  68        { OMAP_DMA_MCBSP3_TX, OMAP_DMA_MCBSP3_RX },
  69};
  70static const unsigned long omap1_mcbsp_port[][2] = {
  71        { OMAP1510_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1,
  72          OMAP1510_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1 },
  73        { OMAP1510_MCBSP2_BASE + OMAP_MCBSP_REG_DXR1,
  74          OMAP1510_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1 },
  75        { OMAP1510_MCBSP3_BASE + OMAP_MCBSP_REG_DXR1,
  76          OMAP1510_MCBSP3_BASE + OMAP_MCBSP_REG_DRR1 },
  77};
  78#else
  79static const int omap1_dma_reqs[][2] = {};
  80static const unsigned long omap1_mcbsp_port[][2] = {};
  81#endif
  82
  83#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
  84static const int omap24xx_dma_reqs[][2] = {
  85        { OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX },
  86        { OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX },
  87#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX)
  88        { OMAP24XX_DMA_MCBSP3_TX, OMAP24XX_DMA_MCBSP3_RX },
  89        { OMAP24XX_DMA_MCBSP4_TX, OMAP24XX_DMA_MCBSP4_RX },
  90        { OMAP24XX_DMA_MCBSP5_TX, OMAP24XX_DMA_MCBSP5_RX },
  91#endif
  92};
  93#else
  94static const int omap24xx_dma_reqs[][2] = {};
  95#endif
  96
  97#if defined(CONFIG_ARCH_OMAP2420)
  98static const unsigned long omap2420_mcbsp_port[][2] = {
  99        { OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1,
 100          OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1 },
 101        { OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR1,
 102          OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1 },
 103};
 104#else
 105static const unsigned long omap2420_mcbsp_port[][2] = {};
 106#endif
 107
 108#if defined(CONFIG_ARCH_OMAP2430)
 109static const unsigned long omap2430_mcbsp_port[][2] = {
 110        { OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR,
 111          OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR },
 112        { OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR,
 113          OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR },
 114        { OMAP2430_MCBSP3_BASE + OMAP_MCBSP_REG_DXR,
 115          OMAP2430_MCBSP3_BASE + OMAP_MCBSP_REG_DRR },
 116        { OMAP2430_MCBSP4_BASE + OMAP_MCBSP_REG_DXR,
 117          OMAP2430_MCBSP4_BASE + OMAP_MCBSP_REG_DRR },
 118        { OMAP2430_MCBSP5_BASE + OMAP_MCBSP_REG_DXR,
 119          OMAP2430_MCBSP5_BASE + OMAP_MCBSP_REG_DRR },
 120};
 121#else
 122static const unsigned long omap2430_mcbsp_port[][2] = {};
 123#endif
 124
 125#if defined(CONFIG_ARCH_OMAP34XX)
 126static const unsigned long omap34xx_mcbsp_port[][2] = {
 127        { OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR,
 128          OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR },
 129        { OMAP34XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR,
 130          OMAP34XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR },
 131        { OMAP34XX_MCBSP3_BASE + OMAP_MCBSP_REG_DXR,
 132          OMAP34XX_MCBSP3_BASE + OMAP_MCBSP_REG_DRR },
 133        { OMAP34XX_MCBSP4_BASE + OMAP_MCBSP_REG_DXR,
 134          OMAP34XX_MCBSP4_BASE + OMAP_MCBSP_REG_DRR },
 135        { OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DXR,
 136          OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DRR },
 137};
 138#else
 139static const unsigned long omap34xx_mcbsp_port[][2] = {};
 140#endif
 141
 142static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream)
 143{
 144        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 145        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 146        struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 147        int dma_op_mode = omap_mcbsp_get_dma_op_mode(mcbsp_data->bus_id);
 148        int samples;
 149
 150        /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
 151        if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD)
 152                samples = snd_pcm_lib_period_bytes(substream) >> 1;
 153        else
 154                samples = 1;
 155
 156        /* Configure McBSP internal buffer usage */
 157        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 158                omap_mcbsp_set_tx_threshold(mcbsp_data->bus_id, samples - 1);
 159        else
 160                omap_mcbsp_set_rx_threshold(mcbsp_data->bus_id, samples - 1);
 161}
 162
 163static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
 164                                  struct snd_soc_dai *dai)
 165{
 166        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 167        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 168        struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 169        int bus_id = mcbsp_data->bus_id;
 170        int err = 0;
 171
 172        if (!cpu_dai->active)
 173                err = omap_mcbsp_request(bus_id);
 174
 175        if (cpu_is_omap343x()) {
 176                int dma_op_mode = omap_mcbsp_get_dma_op_mode(bus_id);
 177                int max_period;
 178
 179                /*
 180                 * McBSP2 in OMAP3 has 1024 * 32-bit internal audio buffer.
 181                 * Set constraint for minimum buffer size to the same than FIFO
 182                 * size in order to avoid underruns in playback startup because
 183                 * HW is keeping the DMA request active until FIFO is filled.
 184                 */
 185                if (bus_id == 1)
 186                        snd_pcm_hw_constraint_minmax(substream->runtime,
 187                                        SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
 188                                        4096, UINT_MAX);
 189
 190                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 191                        max_period = omap_mcbsp_get_max_tx_threshold(bus_id);
 192                else
 193                        max_period = omap_mcbsp_get_max_rx_threshold(bus_id);
 194
 195                max_period++;
 196                max_period <<= 1;
 197
 198                if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD)
 199                        snd_pcm_hw_constraint_minmax(substream->runtime,
 200                                                SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
 201                                                32, max_period);
 202        }
 203
 204        return err;
 205}
 206
 207static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream,
 208                                    struct snd_soc_dai *dai)
 209{
 210        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 211        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 212        struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 213
 214        if (!cpu_dai->active) {
 215                omap_mcbsp_free(mcbsp_data->bus_id);
 216                mcbsp_data->configured = 0;
 217        }
 218}
 219
 220static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd,
 221                                  struct snd_soc_dai *dai)
 222{
 223        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 224        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 225        struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 226        int err = 0, play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
 227
 228        switch (cmd) {
 229        case SNDRV_PCM_TRIGGER_START:
 230        case SNDRV_PCM_TRIGGER_RESUME:
 231        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 232                mcbsp_data->active++;
 233                omap_mcbsp_start(mcbsp_data->bus_id, play, !play);
 234                break;
 235
 236        case SNDRV_PCM_TRIGGER_STOP:
 237        case SNDRV_PCM_TRIGGER_SUSPEND:
 238        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 239                omap_mcbsp_stop(mcbsp_data->bus_id, play, !play);
 240                mcbsp_data->active--;
 241                break;
 242        default:
 243                err = -EINVAL;
 244        }
 245
 246        return err;
 247}
 248
 249static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
 250                                    struct snd_pcm_hw_params *params,
 251                                    struct snd_soc_dai *dai)
 252{
 253        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 254        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 255        struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 256        struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
 257        int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
 258        int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT;
 259        unsigned long port;
 260        unsigned int format;
 261
 262        if (cpu_class_is_omap1()) {
 263                dma = omap1_dma_reqs[bus_id][substream->stream];
 264                port = omap1_mcbsp_port[bus_id][substream->stream];
 265        } else if (cpu_is_omap2420()) {
 266                dma = omap24xx_dma_reqs[bus_id][substream->stream];
 267                port = omap2420_mcbsp_port[bus_id][substream->stream];
 268        } else if (cpu_is_omap2430()) {
 269                dma = omap24xx_dma_reqs[bus_id][substream->stream];
 270                port = omap2430_mcbsp_port[bus_id][substream->stream];
 271        } else if (cpu_is_omap343x()) {
 272                dma = omap24xx_dma_reqs[bus_id][substream->stream];
 273                port = omap34xx_mcbsp_port[bus_id][substream->stream];
 274                omap_mcbsp_dai_dma_params[id][substream->stream].set_threshold =
 275                                                omap_mcbsp_set_threshold;
 276                /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
 277                if (omap_mcbsp_get_dma_op_mode(bus_id) ==
 278                                                MCBSP_DMA_MODE_THRESHOLD)
 279                        sync_mode = OMAP_DMA_SYNC_FRAME;
 280        } else {
 281                return -ENODEV;
 282        }
 283        omap_mcbsp_dai_dma_params[id][substream->stream].name =
 284                substream->stream ? "Audio Capture" : "Audio Playback";
 285        omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma;
 286        omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port;
 287        omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode;
 288        cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream];
 289
 290        if (mcbsp_data->configured) {
 291                /* McBSP already configured by another stream */
 292                return 0;
 293        }
 294
 295        format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
 296        wpf = channels = params_channels(params);
 297        switch (channels) {
 298        case 2:
 299                if (format == SND_SOC_DAIFMT_I2S) {
 300                        /* Use dual-phase frames */
 301                        regs->rcr2      |= RPHASE;
 302                        regs->xcr2      |= XPHASE;
 303                        /* Set 1 word per (McBSP) frame for phase1 and phase2 */
 304                        wpf--;
 305                        regs->rcr2      |= RFRLEN2(wpf - 1);
 306                        regs->xcr2      |= XFRLEN2(wpf - 1);
 307                }
 308        case 1:
 309        case 4:
 310                /* Set word per (McBSP) frame for phase1 */
 311                regs->rcr1      |= RFRLEN1(wpf - 1);
 312                regs->xcr1      |= XFRLEN1(wpf - 1);
 313                break;
 314        default:
 315                /* Unsupported number of channels */
 316                return -EINVAL;
 317        }
 318
 319        switch (params_format(params)) {
 320        case SNDRV_PCM_FORMAT_S16_LE:
 321                /* Set word lengths */
 322                wlen = 16;
 323                regs->rcr2      |= RWDLEN2(OMAP_MCBSP_WORD_16);
 324                regs->rcr1      |= RWDLEN1(OMAP_MCBSP_WORD_16);
 325                regs->xcr2      |= XWDLEN2(OMAP_MCBSP_WORD_16);
 326                regs->xcr1      |= XWDLEN1(OMAP_MCBSP_WORD_16);
 327                break;
 328        default:
 329                /* Unsupported PCM format */
 330                return -EINVAL;
 331        }
 332
 333        /* Set FS period and length in terms of bit clock periods */
 334        switch (format) {
 335        case SND_SOC_DAIFMT_I2S:
 336                regs->srgr2     |= FPER(wlen * channels - 1);
 337                regs->srgr1     |= FWID(wlen - 1);
 338                break;
 339        case SND_SOC_DAIFMT_DSP_A:
 340        case SND_SOC_DAIFMT_DSP_B:
 341                regs->srgr2     |= FPER(wlen * channels - 1);
 342                regs->srgr1     |= FWID(0);
 343                break;
 344        }
 345
 346        omap_mcbsp_config(bus_id, &mcbsp_data->regs);
 347        mcbsp_data->configured = 1;
 348
 349        return 0;
 350}
 351
 352/*
 353 * This must be called before _set_clkdiv and _set_sysclk since McBSP register
 354 * cache is initialized here
 355 */
 356static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 357                                      unsigned int fmt)
 358{
 359        struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 360        struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
 361        unsigned int temp_fmt = fmt;
 362
 363        if (mcbsp_data->configured)
 364                return 0;
 365
 366        mcbsp_data->fmt = fmt;
 367        memset(regs, 0, sizeof(*regs));
 368        /* Generic McBSP register settings */
 369        regs->spcr2     |= XINTM(3) | FREE;
 370        regs->spcr1     |= RINTM(3);
 371        /* RFIG and XFIG are not defined in 34xx */
 372        if (!cpu_is_omap34xx()) {
 373                regs->rcr2      |= RFIG;
 374                regs->xcr2      |= XFIG;
 375        }
 376        if (cpu_is_omap2430() || cpu_is_omap34xx()) {
 377                regs->xccr = DXENDLY(1) | XDMAEN | XDISABLE;
 378                regs->rccr = RFULL_CYCLE | RDMAEN | RDISABLE;
 379        }
 380
 381        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 382        case SND_SOC_DAIFMT_I2S:
 383                /* 1-bit data delay */
 384                regs->rcr2      |= RDATDLY(1);
 385                regs->xcr2      |= XDATDLY(1);
 386                break;
 387        case SND_SOC_DAIFMT_DSP_A:
 388                /* 1-bit data delay */
 389                regs->rcr2      |= RDATDLY(1);
 390                regs->xcr2      |= XDATDLY(1);
 391                /* Invert FS polarity configuration */
 392                temp_fmt ^= SND_SOC_DAIFMT_NB_IF;
 393                break;
 394        case SND_SOC_DAIFMT_DSP_B:
 395                /* 0-bit data delay */
 396                regs->rcr2      |= RDATDLY(0);
 397                regs->xcr2      |= XDATDLY(0);
 398                /* Invert FS polarity configuration */
 399                temp_fmt ^= SND_SOC_DAIFMT_NB_IF;
 400                break;
 401        default:
 402                /* Unsupported data format */
 403                return -EINVAL;
 404        }
 405
 406        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 407        case SND_SOC_DAIFMT_CBS_CFS:
 408                /* McBSP master. Set FS and bit clocks as outputs */
 409                regs->pcr0      |= FSXM | FSRM |
 410                                   CLKXM | CLKRM;
 411                /* Sample rate generator drives the FS */
 412                regs->srgr2     |= FSGM;
 413                break;
 414        case SND_SOC_DAIFMT_CBM_CFM:
 415                /* McBSP slave */
 416                break;
 417        default:
 418                /* Unsupported master/slave configuration */
 419                return -EINVAL;
 420        }
 421
 422        /* Set bit clock (CLKX/CLKR) and FS polarities */
 423        switch (temp_fmt & SND_SOC_DAIFMT_INV_MASK) {
 424        case SND_SOC_DAIFMT_NB_NF:
 425                /*
 426                 * Normal BCLK + FS.
 427                 * FS active low. TX data driven on falling edge of bit clock
 428                 * and RX data sampled on rising edge of bit clock.
 429                 */
 430                regs->pcr0      |= FSXP | FSRP |
 431                                   CLKXP | CLKRP;
 432                break;
 433        case SND_SOC_DAIFMT_NB_IF:
 434                regs->pcr0      |= CLKXP | CLKRP;
 435                break;
 436        case SND_SOC_DAIFMT_IB_NF:
 437                regs->pcr0      |= FSXP | FSRP;
 438                break;
 439        case SND_SOC_DAIFMT_IB_IF:
 440                break;
 441        default:
 442                return -EINVAL;
 443        }
 444
 445        return 0;
 446}
 447
 448static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
 449                                     int div_id, int div)
 450{
 451        struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 452        struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
 453
 454        if (div_id != OMAP_MCBSP_CLKGDV)
 455                return -ENODEV;
 456
 457        regs->srgr1     |= CLKGDV(div - 1);
 458
 459        return 0;
 460}
 461
 462static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data,
 463                                       int clk_id)
 464{
 465        int sel_bit;
 466        u16 reg, reg_devconf1 = OMAP243X_CONTROL_DEVCONF1;
 467
 468        if (cpu_class_is_omap1()) {
 469                /* OMAP1's can use only external source clock */
 470                if (unlikely(clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK))
 471                        return -EINVAL;
 472                else
 473                        return 0;
 474        }
 475
 476        if (cpu_is_omap2420() && mcbsp_data->bus_id > 1)
 477                return -EINVAL;
 478
 479        if (cpu_is_omap343x())
 480                reg_devconf1 = OMAP343X_CONTROL_DEVCONF1;
 481
 482        switch (mcbsp_data->bus_id) {
 483        case 0:
 484                reg = OMAP2_CONTROL_DEVCONF0;
 485                sel_bit = 2;
 486                break;
 487        case 1:
 488                reg = OMAP2_CONTROL_DEVCONF0;
 489                sel_bit = 6;
 490                break;
 491        case 2:
 492                reg = reg_devconf1;
 493                sel_bit = 0;
 494                break;
 495        case 3:
 496                reg = reg_devconf1;
 497                sel_bit = 2;
 498                break;
 499        case 4:
 500                reg = reg_devconf1;
 501                sel_bit = 4;
 502                break;
 503        default:
 504                return -EINVAL;
 505        }
 506
 507        if (clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK)
 508                omap_ctrl_writel(omap_ctrl_readl(reg) & ~(1 << sel_bit), reg);
 509        else
 510                omap_ctrl_writel(omap_ctrl_readl(reg) | (1 << sel_bit), reg);
 511
 512        return 0;
 513}
 514
 515static int omap_mcbsp_dai_set_rcvr_src(struct omap_mcbsp_data *mcbsp_data,
 516                                       int clk_id)
 517{
 518        int sel_bit, set = 0;
 519        u16 reg = OMAP2_CONTROL_DEVCONF0;
 520
 521        if (cpu_class_is_omap1())
 522                return -EINVAL; /* TODO: Can this be implemented for OMAP1? */
 523        if (mcbsp_data->bus_id != 0)
 524                return -EINVAL;
 525
 526        switch (clk_id) {
 527        case OMAP_MCBSP_CLKR_SRC_CLKX:
 528                set = 1;
 529        case OMAP_MCBSP_CLKR_SRC_CLKR:
 530                sel_bit = 3;
 531                break;
 532        case OMAP_MCBSP_FSR_SRC_FSX:
 533                set = 1;
 534        case OMAP_MCBSP_FSR_SRC_FSR:
 535                sel_bit = 4;
 536                break;
 537        default:
 538                return -EINVAL;
 539        }
 540
 541        if (set)
 542                omap_ctrl_writel(omap_ctrl_readl(reg) | (1 << sel_bit), reg);
 543        else
 544                omap_ctrl_writel(omap_ctrl_readl(reg) & ~(1 << sel_bit), reg);
 545
 546        return 0;
 547}
 548
 549static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
 550                                         int clk_id, unsigned int freq,
 551                                         int dir)
 552{
 553        struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 554        struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
 555        int err = 0;
 556
 557        switch (clk_id) {
 558        case OMAP_MCBSP_SYSCLK_CLK:
 559                regs->srgr2     |= CLKSM;
 560                break;
 561        case OMAP_MCBSP_SYSCLK_CLKS_FCLK:
 562        case OMAP_MCBSP_SYSCLK_CLKS_EXT:
 563                err = omap_mcbsp_dai_set_clks_src(mcbsp_data, clk_id);
 564                break;
 565
 566        case OMAP_MCBSP_SYSCLK_CLKX_EXT:
 567                regs->srgr2     |= CLKSM;
 568        case OMAP_MCBSP_SYSCLK_CLKR_EXT:
 569                regs->pcr0      |= SCLKME;
 570                break;
 571
 572        case OMAP_MCBSP_CLKR_SRC_CLKR:
 573        case OMAP_MCBSP_CLKR_SRC_CLKX:
 574        case OMAP_MCBSP_FSR_SRC_FSR:
 575        case OMAP_MCBSP_FSR_SRC_FSX:
 576                err = omap_mcbsp_dai_set_rcvr_src(mcbsp_data, clk_id);
 577                break;
 578        default:
 579                err = -ENODEV;
 580        }
 581
 582        return err;
 583}
 584
 585static struct snd_soc_dai_ops omap_mcbsp_dai_ops = {
 586        .startup        = omap_mcbsp_dai_startup,
 587        .shutdown       = omap_mcbsp_dai_shutdown,
 588        .trigger        = omap_mcbsp_dai_trigger,
 589        .hw_params      = omap_mcbsp_dai_hw_params,
 590        .set_fmt        = omap_mcbsp_dai_set_dai_fmt,
 591        .set_clkdiv     = omap_mcbsp_dai_set_clkdiv,
 592        .set_sysclk     = omap_mcbsp_dai_set_dai_sysclk,
 593};
 594
 595#define OMAP_MCBSP_DAI_BUILDER(link_id)                         \
 596{                                                               \
 597        .name = "omap-mcbsp-dai-"#link_id,                      \
 598        .id = (link_id),                                        \
 599        .playback = {                                           \
 600                .channels_min = 1,                              \
 601                .channels_max = 4,                              \
 602                .rates = OMAP_MCBSP_RATES,                      \
 603                .formats = SNDRV_PCM_FMTBIT_S16_LE,             \
 604        },                                                      \
 605        .capture = {                                            \
 606                .channels_min = 1,                              \
 607                .channels_max = 4,                              \
 608                .rates = OMAP_MCBSP_RATES,                      \
 609                .formats = SNDRV_PCM_FMTBIT_S16_LE,             \
 610        },                                                      \
 611        .ops = &omap_mcbsp_dai_ops,                             \
 612        .private_data = &mcbsp_data[(link_id)].bus_id,          \
 613}
 614
 615struct snd_soc_dai omap_mcbsp_dai[] = {
 616        OMAP_MCBSP_DAI_BUILDER(0),
 617        OMAP_MCBSP_DAI_BUILDER(1),
 618#if NUM_LINKS >= 3
 619        OMAP_MCBSP_DAI_BUILDER(2),
 620#endif
 621#if NUM_LINKS == 5
 622        OMAP_MCBSP_DAI_BUILDER(3),
 623        OMAP_MCBSP_DAI_BUILDER(4),
 624#endif
 625};
 626
 627EXPORT_SYMBOL_GPL(omap_mcbsp_dai);
 628
 629static int __init snd_omap_mcbsp_init(void)
 630{
 631        return snd_soc_register_dais(omap_mcbsp_dai,
 632                                     ARRAY_SIZE(omap_mcbsp_dai));
 633}
 634module_init(snd_omap_mcbsp_init);
 635
 636static void __exit snd_omap_mcbsp_exit(void)
 637{
 638        snd_soc_unregister_dais(omap_mcbsp_dai, ARRAY_SIZE(omap_mcbsp_dai));
 639}
 640module_exit(snd_omap_mcbsp_exit);
 641
 642MODULE_AUTHOR("Jarkko Nikula <jhnikula@gmail.com>");
 643MODULE_DESCRIPTION("OMAP I2S SoC Interface");
 644MODULE_LICENSE("GPL");
 645