linux/sound/soc/imx/mx27vis_wm8974.c
<<
>>
Prefs
   1/*
   2 * mx27vis_wm8974.c  --  SoC audio for mx27vis
   3 *
   4 * Copyright 2009 Vista Silicon S.L.
   5 * Author: Javier Martin
   6 *         javier.martin@vista-silicon.com
   7 *
   8 *  This program is free software; you can redistribute  it and/or modify it
   9 *  under  the terms of  the GNU General  Public License as published by the
  10 *  Free Software Foundation;  either version 2 of the  License, or (at your
  11 *  option) any later version.
  12 *
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/moduleparam.h>
  17#include <linux/device.h>
  18#include <linux/i2c.h>
  19#include <sound/core.h>
  20#include <sound/pcm.h>
  21#include <sound/soc.h>
  22#include <sound/soc-dapm.h>
  23
  24
  25#include "../codecs/wm8974.h"
  26#include "mx1_mx2-pcm.h"
  27#include "mxc-ssi.h"
  28#include <mach/gpio.h>
  29#include <mach/iomux.h>
  30
  31#define IGNORED_ARG 0
  32
  33
  34static struct snd_soc_card mx27vis;
  35
  36/**
  37  * This function connects SSI1 (HPCR1) as slave to
  38  * SSI1 external signals (PPCR1)
  39  * As slave, HPCR1 must set TFSDIR and TCLKDIR as inputs from
  40  * port 4
  41  */
  42void audmux_connect_1_4(void)
  43{
  44        pr_debug("AUDMUX: normal operation mode\n");
  45        /* Reset HPCR1 and PPCR1 */
  46
  47        DAM_HPCR1 = 0x00000000;
  48        DAM_PPCR1 = 0x00000000;
  49
  50        /* set to synchronous */
  51        DAM_HPCR1 |= AUDMUX_HPCR_SYN;
  52        DAM_PPCR1 |= AUDMUX_PPCR_SYN;
  53
  54
  55        /* set Rx sources 1 <--> 4 */
  56        DAM_HPCR1 |= AUDMUX_HPCR_RXDSEL(3); /* port 4 */
  57        DAM_PPCR1 |= AUDMUX_PPCR_RXDSEL(0); /* port 1 */
  58
  59        /* set Tx frame and Clock direction and source  4 --> 1 output */
  60        DAM_HPCR1 |= AUDMUX_HPCR_TFSDIR | AUDMUX_HPCR_TCLKDIR;
  61        DAM_HPCR1 |= AUDMUX_HPCR_TFCSEL(3); /* TxDS and TxCclk from port 4 */
  62
  63        return;
  64}
  65
  66static int mx27vis_hifi_hw_params(struct snd_pcm_substream *substream,
  67        struct snd_pcm_hw_params *params)
  68{
  69        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  70        struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
  71        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
  72        unsigned int pll_out = 0, bclk = 0, fmt = 0, mclk = 0;
  73        int ret = 0;
  74
  75        /*
  76         * The WM8974 is better at generating accurate audio clocks than the
  77         * MX27 SSI controller, so we will use it as master when we can.
  78         */
  79        switch (params_rate(params)) {
  80        case 8000:
  81                fmt = SND_SOC_DAIFMT_CBM_CFM;
  82                mclk = WM8974_MCLKDIV_12;
  83                pll_out = 24576000;
  84                break;
  85        case 16000:
  86                fmt = SND_SOC_DAIFMT_CBM_CFM;
  87                pll_out = 12288000;
  88                break;
  89        case 48000:
  90                fmt = SND_SOC_DAIFMT_CBM_CFM;
  91                bclk = WM8974_BCLKDIV_4;
  92                pll_out = 12288000;
  93                break;
  94        case 96000:
  95                fmt = SND_SOC_DAIFMT_CBM_CFM;
  96                bclk = WM8974_BCLKDIV_2;
  97                pll_out = 12288000;
  98                break;
  99        case 11025:
 100                fmt = SND_SOC_DAIFMT_CBM_CFM;
 101                bclk = WM8974_BCLKDIV_16;
 102                pll_out = 11289600;
 103                break;
 104        case 22050:
 105                fmt = SND_SOC_DAIFMT_CBM_CFM;
 106                bclk = WM8974_BCLKDIV_8;
 107                pll_out = 11289600;
 108                break;
 109        case 44100:
 110                fmt = SND_SOC_DAIFMT_CBM_CFM;
 111                bclk = WM8974_BCLKDIV_4;
 112                mclk = WM8974_MCLKDIV_2;
 113                pll_out = 11289600;
 114                break;
 115        case 88200:
 116                fmt = SND_SOC_DAIFMT_CBM_CFM;
 117                bclk = WM8974_BCLKDIV_2;
 118                pll_out = 11289600;
 119                break;
 120        }
 121
 122        /* set codec DAI configuration */
 123        ret = codec_dai->ops->set_fmt(codec_dai,
 124                SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
 125                SND_SOC_DAIFMT_SYNC | fmt);
 126        if (ret < 0) {
 127                printk(KERN_ERR "Error from codec DAI configuration\n");
 128                return ret;
 129        }
 130
 131        /* set cpu DAI configuration */
 132        ret = cpu_dai->ops->set_fmt(cpu_dai,
 133                SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 134                SND_SOC_DAIFMT_SYNC | fmt);
 135        if (ret < 0) {
 136                printk(KERN_ERR "Error from cpu DAI configuration\n");
 137                return ret;
 138        }
 139
 140        /* Put DC field of STCCR to 1 (not zero) */
 141        ret = cpu_dai->ops->set_tdm_slot(cpu_dai, 0, 2);
 142
 143        /* set the SSI system clock as input */
 144        ret = cpu_dai->ops->set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
 145                SND_SOC_CLOCK_IN);
 146        if (ret < 0) {
 147                printk(KERN_ERR "Error when setting system SSI clk\n");
 148                return ret;
 149        }
 150
 151        /* set codec BCLK division for sample rate */
 152        ret = codec_dai->ops->set_clkdiv(codec_dai, WM8974_BCLKDIV, bclk);
 153        if (ret < 0) {
 154                printk(KERN_ERR "Error when setting BCLK division\n");
 155                return ret;
 156        }
 157
 158
 159        /* codec PLL input is 25 MHz */
 160        ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG,
 161                                        25000000, pll_out);
 162        if (ret < 0) {
 163                printk(KERN_ERR "Error when setting PLL input\n");
 164                return ret;
 165        }
 166
 167        /*set codec MCLK division for sample rate */
 168        ret = codec_dai->ops->set_clkdiv(codec_dai, WM8974_MCLKDIV, mclk);
 169        if (ret < 0) {
 170                printk(KERN_ERR "Error when setting MCLK division\n");
 171                return ret;
 172        }
 173
 174        return 0;
 175}
 176
 177static int mx27vis_hifi_hw_free(struct snd_pcm_substream *substream)
 178{
 179        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 180        struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 181
 182        /* disable the PLL */
 183        return codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, 0, 0);
 184}
 185
 186/*
 187 * mx27vis WM8974 HiFi DAI opserations.
 188 */
 189static struct snd_soc_ops mx27vis_hifi_ops = {
 190        .hw_params = mx27vis_hifi_hw_params,
 191        .hw_free = mx27vis_hifi_hw_free,
 192};
 193
 194
 195static int mx27vis_suspend(struct platform_device *pdev, pm_message_t state)
 196{
 197        return 0;
 198}
 199
 200static int mx27vis_resume(struct platform_device *pdev)
 201{
 202        return 0;
 203}
 204
 205static int mx27vis_probe(struct platform_device *pdev)
 206{
 207        int ret = 0;
 208
 209        ret = get_ssi_clk(0, &pdev->dev);
 210
 211        if (ret < 0) {
 212                printk(KERN_ERR "%s: cant get ssi clock\n", __func__);
 213                return ret;
 214        }
 215
 216
 217        return 0;
 218}
 219
 220static int mx27vis_remove(struct platform_device *pdev)
 221{
 222        put_ssi_clk(0);
 223        return 0;
 224}
 225
 226static struct snd_soc_dai_link mx27vis_dai[] = {
 227{ /* Hifi Playback*/
 228        .name = "WM8974",
 229        .stream_name = "WM8974 HiFi",
 230        .cpu_dai = &imx_ssi_pcm_dai[0],
 231        .codec_dai = &wm8974_dai,
 232        .ops = &mx27vis_hifi_ops,
 233},
 234};
 235
 236static struct snd_soc_card mx27vis = {
 237        .name = "mx27vis",
 238        .platform = &mx1_mx2_soc_platform,
 239        .probe = mx27vis_probe,
 240        .remove = mx27vis_remove,
 241        .suspend_pre = mx27vis_suspend,
 242        .resume_post = mx27vis_resume,
 243        .dai_link = mx27vis_dai,
 244        .num_links = ARRAY_SIZE(mx27vis_dai),
 245};
 246
 247static struct snd_soc_device mx27vis_snd_devdata = {
 248        .card = &mx27vis,
 249        .codec_dev = &soc_codec_dev_wm8974,
 250};
 251
 252static struct platform_device *mx27vis_snd_device;
 253
 254/* Temporal definition of board specific behaviour */
 255void gpio_ssi_active(int ssi_num)
 256{
 257        int ret = 0;
 258
 259        unsigned int ssi1_pins[] = {
 260                PC20_PF_SSI1_FS,
 261                PC21_PF_SSI1_RXD,
 262                PC22_PF_SSI1_TXD,
 263                PC23_PF_SSI1_CLK,
 264        };
 265        unsigned int ssi2_pins[] = {
 266                PC24_PF_SSI2_FS,
 267                PC25_PF_SSI2_RXD,
 268                PC26_PF_SSI2_TXD,
 269                PC27_PF_SSI2_CLK,
 270        };
 271        if (ssi_num == 0)
 272                ret = mxc_gpio_setup_multiple_pins(ssi1_pins,
 273                                ARRAY_SIZE(ssi1_pins), "USB OTG");
 274        else
 275                ret = mxc_gpio_setup_multiple_pins(ssi2_pins,
 276                                ARRAY_SIZE(ssi2_pins), "USB OTG");
 277        if (ret)
 278                printk(KERN_ERR "Error requesting ssi %x pins\n", ssi_num);
 279}
 280
 281
 282static int __init mx27vis_init(void)
 283{
 284        int ret;
 285
 286        mx27vis_snd_device = platform_device_alloc("soc-audio", -1);
 287        if (!mx27vis_snd_device)
 288                return -ENOMEM;
 289
 290        platform_set_drvdata(mx27vis_snd_device, &mx27vis_snd_devdata);
 291        mx27vis_snd_devdata.dev = &mx27vis_snd_device->dev;
 292        ret = platform_device_add(mx27vis_snd_device);
 293
 294        if (ret) {
 295                printk(KERN_ERR "ASoC: Platform device allocation failed\n");
 296                platform_device_put(mx27vis_snd_device);
 297        }
 298
 299        /* WM8974 uses SSI1 (HPCR1) via AUDMUX port 4 for audio (PPCR1) */
 300        gpio_ssi_active(0);
 301        audmux_connect_1_4();
 302
 303        return ret;
 304}
 305
 306static void __exit mx27vis_exit(void)
 307{
 308        /* We should call some "ssi_gpio_inactive()" properly */
 309}
 310
 311module_init(mx27vis_init);
 312module_exit(mx27vis_exit);
 313
 314
 315MODULE_AUTHOR("Javier Martin, javier.martin@vista-silicon.com");
 316MODULE_DESCRIPTION("ALSA SoC WM8974 mx27vis");
 317MODULE_LICENSE("GPL");
 318