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 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        ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, 0);
 116        if (ret < 0)
 117                return ret;
 118
 119        /* setup the CPU DAI */
 120        ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk);
 121        if (ret < 0)
 122                return ret;
 123
 124        ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4);
 125        if (ret < 0)
 126                return ret;
 127
 128        ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1);
 129        if (ret < 0)
 130                return ret;
 131
 132        return 0;
 133}
 134
 135static const struct snd_soc_ops raumfeld_cs4270_ops = {
 136        .startup = raumfeld_cs4270_startup,
 137        .shutdown = raumfeld_cs4270_shutdown,
 138        .hw_params = raumfeld_cs4270_hw_params,
 139};
 140
 141static int raumfeld_analog_suspend(struct snd_soc_card *card)
 142{
 143        raumfeld_enable_audio(false);
 144        return 0;
 145}
 146
 147static int raumfeld_analog_resume(struct snd_soc_card *card)
 148{
 149        raumfeld_enable_audio(true);
 150        return 0;
 151}
 152
 153/* AK4104 */
 154
 155static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream,
 156                                     struct snd_pcm_hw_params *params)
 157{
 158        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 159        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 160        int ret = 0, clk = 0;
 161
 162        switch (params_rate(params)) {
 163        case 44100:
 164                set_max9485_clk(MAX9485_MCLK_FREQ_112896);
 165                clk = 11289600;
 166                break;
 167        case 48000:
 168                set_max9485_clk(MAX9485_MCLK_FREQ_122880);
 169                clk = 12288000;
 170                break;
 171        case 88200:
 172                set_max9485_clk(MAX9485_MCLK_FREQ_225792);
 173                clk = 22579200;
 174                break;
 175        case 96000:
 176                set_max9485_clk(MAX9485_MCLK_FREQ_245760);
 177                clk = 24576000;
 178                break;
 179        default:
 180                return -EINVAL;
 181        }
 182
 183        /* setup the CPU DAI */
 184        ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk);
 185        if (ret < 0)
 186                return ret;
 187
 188        ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4);
 189        if (ret < 0)
 190                return ret;
 191
 192        ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1);
 193        if (ret < 0)
 194                return ret;
 195
 196        return 0;
 197}
 198
 199static struct snd_soc_ops raumfeld_ak4104_ops = {
 200        .hw_params = raumfeld_ak4104_hw_params,
 201};
 202
 203#define DAI_LINK_CS4270         \
 204{                                                       \
 205        .name           = "CS4270",                     \
 206        .stream_name    = "CS4270",                     \
 207        .cpu_dai_name   = "pxa-ssp-dai.0",              \
 208        .platform_name  = "pxa-pcm-audio",              \
 209        .codec_dai_name = "cs4270-hifi",                \
 210        .codec_name     = "cs4270.0-0048",      \
 211        .dai_fmt        = SND_SOC_DAIFMT_I2S |          \
 212                          SND_SOC_DAIFMT_NB_NF |        \
 213                          SND_SOC_DAIFMT_CBS_CFS,       \
 214        .ops            = &raumfeld_cs4270_ops,         \
 215}
 216
 217#define DAI_LINK_AK4104         \
 218{                                                       \
 219        .name           = "ak4104",                     \
 220        .stream_name    = "Playback",                   \
 221        .cpu_dai_name   = "pxa-ssp-dai.1",              \
 222        .codec_dai_name = "ak4104-hifi",                \
 223        .platform_name  = "pxa-pcm-audio",              \
 224        .dai_fmt        = SND_SOC_DAIFMT_I2S |          \
 225                          SND_SOC_DAIFMT_NB_NF |        \
 226                          SND_SOC_DAIFMT_CBS_CFS,       \
 227        .ops            = &raumfeld_ak4104_ops,         \
 228        .codec_name     = "spi0.0",                     \
 229}
 230
 231static struct snd_soc_dai_link snd_soc_raumfeld_connector_dai[] = {
 232        DAI_LINK_CS4270,
 233        DAI_LINK_AK4104,
 234};
 235
 236static struct snd_soc_dai_link snd_soc_raumfeld_speaker_dai[] = {
 237        DAI_LINK_CS4270,
 238};
 239
 240static struct snd_soc_card snd_soc_raumfeld_connector = {
 241        .name           = "Raumfeld Connector",
 242        .owner          = THIS_MODULE,
 243        .dai_link       = snd_soc_raumfeld_connector_dai,
 244        .num_links      = ARRAY_SIZE(snd_soc_raumfeld_connector_dai),
 245        .suspend_post   = raumfeld_analog_suspend,
 246        .resume_pre     = raumfeld_analog_resume,
 247};
 248
 249static struct snd_soc_card snd_soc_raumfeld_speaker = {
 250        .name           = "Raumfeld Speaker",
 251        .owner          = THIS_MODULE,
 252        .dai_link       = snd_soc_raumfeld_speaker_dai,
 253        .num_links      = ARRAY_SIZE(snd_soc_raumfeld_speaker_dai),
 254        .suspend_post   = raumfeld_analog_suspend,
 255        .resume_pre     = raumfeld_analog_resume,
 256};
 257
 258static struct platform_device *raumfeld_audio_device;
 259
 260static int __init raumfeld_audio_init(void)
 261{
 262        int ret;
 263
 264        if (!machine_is_raumfeld_speaker() &&
 265            !machine_is_raumfeld_connector())
 266                return 0;
 267
 268        max9486_client = i2c_new_device(i2c_get_adapter(0),
 269                                        &max9486_hwmon_info);
 270
 271        if (!max9486_client)
 272                return -ENOMEM;
 273
 274        set_max9485_clk(MAX9485_MCLK_FREQ_122880);
 275
 276        /* Register analog device */
 277        raumfeld_audio_device = platform_device_alloc("soc-audio", 0);
 278        if (!raumfeld_audio_device)
 279                return -ENOMEM;
 280
 281        if (machine_is_raumfeld_speaker())
 282                platform_set_drvdata(raumfeld_audio_device,
 283                                     &snd_soc_raumfeld_speaker);
 284
 285        if (machine_is_raumfeld_connector())
 286                platform_set_drvdata(raumfeld_audio_device,
 287                                     &snd_soc_raumfeld_connector);
 288
 289        ret = platform_device_add(raumfeld_audio_device);
 290        if (ret < 0) {
 291                platform_device_put(raumfeld_audio_device);
 292                return ret;
 293        }
 294
 295        raumfeld_enable_audio(true);
 296        return 0;
 297}
 298
 299static void __exit raumfeld_audio_exit(void)
 300{
 301        raumfeld_enable_audio(false);
 302
 303        platform_device_unregister(raumfeld_audio_device);
 304
 305        i2c_unregister_device(max9486_client);
 306
 307        gpio_free(GPIO_MCLK_RESET);
 308        gpio_free(GPIO_CODEC_RESET);
 309        gpio_free(GPIO_SPDIF_RESET);
 310}
 311
 312module_init(raumfeld_audio_init);
 313module_exit(raumfeld_audio_exit);
 314
 315/* Module information */
 316MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
 317MODULE_DESCRIPTION("Raumfeld audio SoC");
 318MODULE_LICENSE("GPL");
 319