linux/sound/soc/soc-compress.c
<<
>>
Prefs
   1/*
   2 * soc-compress.c  --  ALSA SoC Compress
   3 *
   4 * Copyright (C) 2012 Intel Corp.
   5 *
   6 * Authors: Namarta Kohli <namartax.kohli@intel.com>
   7 *          Ramesh Babu K V <ramesh.babu@linux.intel.com>
   8 *          Vinod Koul <vinod.koul@linux.intel.com>
   9 *
  10 *  This program is free software; you can redistribute  it and/or modify it
  11 *  under  the terms of  the GNU General  Public License as published by the
  12 *  Free Software Foundation;  either version 2 of the  License, or (at your
  13 *  option) any later version.
  14 *
  15 */
  16
  17#include <linux/kernel.h>
  18#include <linux/init.h>
  19#include <linux/delay.h>
  20#include <linux/slab.h>
  21#include <linux/workqueue.h>
  22#include <sound/core.h>
  23#include <sound/compress_params.h>
  24#include <sound/compress_driver.h>
  25#include <sound/soc.h>
  26#include <sound/initval.h>
  27
  28static int soc_compr_open(struct snd_compr_stream *cstream)
  29{
  30        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
  31        struct snd_soc_platform *platform = rtd->platform;
  32        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  33        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  34        int ret = 0;
  35
  36        if (platform->driver->compr_ops && platform->driver->compr_ops->open) {
  37                ret = platform->driver->compr_ops->open(cstream);
  38                if (ret < 0) {
  39                        pr_err("compress asoc: can't open platform %s\n", platform->name);
  40                        goto out;
  41                }
  42        }
  43
  44        if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->startup) {
  45                ret = rtd->dai_link->compr_ops->startup(cstream);
  46                if (ret < 0) {
  47                        pr_err("compress asoc: %s startup failed\n", rtd->dai_link->name);
  48                        goto machine_err;
  49                }
  50        }
  51
  52        if (cstream->direction == SND_COMPRESS_PLAYBACK) {
  53                cpu_dai->playback_active++;
  54                codec_dai->playback_active++;
  55        } else {
  56                cpu_dai->capture_active++;
  57                codec_dai->capture_active++;
  58        }
  59
  60        cpu_dai->active++;
  61        codec_dai->active++;
  62        rtd->codec->active++;
  63
  64        return 0;
  65
  66machine_err:
  67        if (platform->driver->compr_ops && platform->driver->compr_ops->free)
  68                platform->driver->compr_ops->free(cstream);
  69out:
  70        return ret;
  71}
  72
  73static int soc_compr_free(struct snd_compr_stream *cstream)
  74{
  75        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
  76        struct snd_soc_platform *platform = rtd->platform;
  77        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  78        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  79        struct snd_soc_codec *codec = rtd->codec;
  80
  81        if (cstream->direction == SND_COMPRESS_PLAYBACK) {
  82                cpu_dai->playback_active--;
  83                codec_dai->playback_active--;
  84        } else {
  85                cpu_dai->capture_active--;
  86                codec_dai->capture_active--;
  87        }
  88
  89        snd_soc_dai_digital_mute(codec_dai, 1);
  90
  91        cpu_dai->active--;
  92        codec_dai->active--;
  93        codec->active--;
  94
  95        if (!cpu_dai->active)
  96                cpu_dai->rate = 0;
  97
  98        if (!codec_dai->active)
  99                codec_dai->rate = 0;
 100
 101
 102        if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->shutdown)
 103                rtd->dai_link->compr_ops->shutdown(cstream);
 104
 105        if (platform->driver->compr_ops && platform->driver->compr_ops->free)
 106                platform->driver->compr_ops->free(cstream);
 107        cpu_dai->runtime = NULL;
 108
 109        if (cstream->direction == SND_COMPRESS_PLAYBACK) {
 110                if (!rtd->pmdown_time || codec->ignore_pmdown_time ||
 111                    rtd->dai_link->ignore_pmdown_time) {
 112                        snd_soc_dapm_stream_event(rtd,
 113                                        SNDRV_PCM_STREAM_PLAYBACK,
 114                                        SND_SOC_DAPM_STREAM_STOP);
 115                } else
 116                        rtd->pop_wait = 1;
 117                        schedule_delayed_work(&rtd->delayed_work,
 118                                msecs_to_jiffies(rtd->pmdown_time));
 119        } else {
 120                /* capture streams can be powered down now */
 121                snd_soc_dapm_stream_event(rtd,
 122                        SNDRV_PCM_STREAM_CAPTURE,
 123                        SND_SOC_DAPM_STREAM_STOP);
 124        }
 125
 126        return 0;
 127}
 128
 129static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd)
 130{
 131
 132        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 133        struct snd_soc_platform *platform = rtd->platform;
 134        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 135        int ret = 0;
 136
 137        if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) {
 138                ret = platform->driver->compr_ops->trigger(cstream, cmd);
 139                if (ret < 0)
 140                        return ret;
 141        }
 142
 143        if (cmd == SNDRV_PCM_TRIGGER_START)
 144                snd_soc_dai_digital_mute(codec_dai, 0);
 145        else if (cmd == SNDRV_PCM_TRIGGER_STOP)
 146                snd_soc_dai_digital_mute(codec_dai, 1);
 147
 148        return ret;
 149}
 150
 151static int soc_compr_set_params(struct snd_compr_stream *cstream,
 152                                        struct snd_compr_params *params)
 153{
 154        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 155        struct snd_soc_platform *platform = rtd->platform;
 156        int ret = 0;
 157
 158        /* first we call set_params for the platform driver
 159         * this should configure the soc side
 160         * if the machine has compressed ops then we call that as well
 161         * expectation is that platform and machine will configure everything
 162         * for this compress path, like configuring pcm port for codec
 163         */
 164        if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) {
 165                ret = platform->driver->compr_ops->set_params(cstream, params);
 166                if (ret < 0)
 167                        return ret;
 168        }
 169
 170        if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) {
 171                ret = rtd->dai_link->compr_ops->set_params(cstream);
 172                if (ret < 0)
 173                        return ret;
 174        }
 175
 176        snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
 177                                SND_SOC_DAPM_STREAM_START);
 178
 179        return ret;
 180}
 181
 182static int soc_compr_get_params(struct snd_compr_stream *cstream,
 183                                        struct snd_codec *params)
 184{
 185        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 186        struct snd_soc_platform *platform = rtd->platform;
 187        int ret = 0;
 188
 189        if (platform->driver->compr_ops && platform->driver->compr_ops->get_params)
 190                ret = platform->driver->compr_ops->get_params(cstream, params);
 191
 192        return ret;
 193}
 194
 195static int soc_compr_get_caps(struct snd_compr_stream *cstream,
 196                                struct snd_compr_caps *caps)
 197{
 198        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 199        struct snd_soc_platform *platform = rtd->platform;
 200        int ret = 0;
 201
 202        if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps)
 203                ret = platform->driver->compr_ops->get_caps(cstream, caps);
 204
 205        return ret;
 206}
 207
 208static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream,
 209                                struct snd_compr_codec_caps *codec)
 210{
 211        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 212        struct snd_soc_platform *platform = rtd->platform;
 213        int ret = 0;
 214
 215        if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps)
 216                ret = platform->driver->compr_ops->get_codec_caps(cstream, codec);
 217
 218        return ret;
 219}
 220
 221static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes)
 222{
 223        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 224        struct snd_soc_platform *platform = rtd->platform;
 225        int ret = 0;
 226
 227        if (platform->driver->compr_ops && platform->driver->compr_ops->ack)
 228                ret = platform->driver->compr_ops->ack(cstream, bytes);
 229
 230        return ret;
 231}
 232
 233static int soc_compr_pointer(struct snd_compr_stream *cstream,
 234                        struct snd_compr_tstamp *tstamp)
 235{
 236        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 237        struct snd_soc_platform *platform = rtd->platform;
 238
 239        if (platform->driver->compr_ops && platform->driver->compr_ops->pointer)
 240                 platform->driver->compr_ops->pointer(cstream, tstamp);
 241
 242        return 0;
 243}
 244
 245/* ASoC Compress operations */
 246static struct snd_compr_ops soc_compr_ops = {
 247        .open           = soc_compr_open,
 248        .free           = soc_compr_free,
 249        .set_params     = soc_compr_set_params,
 250        .get_params     = soc_compr_get_params,
 251        .trigger        = soc_compr_trigger,
 252        .pointer        = soc_compr_pointer,
 253        .ack            = soc_compr_ack,
 254        .get_caps       = soc_compr_get_caps,
 255        .get_codec_caps = soc_compr_get_codec_caps
 256};
 257
 258/* create a new compress */
 259int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
 260{
 261        struct snd_soc_codec *codec = rtd->codec;
 262        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 263        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 264        struct snd_compr *compr;
 265        char new_name[64];
 266        int ret = 0, direction = 0;
 267
 268        /* check client and interface hw capabilities */
 269        snprintf(new_name, sizeof(new_name), "%s %s-%d",
 270                        rtd->dai_link->stream_name, codec_dai->name, num);
 271        direction = SND_COMPRESS_PLAYBACK;
 272        compr = kzalloc(sizeof(*compr), GFP_KERNEL);
 273        if (compr == NULL) {
 274                snd_printk(KERN_ERR "Cannot allocate compr\n");
 275                return -ENOMEM;
 276        }
 277
 278        compr->ops = &soc_compr_ops;
 279        mutex_init(&compr->lock);
 280        ret = snd_compress_new(rtd->card->snd_card, num, direction, compr);
 281        if (ret < 0) {
 282                pr_err("compress asoc: can't create compress for codec %s\n",
 283                        codec->name);
 284                kfree(compr);
 285                return ret;
 286        }
 287
 288        rtd->compr = compr;
 289        compr->private_data = rtd;
 290
 291        printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name,
 292                cpu_dai->name);
 293        return ret;
 294}
 295