linux/sound/soc/samsung/h1940_uda1380.c
<<
>>
Prefs
   1/*
   2 * h1940-uda1380.c  --  ALSA Soc Audio Layer
   3 *
   4 * Copyright (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
   5 * Copyright (c) 2010 Vasily Khoruzhick <anarsoul@gmail.com>
   6 *
   7 * Based on version from Arnaud Patard <arnaud.patard@rtp-net.org>
   8 *
   9 * This program is free software; you can redistribute  it and/or modify it
  10 * under  the terms of  the GNU General  Public License as published by the
  11 * Free Software Foundation;  either version 2 of the  License, or (at your
  12 * option) any later version.
  13 *
  14 */
  15
  16#include <linux/module.h>
  17#include <linux/moduleparam.h>
  18#include <linux/platform_device.h>
  19#include <linux/i2c.h>
  20#include <linux/gpio.h>
  21
  22#include <sound/soc.h>
  23#include <sound/uda1380.h>
  24#include <sound/jack.h>
  25
  26#include <plat/regs-iis.h>
  27
  28#include <mach/h1940-latch.h>
  29
  30#include <asm/mach-types.h>
  31
  32#include "dma.h"
  33#include "s3c24xx-i2s.h"
  34#include "../codecs/uda1380.h"
  35
  36static unsigned int rates[] = {
  37        11025,
  38        22050,
  39        44100,
  40};
  41
  42static struct snd_pcm_hw_constraint_list hw_rates = {
  43        .count = ARRAY_SIZE(rates),
  44        .list = rates,
  45        .mask = 0,
  46};
  47
  48static struct snd_soc_jack hp_jack;
  49
  50static struct snd_soc_jack_pin hp_jack_pins[] = {
  51        {
  52                .pin    = "Headphone Jack",
  53                .mask   = SND_JACK_HEADPHONE,
  54        },
  55        {
  56                .pin    = "Speaker",
  57                .mask   = SND_JACK_HEADPHONE,
  58                .invert = 1,
  59        },
  60};
  61
  62static struct snd_soc_jack_gpio hp_jack_gpios[] = {
  63        {
  64                .gpio                   = S3C2410_GPG(4),
  65                .name                   = "hp-gpio",
  66                .report                 = SND_JACK_HEADPHONE,
  67                .invert                 = 1,
  68                .debounce_time          = 200,
  69        },
  70};
  71
  72static int h1940_startup(struct snd_pcm_substream *substream)
  73{
  74        struct snd_pcm_runtime *runtime = substream->runtime;
  75
  76        runtime->hw.rate_min = hw_rates.list[0];
  77        runtime->hw.rate_max = hw_rates.list[hw_rates.count - 1];
  78        runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
  79
  80        return snd_pcm_hw_constraint_list(runtime, 0,
  81                                        SNDRV_PCM_HW_PARAM_RATE,
  82                                        &hw_rates);
  83}
  84
  85static int h1940_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 *cpu_dai = rtd->cpu_dai;
  90        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  91        int div;
  92        int ret;
  93        unsigned int rate = params_rate(params);
  94
  95        switch (rate) {
  96        case 11025:
  97        case 22050:
  98        case 44100:
  99                div = s3c24xx_i2s_get_clockrate() / (384 * rate);
 100                if (s3c24xx_i2s_get_clockrate() % (384 * rate) > (192 * rate))
 101                        div++;
 102                break;
 103        default:
 104                dev_err(&rtd->dev, "%s: rate %d is not supported\n",
 105                        __func__, rate);
 106                return -EINVAL;
 107        }
 108
 109        /* set codec DAI configuration */
 110        ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 111                SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 112        if (ret < 0)
 113                return ret;
 114
 115        /* set cpu DAI configuration */
 116        ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 117                SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 118        if (ret < 0)
 119                return ret;
 120
 121        /* select clock source */
 122        ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_PCLK, rate,
 123                        SND_SOC_CLOCK_OUT);
 124        if (ret < 0)
 125                return ret;
 126
 127        /* set MCLK division for sample rate */
 128        ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
 129                S3C2410_IISMOD_384FS);
 130        if (ret < 0)
 131                return ret;
 132
 133        /* set BCLK division for sample rate */
 134        ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
 135                S3C2410_IISMOD_32FS);
 136        if (ret < 0)
 137                return ret;
 138
 139        /* set prescaler division for sample rate */
 140        ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
 141                S3C24XX_PRESCALE(div, div));
 142        if (ret < 0)
 143                return ret;
 144
 145        return 0;
 146}
 147
 148static struct snd_soc_ops h1940_ops = {
 149        .startup        = h1940_startup,
 150        .hw_params      = h1940_hw_params,
 151};
 152
 153static int h1940_spk_power(struct snd_soc_dapm_widget *w,
 154                                struct snd_kcontrol *kcontrol, int event)
 155{
 156        if (SND_SOC_DAPM_EVENT_ON(event))
 157                gpio_set_value(H1940_LATCH_AUDIO_POWER, 1);
 158        else
 159                gpio_set_value(H1940_LATCH_AUDIO_POWER, 0);
 160
 161        return 0;
 162}
 163
 164/* h1940 machine dapm widgets */
 165static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
 166        SND_SOC_DAPM_HP("Headphone Jack", NULL),
 167        SND_SOC_DAPM_MIC("Mic Jack", NULL),
 168        SND_SOC_DAPM_SPK("Speaker", h1940_spk_power),
 169};
 170
 171/* h1940 machine audio_map */
 172static const struct snd_soc_dapm_route audio_map[] = {
 173        /* headphone connected to VOUTLHP, VOUTRHP */
 174        {"Headphone Jack", NULL, "VOUTLHP"},
 175        {"Headphone Jack", NULL, "VOUTRHP"},
 176
 177        /* ext speaker connected to VOUTL, VOUTR  */
 178        {"Speaker", NULL, "VOUTL"},
 179        {"Speaker", NULL, "VOUTR"},
 180
 181        /* mic is connected to VINM */
 182        {"VINM", NULL, "Mic Jack"},
 183};
 184
 185static struct platform_device *s3c24xx_snd_device;
 186
 187static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd)
 188{
 189        struct snd_soc_codec *codec = rtd->codec;
 190        struct snd_soc_dapm_context *dapm = &codec->dapm;
 191        int err;
 192
 193        /* Add h1940 specific widgets */
 194        err = snd_soc_dapm_new_controls(dapm, uda1380_dapm_widgets,
 195                                  ARRAY_SIZE(uda1380_dapm_widgets));
 196        if (err)
 197                return err;
 198
 199        /* Set up h1940 specific audio path audio_mapnects */
 200        err = snd_soc_dapm_add_routes(dapm, audio_map,
 201                                      ARRAY_SIZE(audio_map));
 202        if (err)
 203                return err;
 204
 205        snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
 206        snd_soc_dapm_enable_pin(dapm, "Speaker");
 207        snd_soc_dapm_enable_pin(dapm, "Mic Jack");
 208
 209        snd_soc_dapm_sync(dapm);
 210
 211        snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
 212                &hp_jack);
 213
 214        snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins),
 215                hp_jack_pins);
 216
 217        snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
 218                hp_jack_gpios);
 219
 220        return 0;
 221}
 222
 223/* s3c24xx digital audio interface glue - connects codec <--> CPU */
 224static struct snd_soc_dai_link h1940_uda1380_dai[] = {
 225        {
 226                .name           = "uda1380",
 227                .stream_name    = "UDA1380 Duplex",
 228                .cpu_dai_name   = "s3c24xx-iis",
 229                .codec_dai_name = "uda1380-hifi",
 230                .init           = h1940_uda1380_init,
 231                .platform_name  = "samsung-audio",
 232                .codec_name     = "uda1380-codec.0-001a",
 233                .ops            = &h1940_ops,
 234        },
 235};
 236
 237static struct snd_soc_card h1940_asoc = {
 238        .name = "h1940",
 239        .dai_link = h1940_uda1380_dai,
 240        .num_links = ARRAY_SIZE(h1940_uda1380_dai),
 241};
 242
 243static int __init h1940_init(void)
 244{
 245        int ret;
 246
 247        if (!machine_is_h1940())
 248                return -ENODEV;
 249
 250        /* configure some gpios */
 251        ret = gpio_request(H1940_LATCH_AUDIO_POWER, "speaker-power");
 252        if (ret)
 253                goto err_out;
 254
 255        ret = gpio_direction_output(H1940_LATCH_AUDIO_POWER, 0);
 256        if (ret)
 257                goto err_gpio;
 258
 259        s3c24xx_snd_device = platform_device_alloc("soc-audio", -1);
 260        if (!s3c24xx_snd_device) {
 261                ret = -ENOMEM;
 262                goto err_gpio;
 263        }
 264
 265        platform_set_drvdata(s3c24xx_snd_device, &h1940_asoc);
 266        ret = platform_device_add(s3c24xx_snd_device);
 267
 268        if (ret)
 269                goto err_plat;
 270
 271        return 0;
 272
 273err_plat:
 274        platform_device_put(s3c24xx_snd_device);
 275err_gpio:
 276        gpio_free(H1940_LATCH_AUDIO_POWER);
 277
 278err_out:
 279        return ret;
 280}
 281
 282static void __exit h1940_exit(void)
 283{
 284        platform_device_unregister(s3c24xx_snd_device);
 285        snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
 286                hp_jack_gpios);
 287        gpio_free(H1940_LATCH_AUDIO_POWER);
 288}
 289
 290module_init(h1940_init);
 291module_exit(h1940_exit);
 292
 293/* Module information */
 294MODULE_AUTHOR("Arnaud Patard, Vasily Khoruzhick");
 295MODULE_DESCRIPTION("ALSA SoC H1940");
 296MODULE_LICENSE("GPL");
 297