linux/sound/soc/pxa/raumfeld.c
<<
>>
Prefs
   1/*
   2 * raumfeld_audio.c  --  SoC audio for Raumfeld audio devices
   3 *
   4 * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
   5 *
   6 * based on code from:
   7 *
   8 *    Wolfson Microelectronics PLC.
   9 *    Openedhand Ltd.
  10 *    Liam Girdwood <lrg@slimlogic.co.uk>
  11 *    Richard Purdie <richard@openedhand.com>
  12 *
  13 * This program is free software; you can redistribute  it and/or modify it
  14 * under  the terms of  the GNU General  Public License as published by the
  15 * Free Software Foundation;  either version 2 of the  License, or (at your
  16 * option) any later version.
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/i2c.h>
  21#include <linux/delay.h>
  22#include <linux/gpio.h>
  23#include <sound/pcm.h>
  24#include <sound/soc.h>
  25
  26#include <asm/mach-types.h>
  27
  28#include "pxa-ssp.h"
  29
  30#define GPIO_SPDIF_RESET        (38)
  31#define GPIO_MCLK_RESET         (111)
  32#define GPIO_CODEC_RESET        (120)
  33
  34static struct i2c_client *max9486_client;
  35static struct i2c_board_info max9486_hwmon_info = {
  36        I2C_BOARD_INFO("max9485", 0x63),
  37};
  38
  39#define MAX9485_MCLK_FREQ_112896 0x22
  40#define MAX9485_MCLK_FREQ_122880 0x23
  41#define MAX9485_MCLK_FREQ_225792 0x32
  42#define MAX9485_MCLK_FREQ_245760 0x33
  43
  44static void set_max9485_clk(char clk)
  45{
  46        i2c_master_send(max9486_client, &clk, 1);
  47}
  48
  49static void raumfeld_enable_audio(bool en)
  50{
  51        if (en) {
  52                gpio_set_value(GPIO_MCLK_RESET, 1);
  53
  54                /* wait some time to let the clocks become stable */
  55                msleep(100);
  56
  57                gpio_set_value(GPIO_SPDIF_RESET, 1);
  58                gpio_set_value(GPIO_CODEC_RESET, 1);
  59        } else {
  60                gpio_set_value(GPIO_MCLK_RESET, 0);
  61                gpio_set_value(GPIO_SPDIF_RESET, 0);
  62                gpio_set_value(GPIO_CODEC_RESET, 0);
  63        }
  64}
  65
  66/* CS4270 */
  67static int raumfeld_cs4270_startup(struct snd_pcm_substream *substream)
  68{
  69        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  70        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  71
  72        /* set freq to 0 to enable all possible codec sample rates */
  73        return snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0);
  74}
  75
  76static void raumfeld_cs4270_shutdown(struct snd_pcm_substream *substream)
  77{
  78        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  79        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  80
  81        /* set freq to 0 to enable all possible codec sample rates */
  82        snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0);
  83}
  84
  85static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream,
  86                                     struct snd_pcm_hw_params *params)
  87{
  88        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  89        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  90        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  91        unsigned int fmt, clk = 0;
  92        int ret = 0;
  93
  94        switch (params_rate(params)) {
  95        case 44100:
  96                set_max9485_clk(MAX9485_MCLK_FREQ_112896);
  97                clk = 11289600;
  98                break;
  99        case 48000:
 100                set_max9485_clk(MAX9485_MCLK_FREQ_122880);
 101                clk = 12288000;
 102                break;
 103        case 88200:
 104                set_max9485_clk(MAX9485_MCLK_FREQ_225792);
 105                clk = 22579200;
 106                break;
 107        case 96000:
 108                set_max9485_clk(MAX9485_MCLK_FREQ_245760);
 109                clk = 24576000;
 110                break;
 111        default:
 112                return -EINVAL;
 113        }
 114
 115        fmt = SND_SOC_DAIFMT_I2S |
 116              SND_SOC_DAIFMT_NB_NF |
 117              SND_SOC_DAIFMT_CBS_CFS;
 118
 119        /* setup the CODEC DAI */
 120        ret = snd_soc_dai_set_fmt(codec_dai, fmt);
 121        if (ret < 0)
 122                return ret;
 123
 124        ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, 0);
 125        if (ret < 0)
 126                return ret;
 127
 128        /* setup the CPU DAI */
 129        ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk);
 130        if (ret < 0)
 131                return ret;
 132
 133        ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
 134        if (ret < 0)
 135                return ret;
 136
 137        ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4);
 138        if (ret < 0)
 139                return ret;
 140
 141        ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1);
 142        if (ret < 0)
 143                return ret;
 144
 145        return 0;
 146}
 147
 148static struct snd_soc_ops raumfeld_cs4270_ops = {
 149        .startup = raumfeld_cs4270_startup,
 150        .shutdown = raumfeld_cs4270_shutdown,
 151        .hw_params = raumfeld_cs4270_hw_params,
 152};
 153
 154static int raumfeld_analog_suspend(struct snd_soc_card *card)
 155{
 156        raumfeld_enable_audio(false);
 157        return 0;
 158}
 159
 160static int raumfeld_analog_resume(struct snd_soc_card *card)
 161{
 162        raumfeld_enable_audio(true);
 163        return 0;
 164}
 165
 166/* AK4104 */
 167
 168static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream,
 169                                     struct snd_pcm_hw_params *params)
 170{
 171        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 172        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 173        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 174        int fmt, ret = 0, clk = 0;
 175
 176        switch (params_rate(params)) {
 177        case 44100:
 178                set_max9485_clk(MAX9485_MCLK_FREQ_112896);
 179                clk = 11289600;
 180                break;
 181        case 48000:
 182                set_max9485_clk(MAX9485_MCLK_FREQ_122880);
 183                clk = 12288000;
 184                break;
 185        case 88200:
 186                set_max9485_clk(MAX9485_MCLK_FREQ_225792);
 187                clk = 22579200;
 188                break;
 189        case 96000:
 190                set_max9485_clk(MAX9485_MCLK_FREQ_245760);
 191                clk = 24576000;
 192                break;
 193        default:
 194                return -EINVAL;
 195        }
 196
 197        fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF;
 198
 199        /* setup the CODEC DAI */
 200        ret = snd_soc_dai_set_fmt(codec_dai, fmt | SND_SOC_DAIFMT_CBS_CFS);
 201        if (ret < 0)
 202                return ret;
 203
 204        /* setup the CPU DAI */
 205        ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk);
 206        if (ret < 0)
 207                return ret;
 208
 209        ret = snd_soc_dai_set_fmt(cpu_dai, fmt | SND_SOC_DAIFMT_CBS_CFS);
 210        if (ret < 0)
 211                return ret;
 212
 213        ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4);
 214        if (ret < 0)
 215                return ret;
 216
 217        ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1);
 218        if (ret < 0)
 219                return ret;
 220
 221        return 0;
 222}
 223
 224static struct snd_soc_ops raumfeld_ak4104_ops = {
 225        .hw_params = raumfeld_ak4104_hw_params,
 226};
 227
 228#define DAI_LINK_CS4270         \
 229{                                                       \
 230        .name           = "CS4270",                     \
 231        .stream_name    = "CS4270",                     \
 232        .cpu_dai_name   = "pxa-ssp-dai.0",              \
 233        .platform_name  = "pxa-pcm-audio",              \
 234        .codec_dai_name = "cs4270-hifi",                \
 235        .codec_name     = "cs4270.0-0048",      \
 236        .ops            = &raumfeld_cs4270_ops,         \
 237}
 238
 239#define DAI_LINK_AK4104         \
 240{                                                       \
 241        .name           = "ak4104",                     \
 242        .stream_name    = "Playback",                   \
 243        .cpu_dai_name   = "pxa-ssp-dai.1",              \
 244        .codec_dai_name = "ak4104-hifi",                \
 245        .platform_name  = "pxa-pcm-audio",              \
 246        .ops            = &raumfeld_ak4104_ops,         \
 247        .codec_name     = "spi0.0",                     \
 248}
 249
 250static struct snd_soc_dai_link snd_soc_raumfeld_connector_dai[] =
 251{
 252        DAI_LINK_CS4270,
 253        DAI_LINK_AK4104,
 254};
 255
 256static struct snd_soc_dai_link snd_soc_raumfeld_speaker_dai[] =
 257{
 258        DAI_LINK_CS4270,
 259};
 260
 261static struct snd_soc_card snd_soc_raumfeld_connector = {
 262        .name           = "Raumfeld Connector",
 263        .owner          = THIS_MODULE,
 264        .dai_link       = snd_soc_raumfeld_connector_dai,
 265        .num_links      = ARRAY_SIZE(snd_soc_raumfeld_connector_dai),
 266        .suspend_post   = raumfeld_analog_suspend,
 267        .resume_pre     = raumfeld_analog_resume,
 268};
 269
 270static struct snd_soc_card snd_soc_raumfeld_speaker = {
 271        .name           = "Raumfeld Speaker",
 272        .owner          = THIS_MODULE,
 273        .dai_link       = snd_soc_raumfeld_speaker_dai,
 274        .num_links      = ARRAY_SIZE(snd_soc_raumfeld_speaker_dai),
 275        .suspend_post   = raumfeld_analog_suspend,
 276        .resume_pre     = raumfeld_analog_resume,
 277};
 278
 279static struct platform_device *raumfeld_audio_device;
 280
 281static int __init raumfeld_audio_init(void)
 282{
 283        int ret;
 284
 285        if (!machine_is_raumfeld_speaker() &&
 286            !machine_is_raumfeld_connector())
 287                return 0;
 288
 289        max9486_client = i2c_new_device(i2c_get_adapter(0),
 290                                        &max9486_hwmon_info);
 291
 292        if (!max9486_client)
 293                return -ENOMEM;
 294
 295        set_max9485_clk(MAX9485_MCLK_FREQ_122880);
 296
 297        /* Register analog device */
 298        raumfeld_audio_device = platform_device_alloc("soc-audio", 0);
 299        if (!raumfeld_audio_device)
 300                return -ENOMEM;
 301
 302        if (machine_is_raumfeld_speaker())
 303                platform_set_drvdata(raumfeld_audio_device,
 304                                     &snd_soc_raumfeld_speaker);
 305
 306        if (machine_is_raumfeld_connector())
 307                platform_set_drvdata(raumfeld_audio_device,
 308                                     &snd_soc_raumfeld_connector);
 309
 310        ret = platform_device_add(raumfeld_audio_device);
 311        if (ret < 0) {
 312                platform_device_put(raumfeld_audio_device);
 313                return ret;
 314        }
 315
 316        raumfeld_enable_audio(true);
 317        return 0;
 318}
 319
 320static void __exit raumfeld_audio_exit(void)
 321{
 322        raumfeld_enable_audio(false);
 323
 324        platform_device_unregister(raumfeld_audio_device);
 325
 326        i2c_unregister_device(max9486_client);
 327
 328        gpio_free(GPIO_MCLK_RESET);
 329        gpio_free(GPIO_CODEC_RESET);
 330        gpio_free(GPIO_SPDIF_RESET);
 331}
 332
 333module_init(raumfeld_audio_init);
 334module_exit(raumfeld_audio_exit);
 335
 336/* Module information */
 337MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
 338MODULE_DESCRIPTION("Raumfeld audio SoC");
 339MODULE_LICENSE("GPL");
 340