Merge changes Id9b8570c,I6c849673,I30a1d8b5,I9d35b1a3 into msm-3.0

* changes:
  added G723 and G729
  Fixes for 1. fixes for comments recieved on alsa-devel
  compress: add the core file
  compress: add API header and driver header files
This commit is contained in:
Linux Build Service Account
2011-11-26 15:56:23 -08:00
committed by QuIC Gerrit Code Review
5 changed files with 1049 additions and 107 deletions

View File

@@ -180,6 +180,7 @@ Instead,
Credits:
- Mark Brown and Liam Girdwood for discussions on the need for this API
- Harsha Priya for her work on intel_sst compressed API
- Rakesh Ughreja for valuable feedback
- Sing Nallasellan, Sikkandar Madar and Prasanna Samaga for
demonstrating and quantifying the benefits of audio offload on a

View File

@@ -0,0 +1,150 @@
/*
* compress_driver.h - compress offload driver definations
*
* Copyright (C) 2011 Intel Corporation
* Authors: Vinod Koul <vinod.koul@linux.intel.com>
* Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*/
#ifndef __COMPRESS_DRIVER_H
#define __COMPRESS_DRIVER_H
#include <sound/compress_offload.h>
#include <sound/asound.h>
#include <sound/pcm.h>
struct snd_compr_ops;
/**
* struct snd_compr_runtime: runtime stream description
* @state: stream state
* @ops: pointer to DSP callbacks
* @buffer: pointer to kernel buffer, valid only when not in mmap mode or
* DSP doesn't implement copy
* @buffer_size: size of the above buffer
* @fragment_size: size of buffer fragment in bytes
* @fragments: number of such fragments
* @hw_pointer: offset of last location in buffer where DSP copied data
* @app_pointer: offset of last location in buffer where app wrote data
* @sleep: poll sleep
*/
struct snd_compr_runtime {
snd_pcm_state_t state;
struct snd_compr_ops *ops;
void *buffer;
size_t buffer_size;
size_t fragment_size;
unsigned int fragments;
size_t hw_pointer;
size_t app_pointer;
wait_queue_head_t sleep;
};
/**
* struct snd_compr_stream: compressed stream
* @name: device name
* @ops: pointer to DSP callbacks
* @runtime: pointer to runtime structure
* @device: device pointer
* @direction: stream direction, playback/recording
* @private_data: pointer to DSP private data
*/
struct snd_compr_stream {
const char *name;
struct snd_compr_ops *ops;
struct snd_compr_runtime *runtime;
struct snd_compr *device;
unsigned int direction;
void *private_data;
};
/**
* struct snd_compr_ops: compressed path DSP operations
* @open: Open the compressed stream
* This callback is mandatory and shall keep dsp ready to receive the stream
* parameter
* @free: Close the compressed stream, mandatory
* @set_params: Sets the compressed stream parameters, mandatory
* This can be called in during stream creation only to set codec params
* and the stream properties
* @get_params: retrieve the codec parameters, mandatory
* @trigger: Trigger operations like start, pause, resume, drain, stop.
* This callback is mandatory
* @pointer: Retrieve current h/w pointer information. Mandatory
* @copy: Copy the compressed data to/from userspace, Optional
* Can't be implemented if DSP supports mmap
* @mmap: DSP mmap method to mmap DSP memory
* @ack: Ack for DSP when data is written to audio buffer, Optional
* Not valid if copy is implemented
* @get_caps: Retrieve DSP capabilities, mandatory
* @get_codec_caps: Retrieve capabilities for a specific codec, mandatory
*/
struct snd_compr_ops {
int (*open)(struct snd_compr_stream *stream);
int (*free)(struct snd_compr_stream *stream);
int (*set_params)(struct snd_compr_stream *stream,
struct snd_compr_params *params);
int (*get_params)(struct snd_compr_stream *stream,
struct snd_compr_params *params);
int (*trigger)(struct snd_compr_stream *stream, int cmd);
int (*pointer)(struct snd_compr_stream *stream,
struct snd_compr_tstamp *tstamp);
int (*copy)(struct snd_compr_stream *stream, const char __user *buf,
size_t count);
int (*mmap)(struct snd_compr_stream *stream,
struct vm_area_struct *vma);
int (*ack)(struct snd_compr_stream *stream);
int (*get_caps) (struct snd_compr_stream *stream,
struct snd_compr_caps *caps);
int (*get_codec_caps) (struct snd_compr_stream *stream,
struct snd_compr_codec_caps *codec);
};
/**
* struct snd_compr: Compressed device
* @name: DSP device name
* @dev: Device pointer
* @lock: device lock
* @ops: pointer to DSP callbacks
* @private_data: pointer to DSP pvt data
*/
struct snd_compr {
const char *name;
struct device *dev;
struct mutex lock;
struct snd_compr_ops *ops;
struct list_head list;
void *private_data;
};
/* compress device register APIs */
int snd_compress_register(struct snd_compr *device);
int snd_compress_deregister(struct snd_compr *device);
/* dsp driver callback apis
* For playback: driver should call snd_compress_fragment_elapsed() to let the
* framework know that a fragment has been consumed from the ring buffer
* For recording: we may want to know when a frame is available or when
* at least one frame is available for userspace, a different
* snd_compress_frame_elapsed() callback should be used
*/
void snd_compr_fragment_elapsed(struct snd_compr_stream *stream);
void snd_compr_frame_elapsed(struct snd_compr_stream *stream);
#endif

View File

@@ -0,0 +1,137 @@
/*
* compress_offload.h - compress offload header definations
*
* Copyright (C) 2011 Intel Corporation
* Authors: Vinod Koul <vinod.koul@linux.intel.com>
* Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*/
#ifndef __COMPRESS_OFFLOAD_H
#define __COMPRESS_OFFLOAD_H
#include <linux/types.h>
/**
* struct snd_compressed_buffer:compressed buffer
* @fragment_size: size of buffer fragment in bytes
* @fragments: number of such fragments
*/
struct snd_compressed_buffer {
size_t fragment_size;
int fragments;
};
/* */
struct snd_compr_params {
struct snd_compressed_buffer buffer;
struct snd_codec codec;
};
/**
* struct snd_compr_tstamp: timestamp descriptor
* @copied_bytes: Number of bytes offset in ring buffer to DSP
* @copied_total: Total number of bytes copied from ring buffer to DSP
* @decoded: Frames decoded by DSP
* @rendered: Frames rendered by DSP into a mixer or an audio output
* @sampling_rate: sampling rate of audio
*/
struct snd_compr_tstamp {
size_t copied_bytes;
size_t copied_total;
size_t decoded;
size_t rendered;
__u32 sampling_rate;
};
/**
* struct snd_compr_avail: avail descriptor
* @avail: Number of bytes available in ring buffer for writing/reading
* @tstamp: timestamp infomation
*/
struct snd_compr_avail {
size_t avail;
struct snd_compr_tstamp tstamp;
};
/**
* struct snd_compr_caps: caps descriptor
* @codecs: pointer to array of codecs
* @min_fragment_size: minimum fragment supported by DSP
* @max_fragment_size: maximum fragment supported by DSP
* @min_fragments: min fragments supported by DSP
* @max_fragments: max fragments supported by DSP
* @num_codecs: number of codecs supported
* @reserved: reserved field
*/
struct snd_compr_caps {
__u32 num_codecs;
__u32 min_fragment_size;
__u32 max_fragment_size;
__u32 min_fragments;
__u32 max_fragments;
__u32 codecs[MAX_NUM_CODECS];
__u32 reserved[11];
};
/**
* struct snd_compr_codec_caps: query capability of codec
* @codec: codec for which capability is queried
* @num_descriptors: number of codec descriptors
* @descriptor: array of codec capability descriptor
*/
struct snd_compr_codec_caps {
__u32 codec;
__u32 num_descriptors;
struct snd_codec_desc descriptor[MAX_NUM_CODEC_DESCRIPTORS];
};
/**
* compress path ioctl definitions
* SNDRV_COMPRESS_GET_CAPS: Query capability of DSP
* SNDRV_COMPRESS_GET_CODEC_CAPS: Query capability of a codec
* SNDRV_COMPRESS_SET_PARAMS: Set codec and stream parameters
* Note: only codec params can be changed runtime and stream params cant be
* SNDRV_COMPRESS_GET_PARAMS: Query codec and stream params
* SNDRV_COMPRESS_TSTAMP: get the current timestamp value
* SNDRV_COMPRESS_AVAIL: get the current buffer avail value.
* This also queries the tstamp properties
* SNDRV_COMPRESS_PAUSE: Pause the running stream
* SNDRV_COMPRESS_RESUME: resume a paused stream
* SNDRV_COMPRESS_START: Start a stream
* SNDRV_COMPRESS_STOP: stop a running stream, discarding ring buffer content
* and the buffers currently with DSP
* SNDRV_COMPRESS_DRAIN: Play till end of buffers and stop after that
*/
#define SNDRV_COMPRESS_GET_CAPS _IOWR('C', 0x00, struct snd_compr_caps *)
#define SNDRV_COMPRESS_GET_CODEC_CAPS _IOWR('C', 0x01, struct snd_compr_codec_caps *)
#define SNDRV_COMPRESS_SET_PARAMS _IOW('C', 0x02, struct snd_compr_params *)
#define SNDRV_COMPRESS_GET_PARAMS _IOR('C', 0x03, struct snd_compr_params *)
#define SNDRV_COMPRESS_TSTAMP _IOR('C', 0x10, struct snd_compr_tstamp *)
#define SNDRV_COMPRESS_AVAIL _IOR('C', 0x11, struct snd_compr_avail *)
#define SNDRV_COMPRESS_PAUSE _IO('C', 0x20)
#define SNDRV_COMPRESS_RESUME _IO('C', 0x21)
#define SNDRV_COMPRESS_START _IO('C', 0x22)
#define SNDRV_COMPRESS_STOP _IO('C', 0x23)
#define SNDRV_COMPRESS_DRAIN _IO('C', 0x24)
/*
* TODO
* 1. add mmap support
*
*/
#define SND_COMPR_TRIGGER_DRAIN 7 /*FIXME move this to pcm.h */
#endif

View File

@@ -3,7 +3,7 @@
* streaming interface
*
* Copyright (C) 2011 Intel Corporation
* Authors: Pierre-Louis.Bossart <pierre-louis.bossart@linux.intel.com>
* Authors: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
* Vinod Koul <vinod.koul@linux.intel.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -53,7 +53,6 @@
/* AUDIO CODECS SUPPORTED */
#define MAX_NUM_CODECS 32
#define MAX_NUM_CODEC_DESCRIPTORS 32
#define MAX_NUM_RATES 32
#define MAX_NUM_BITRATES 32
/* Codecs are listed linearly to allow for extensibility */
@@ -68,6 +67,8 @@
#define SND_AUDIOCODEC_VORBIS ((__u32) 0x00000009)
#define SND_AUDIOCODEC_FLAC ((__u32) 0x0000000A)
#define SND_AUDIOCODEC_IEC61937 ((__u32) 0x0000000B)
#define SND_AUDIOCODEC_G723_1 ((__u32) 0x0000000C)
#define SND_AUDIOCODEC_G729 ((__u32) 0x0000000D)
/*
* Profile and modes are listed with bit masks. This allows for a
@@ -188,8 +189,8 @@
/*
* IEC modes are mandatory for decoders. Format autodetection
* will only happen on the DSP side with mode 0. The PCM mode should
* not be used, the PCM codec should be used instead
* will only happen on the DSP side with mode 0. The PCM mode should
* not be used, the PCM codec should be used instead.
*/
#define SND_AUDIOMODE_IEC_REF_STREAM_HEADER ((__u32) 0x00000000)
#define SND_AUDIOMODE_IEC_LPCM ((__u32) 0x00000001)
@@ -211,6 +212,17 @@
#define SND_AUDIOMODE_IEC_HE_AAC2 ((__u32) 0x00010000)
#define SND_AUDIOMODE_IEC_MPEG_SURROUND ((__u32) 0x00020000)
#define SND_AUDIOPROFILE_G723_1 ((__u32) 0x00000001)
#define SND_AUDIOMODE_G723_1_ANNEX_A ((__u32) 0x00000001)
#define SND_AUDIOMODE_G723_1_ANNEX_B ((__u32) 0x00000002)
#define SND_AUDIOMODE_G723_1_ANNEX_C ((__u32) 0x00000004)
#define SND_AUDIOPROFILE_G729 ((__u32) 0x00000001)
#define SND_AUDIOMODE_G729_ANNEX_A ((__u32) 0x00000001)
#define SND_AUDIOMODE_G729_ANNEX_B ((__u32) 0x00000002)
/* <FIXME: multichannel encoders aren't supported for now. Would need
an additional definition of channel arrangement> */
@@ -220,13 +232,13 @@
/* Encoder options */
struct wmaEncoderOptions {
struct snd_enc_wma {
__u32 super_block_align; /* WMA Type-specific data */
};
/**
* struct vorbisEncoderOptions
* struct snd_enc_vorbis
* @quality: Sets encoding quality to n, between -1 (low) and 10 (high).
* In the default mode of operation, the quality level is 3.
* Normal quality range is 0 - 10.
@@ -234,52 +246,53 @@ struct wmaEncoderOptions {
* normal VBR encoding, but allows hard or soft bitrate constraints to be
* enforced by the encoder. This mode can be slower, and may also be
* lower quality. It is primarily useful for streaming.
* @maxBitrate: enabled only is managed is TRUE
* @minBitrate: enabled only is managed is TRUE
* @max_bit_rate: Enabled only if managed is TRUE
* @min_bit_rate: Enabled only if managed is TRUE
* @downmix: Boolean. Downmix input from stereo to mono (has no effect on
* non-stereo streams). Useful for lower-bitrate encoding.
*
* These options were extracted from the OpenMAX IL spec and gstreamer vorbisenc
* These options were extracted from the OpenMAX IL spec and Gstreamer vorbisenc
* properties
*
* For best quality users should specify VBR mode and set quality levels.
*/
struct vorbisEncoderOptions {
struct snd_enc_vorbis {
int quality;
__u32 managed;
__u32 maxBitrate;
__u32 minBirate;
__u32 max_bit_rate;
__u32 min_bit_rate;
__u32 downmix;
};
/**
* struct realEncoderOptions
* @coupling_quant_bits: is the number of coupling quantization bits in the stream
* @coupling_start_region: is the coupling start region in the stream
* @num_regions: is the number of regions value
* struct snd_enc_real
* @quant_bits: number of coupling quantization bits in the stream
* @start_region: coupling start region in the stream
* @num_regions: number of regions value
*
* These options were extracted from the OpenMAX IL spec
*/
struct realEncoderOptions {
__u32 coupling_quant_bits;
__u32 coupling_start_region;
struct snd_enc_real {
__u32 quant_bits;
__u32 start_region;
__u32 num_regions;
};
/**
* struct flacEncoderOptions
* @serialNumber: valid only for OGG formats, needs to be set by application
* @replayGain: Add ReplayGain tags
* struct snd_enc_flac
* @num: serial number, valid only for OGG formats
* needs to be set by application
* @gain: Add replay gain tags
*
* These options were extracted from the FLAC online documentation
* at http://flac.sourceforge.net/documentation_tools_flac.html
*
* To make the API simpler, it is assumed that the user will select quality
* profiles. Additional options that affect encoding quality and speed can
* be added at a later stage if need be.
* be added at a later stage if needed.
*
* By default the Subset format is used by encoders.
*
@@ -287,110 +300,93 @@ struct realEncoderOptions {
* not supported in this API.
*/
struct flacEncoderOptions {
__u32 serialNumber;
__u32 replayGain;
struct snd_enc_flac {
__u32 num;
__u32 gain;
};
struct genericEncoderOptions {
__u32 encoderBandwidth;
struct snd_enc_generic {
__u32 bw; /* encoder bandwidth */
int reserved[15];
};
union AudioCodecOptions {
struct wmaEncoderOptions wmaSpecificOptions;
struct vorbisEncoderOptions vorbisSpecificOptions;
struct realEncoderOptions realSpecificOptions;
struct flacEncoderOptions flacEncoderOptions;
struct genericEncoderOptions genericOptions;
union snd_codec_options {
struct snd_enc_wma wma;
struct snd_enc_vorbis vorbis;
struct snd_enc_real real;
struct snd_enc_flac flac;
struct snd_enc_generic generic;
};
/** struct SndAudioCodecDescriptor - description of codec capabilities
* @maxChannels: maximum number of audio channels
* @minBitsPerSample: Minimum bits per sample of PCM data <FIXME: needed?>
* @maxBitsPerSample: Maximum bits per sample of PCM data <FIXME: needed?>
* @minSampleRate: Minimum sampling rate supported, unit is Hz
* @maxSampleRate: Minimum sampling rate supported, unit is Hz
* @isFreqRangeContinuous: TRUE if the device supports a continuous range of
* sampling rates between minSampleRate and maxSampleRate;
* otherwise FALSE <FIXME: needed?>
* @SampleRatesSupported: Indexed array containing supported sampling rates in Hz
* @numSampleRatesSupported: Size of the pSamplesRatesSupported array
* @minBitRate: Minimum bitrate in bits per second
* @maxBitRate: Max bitrate in bits per second
* @isBitrateRangeContinuous: TRUE if the device supports a continuous range of
* bitrates between minBitRate and maxBitRate; otherwise FALSE
* @BitratesSupported: Indexed array containing supported bit rates
* @numBitratesSupported: Size of the pBiratesSupported array
* @rateControlSupported: value is specified by SND_RATECONTROLMODE defines.
* @profileSetting: Profile supported. See SND_AUDIOPROFILE defines.
* @modeSetting: Mode supported. See SND_AUDIOMODE defines
* @streamFormat: Format supported. See SND_AUDIOSTREAMFORMAT defines
/** struct snd_codec_desc - description of codec capabilities
* @max_ch: Maximum number of audio channels
* @sample_rates: Sampling rates in Hz, use SNDRV_PCM_RATE_xxx for this
* @bit_rate: Indexed array containing supported bit rates
* @num_bitrates: Number of valid values in bit_rate array
* @rate_control: value is specified by SND_RATECONTROLMODE defines.
* @profiles: Supported profiles. See SND_AUDIOPROFILE defines.
* @modes: Supported modes. See SND_AUDIOMODE defines
* @formats: Supported formats. See SND_AUDIOSTREAMFORMAT defines
* @reserved: reserved for future use
*
* This structure provides a scalar value for profile, mode and stream format fields.
* If an implementation supports multiple combinations, they will be listed as codecs
* with different IDs, for example there would be 2 decoders for AAC-RAW and AAC-ADTS.
* This entails some redundancy but makes it easier to avoid invalid configurations.
* This structure provides a scalar value for profiles, modes and stream
* format fields.
* If an implementation supports multiple combinations, they will be listed as
* codecs with different descriptors, for example there would be 2 descriptors
* for AAC-RAW and AAC-ADTS.
* This entails some redundancy but makes it easier to avoid invalid
* configurations.
*
*/
struct SndAudioCodecDescriptor {
__u32 maxChannels;
__u32 minBitsPerSample;
__u32 maxBitsPerSample;
__u32 minSampleRate;
__u32 maxSampleRate;
__u32 isFreqRangeContinuous;
__u32 sampleRatesSupported[MAX_NUM_RATES];
__u32 numSampleRatesSupported;
__u32 minBitRate;
__u32 maxBitRate;
__u32 isBitrateRangeContinuous;
__u32 bitratesSupported[MAX_NUM_BITRATES];
__u32 numBitratesSupported;
__u32 rateControlSupported;
__u32 profileSetting;
__u32 modeSetting;
__u32 streamFormat;
struct snd_codec_desc {
__u32 max_ch;
__u32 sample_rates;
__u32 bit_rate[MAX_NUM_BITRATES];
__u32 num_bitrates;
__u32 rate_control;
__u32 profiles;
__u32 modes;
__u32 formats;
__u32 reserved[16];
};
/** struct SndAudioCodecSettings -
* @codecId: Identifies the supported audio encoder/decoder. See SND_AUDIOCODEC macros.
* @channelsIn: Number of input audio channels
* @channelsOut: Number of output channels. In case of contradiction between this field and the
* channelMode field, the channelMode field overrides
* @sampleRate: Audio sample rate of input data
* @bitRate: Bitrate of encoded data. May be ignored by decoders
* @bitsPerSample: <FIXME: Needed? DSP implementations can handle their own format>
* @rateControl: Encoding rate control. See SND_RATECONTROLMODE defines.
/** struct snd_codec
* @id: Identifies the supported audio encoder/decoder.
* See SND_AUDIOCODEC macros.
* @ch_in: Number of input audio channels
* @ch_out: Number of output channels. In case of contradiction between
* this field and the channelMode field, the channelMode field
* overrides.
* @sample_rate: Audio sample rate of input data
* @bit_rate: Bitrate of encoded data. May be ignored by decoders
* @rate_control: Encoding rate control. See SND_RATECONTROLMODE defines.
* Encoders may rely on profiles for quality levels.
* May be ignored by decoders.
* @profileSetting: Mandatory for encoders, can be mandatory for specific decoders as well.
* See SND_AUDIOPROFILE defines
* @levelSetting: Supported level (Only used by WMA at the moment)
* @channelMode: Channel mode for encoder. See SND_AUDIOCHANMODE defines
* @streamFormat: Format of encoded bistream. Mandatory when defined. See SND_AUDIOSTREAMFORMAT
* defines
* @blockAlignment: Block alignment in bytes of an audio sample. Only required for PCM or IEC formats
* @profile: Mandatory for encoders, can be mandatory for specific
* decoders as well. See SND_AUDIOPROFILE defines.
* @level: Supported level (Only used by WMA at the moment)
* @ch_mode: Channel mode for encoder. See SND_AUDIOCHANMODE defines
* @format: Format of encoded bistream. Mandatory when defined.
* See SND_AUDIOSTREAMFORMAT defines.
* @align: Block alignment in bytes of an audio sample.
* Only required for PCM or IEC formats.
* @options: encoder-specific settings
* @reserved: reserved for future use
*/
struct SndAudioCodecSettings {
__u32 codecId;
__u32 channelsIn;
__u32 channelsOut;
__u32 sampleRate;
__u32 bitRate;
__u32 bitsPerSample;
__u32 rateControl;
__u32 profileSetting;
__u32 levelSetting;
__u32 channelMode;
__u32 streamFormat;
__u32 blockAlignment;
union AudioCodecOptions options;
struct snd_codec {
__u32 id;
__u32 ch_in;
__u32 ch_out;
__u32 sample_rate;
__u32 bit_rate;
__u32 rate_control;
__u32 profile;
__u32 level;
__u32 ch_mode;
__u32 format;
__u32 align;
union snd_codec_options options;
__u32 reserved[3];
};

View File

@@ -0,0 +1,658 @@
/*
* core.c - compress offload core
*
* Copyright (C) 2011 Intel Corporation
* Authors: Vinod Koul <vinod.koul@linux.intel.com>
* Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*/
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/uio.h>
#include <linux/uaccess.h>
#include <sound/snd_compress_params.h>
#include <sound/compress_offload.h>
#include <sound/compress_driver.h>
/* TODO:
* - Integrate with alsa, compressed devices should register as alsa devices
* as /dev/snd_compr_xxx
* - Integrate with ASoC:
* Opening compressed path should also start the codec dai
* TBD how the cpu dai will be viewed and started.
* ASoC should always be optional part
* (we should be able to use this framework in non asoc systems
* - Multiple node representation
* driver should be able to register multiple nodes
* - Version numbering for API
*/
static DEFINE_MUTEX(device_mutex);
static LIST_HEAD(device_list);
static LIST_HEAD(misc_list);
/*
* currently we are using misc device for registration and exposing ioctls
* this is temporary and will be moved to snd
* the device should be registered as /dev/snd_compr.....
*/
struct snd_compr_misc {
struct miscdevice misc;
struct list_head list;
struct snd_compr *compr;
};
struct snd_ioctl_data {
struct snd_compr_misc *misc;
unsigned long caps;
unsigned int minor;
struct snd_compr_stream stream;
};
static struct snd_compr_misc *snd_compr_get_device(unsigned int minor)
{
struct snd_compr_misc *misc;
list_for_each_entry(misc, &misc_list, list) {
if (minor == misc->misc.minor)
return misc;
}
return NULL;
}
static int snd_compr_open(struct inode *inode, struct file *f)
{
unsigned int minor = iminor(inode);
struct snd_compr_misc *misc = snd_compr_get_device(minor);
struct snd_ioctl_data *data;
struct snd_compr_runtime *runtime;
unsigned int direction;
int ret;
mutex_lock(&device_mutex);
if (f->f_flags & O_WRONLY)
direction = SNDRV_PCM_STREAM_PLAYBACK;
else {
ret = -ENXIO;
goto out;
}
/* curently only encoded playback is supported, above needs to be
* removed once we have recording support */
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto out;
}
data->misc = misc;
data->minor = minor;
data->stream.ops = misc->compr->ops;
data->stream.direction = direction;
data->stream.private_data = misc->compr->private_data;
data->stream.device = misc->compr;
runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
if (!runtime) {
ret = -ENOMEM;
kfree(data);
goto out;
}
runtime->state = SNDRV_PCM_STATE_OPEN;
init_waitqueue_head(&runtime->sleep);
data->stream.runtime = runtime;
f->private_data = (void *)data;
ret = misc->compr->ops->open(&data->stream);
if (ret) {
kfree(runtime);
kfree(data);
goto out;
}
out:
mutex_unlock(&device_mutex);
return ret;
}
static int snd_compr_free(struct inode *inode, struct file *f)
{
struct snd_ioctl_data *data = f->private_data;
mutex_lock(&device_mutex);
data->stream.ops->free(&data->stream);
kfree(data->stream.runtime->buffer);
kfree(data->stream.runtime);
kfree(data);
mutex_unlock(&device_mutex);
return 0;
}
static void snd_compr_update_tstamp(struct snd_compr_stream *stream,
struct snd_compr_tstamp *tstamp)
{
stream->ops->pointer(stream, tstamp);
stream->runtime->hw_pointer = tstamp->copied_bytes;
}
static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
struct snd_compr_avail *avail)
{
size_t avail_calc;
snd_compr_update_tstamp(stream, &avail->tstamp);
avail_calc = stream->runtime->app_pointer - stream->runtime->hw_pointer;
if (avail_calc < 0)
avail_calc = stream->runtime->buffer_size + avail_calc;
avail->avail = avail_calc;
return avail_calc;
}
static size_t snd_compr_get_avail(struct snd_compr_stream *stream)
{
struct snd_compr_avail avail;
return snd_compr_calc_avail(stream, &avail);
}
static int
snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
{
struct snd_compr_avail ioctl_avail;
snd_compr_calc_avail(stream, &ioctl_avail);
if (copy_to_user((unsigned long __user *)arg, &ioctl_avail, sizeof(ioctl_avail)))
return -EFAULT;
return 0;
}
static int snd_compr_write_data(struct snd_compr_stream *stream,
const char __user *buf, size_t count)
{
void *dstn;
size_t copy;
dstn = stream->runtime->buffer + stream->runtime->app_pointer;
if (count < stream->runtime->buffer_size - stream->runtime->app_pointer) {
if (copy_from_user(dstn, buf, count))
return -EFAULT;
stream->runtime->app_pointer += count;
} else {
copy = stream->runtime->buffer_size - stream->runtime->app_pointer;
if (copy_from_user(dstn, buf, copy))
return -EFAULT;
if (copy_from_user(stream->runtime->buffer, buf + copy, count - copy))
return -EFAULT;
stream->runtime->app_pointer = count - copy;
}
/* if DSP cares, let it know data has been written */
if (stream->ops->ack)
stream->ops->ack(stream);
return count;
}
static ssize_t snd_compr_write(struct file *f, const char __user *buf,
size_t count, loff_t *offset)
{
struct snd_ioctl_data *data = f->private_data;
struct snd_compr_stream *stream;
size_t avail;
int retval;
BUG_ON(!data);
stream = &data->stream;
mutex_lock(&stream->device->lock);
/* write is allowed when stream is running or has been steup */
if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
mutex_unlock(&stream->device->lock);
return -EPERM;
}
avail = snd_compr_get_avail(stream);
/* calculate how much we can write to buffer */
if (avail > count)
avail = count;
if (stream->ops->copy)
retval = stream->ops->copy(stream, buf, avail);
else
retval = snd_compr_write_data(stream, buf, avail);
/* while initiating the stream, write should be called before START
* call, so in setup move state */
if (stream->runtime->state == SNDRV_PCM_STATE_SETUP)
stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
mutex_unlock(&stream->device->lock);
return retval;
}
static ssize_t snd_compr_read(struct file *f, char __user *buf,
size_t count, loff_t *offset)
{
return -ENXIO;
}
static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
{
return -ENXIO;
}
unsigned int snd_compr_poll(struct file *f, poll_table *wait)
{
struct snd_ioctl_data *data = f->private_data;
struct snd_compr_stream *stream;
int retval = 0;
BUG_ON(!data);
stream = &data->stream;
mutex_lock(&stream->device->lock);
if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
retval = -ENXIO;
goto out;
}
poll_wait(f, &stream->runtime->sleep, wait);
/* this would change after read is implemented, we would need to
* check for direction here */
if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
retval = POLLOUT | POLLWRNORM;
out:
mutex_unlock(&stream->device->lock);
return retval;
}
void snd_compr_fragment_elapsed(struct snd_compr_stream *stream)
{
size_t avail;
if (stream->direction != SNDRV_PCM_STREAM_PLAYBACK)
return;
avail = snd_compr_get_avail(stream);
if (avail >= stream->runtime->fragment_size)
wake_up(&stream->runtime->sleep);
}
EXPORT_SYMBOL_GPL(snd_compr_fragment_elapsed);
void snd_compr_frame_elapsed(struct snd_compr_stream *stream)
{
size_t avail;
if (stream->direction != SNDRV_PCM_STREAM_CAPTURE)
return;
avail = snd_compr_get_avail(stream);
if (avail)
wake_up(&stream->runtime->sleep);
}
EXPORT_SYMBOL_GPL(snd_compr_frame_elapsed);
static int snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)
{
int retval;
struct snd_compr_caps caps;
if (!stream->ops->get_caps)
return -ENXIO;
retval = stream->ops->get_caps(stream, &caps);
if (retval)
goto out;
if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
retval = -EFAULT;
out:
return retval;
}
static int snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
{
int retval;
struct snd_compr_codec_caps *caps;
if (!stream->ops->get_codec_caps)
return -ENXIO;
caps = kmalloc(sizeof(*caps), GFP_KERNEL);
if (!caps)
return -ENOMEM;
retval = stream->ops->get_codec_caps(stream, caps);
if (retval)
goto out;
if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
retval = -EFAULT;
out:
kfree(caps);
return retval;
}
/* revisit this with snd_pcm_preallocate_xxx */
static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
struct snd_compr_params *params)
{
unsigned int buffer_size;
void *buffer;
buffer_size = params->buffer.fragment_size * params->buffer.fragments;
if (stream->ops->copy) {
buffer = NULL;
/* if copy is defined the driver will be required to copy
* the data from core
*/
} else {
buffer = kmalloc(buffer_size, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
}
stream->runtime->fragment_size = params->buffer.fragment_size;
stream->runtime->fragments = params->buffer.fragments;
stream->runtime->buffer = buffer;
stream->runtime->buffer_size = buffer_size;
return 0;
}
static int snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
{
struct snd_compr_params *params;
int retval;
if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
/*
* we should allow parameter change only when stream has been
* opened not in other cases
*/
params = kmalloc(sizeof(*params), GFP_KERNEL);
if (!params)
return -ENOMEM;
if (copy_from_user(params, (void __user *)arg, sizeof(*params)))
return -EFAULT;
retval = snd_compr_allocate_buffer(stream, params);
if (retval) {
kfree(params);
return -ENOMEM;
}
retval = stream->ops->set_params(stream, params);
if (retval)
goto out;
stream->runtime->state = SNDRV_PCM_STATE_SETUP;
} else
return -EPERM;
out:
kfree(params);
return retval;
}
static int snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
{
struct snd_compr_params *params;
int retval;
if (!stream->ops->get_params)
return -ENXIO;
params = kmalloc(sizeof(*params), GFP_KERNEL);
if (!params)
return -ENOMEM;
retval = stream->ops->get_params(stream, params);
if (retval)
goto out;
if (copy_to_user((char __user *)arg, params, sizeof(*params)))
retval = -EFAULT;
out:
kfree(params);
return retval;
}
static int snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
{
struct snd_compr_tstamp tstamp;
snd_compr_update_tstamp(stream, &tstamp);
if (copy_to_user((struct snd_compr_tstamp __user *)arg, &tstamp, sizeof(tstamp)))
return -EFAULT;
return 0;
}
static int snd_compr_pause(struct snd_compr_stream *stream)
{
int retval;
if (stream->runtime->state == SNDRV_PCM_STATE_PAUSED)
return 0;
retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
if (!retval) {
stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
wake_up(&stream->runtime->sleep);
}
return retval;
}
static int snd_compr_resume(struct snd_compr_stream *stream)
{
int retval;
if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
return -EPERM;
retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
if (!retval)
stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
return retval;
}
static int snd_compr_start(struct snd_compr_stream *stream)
{
int retval;
if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED)
return -EPERM;
retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
if (!retval)
stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
return retval;
}
static int snd_compr_stop(struct snd_compr_stream *stream)
{
int retval;
if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED)
return -EPERM;
retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
if (!retval) {
stream->runtime->state = SNDRV_PCM_STATE_SETUP;
wake_up(&stream->runtime->sleep);
}
return retval;
}
static int snd_compr_drain(struct snd_compr_stream *stream)
{
int retval;
if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED ||
stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
return -EPERM;
retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
if (!retval) {
stream->runtime->state = SNDRV_PCM_STATE_SETUP;
wake_up(&stream->runtime->sleep);
}
return retval;
}
static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
{
struct snd_ioctl_data *data = f->private_data;
struct snd_compr_stream *stream;
int retval = -ENOTTY;
BUG_ON(!data);
stream = &data->stream;
mutex_lock(&stream->device->lock);
switch (_IOC_NR(cmd)) {
case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
retval = snd_compr_get_caps(stream, arg);
break;
case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
retval = snd_compr_get_codec_caps(stream, arg);
break;
case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
retval = snd_compr_set_params(stream, arg);
break;
case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
retval = snd_compr_get_params(stream, arg);
break;
case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
retval = snd_compr_tstamp(stream, arg);
break;
case _IOC_NR(SNDRV_COMPRESS_AVAIL):
retval = snd_compr_ioctl_avail(stream, arg);
case _IOC_NR(SNDRV_COMPRESS_PAUSE):
retval = snd_compr_pause(stream);
break;
case _IOC_NR(SNDRV_COMPRESS_RESUME):
retval = snd_compr_resume(stream);
break;
case _IOC_NR(SNDRV_COMPRESS_START):
retval = snd_compr_start(stream);
break;
case _IOC_NR(SNDRV_COMPRESS_STOP):
retval = snd_compr_stop(stream);
break;
case _IOC_NR(SNDRV_COMPRESS_DRAIN):
cmd = SND_COMPR_TRIGGER_DRAIN;
retval = snd_compr_drain(stream);
break;
}
mutex_unlock(&stream->device->lock);
return retval;
}
static const struct file_operations snd_comp_file = {
.owner = THIS_MODULE,
.open = snd_compr_open,
.release = snd_compr_free,
.read = snd_compr_read,
.write = snd_compr_write,
.unlocked_ioctl = snd_compr_ioctl,
.mmap = snd_compr_mmap,
.poll = snd_compr_poll,
};
static int snd_compress_add_device(struct snd_compr *device)
{
int ret;
struct snd_compr_misc *misc = kzalloc(sizeof(*misc), GFP_KERNEL);
misc->misc.name = device->name;
misc->misc.fops = &snd_comp_file;
misc->misc.minor = MISC_DYNAMIC_MINOR;
misc->compr = device;
ret = misc_register(&misc->misc);
if (ret) {
pr_err("couldn't register misc device\n");
kfree(misc);
} else {
pr_debug("Got minor %d\n", misc->misc.minor);
list_add_tail(&misc->list, &misc_list);
}
return ret;
}
static int snd_compress_remove_device(struct snd_compr *device)
{
struct snd_compr_misc *misc, *__misc;
list_for_each_entry_safe(misc, __misc, &misc_list, list) {
if (device == misc->compr) {
misc_deregister(&misc->misc);
list_del(&device->list);
kfree(misc);
}
}
return 0;
}
/**
* snd_compress_register - register compressed device
*
* @device: compressed device to register
*/
int snd_compress_register(struct snd_compr *device)
{
int retval;
if (device->name == NULL || device->dev == NULL || device->ops == NULL)
return -EINVAL;
BUG_ON(!device->ops->open);
BUG_ON(!device->ops->free);
BUG_ON(!device->ops->set_params);
BUG_ON(!device->ops->get_params);
BUG_ON(!device->ops->trigger);
BUG_ON(!device->ops->pointer);
BUG_ON(!device->ops->get_caps);
BUG_ON(!device->ops->get_codec_caps);
INIT_LIST_HEAD(&device->list);
/* todo register the compressed streams */
/* todo integrate with asoc */
/* register a compressed card TBD if this needs change */
pr_debug("Registering compressed device %s\n", device->name);
mutex_lock(&device_mutex);
/* register a msic device for now */
retval = snd_compress_add_device(device);
if (!retval)
list_add_tail(&device->list, &device_list);
mutex_unlock(&device_mutex);
return retval;
}
EXPORT_SYMBOL_GPL(snd_compress_register);
int snd_compress_deregister(struct snd_compr *device)
{
pr_debug("Removing compressed device %s\n", device->name);
mutex_lock(&device_mutex);
snd_compress_remove_device(device);
list_del(&device->list);
mutex_unlock(&device_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_compress_deregister);
static int __init snd_compress_init(void)
{
return 0;
}
static void __exit snd_compress_exit(void)
{
}
module_init(snd_compress_init);
module_exit(snd_compress_exit);