diff --git a/Documentation/sound/alsa/compress/snd_compress_data.txt b/Documentation/sound/alsa/compress/snd_compress_data.txt index a8e6762bad7..98e2cc98c34 100644 --- a/Documentation/sound/alsa/compress/snd_compress_data.txt +++ b/Documentation/sound/alsa/compress/snd_compress_data.txt @@ -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 diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h new file mode 100644 index 00000000000..2fd1604cc86 --- /dev/null +++ b/include/sound/compress_driver.h @@ -0,0 +1,150 @@ +/* + * compress_driver.h - compress offload driver definations + * + * Copyright (C) 2011 Intel Corporation + * Authors: Vinod Koul + * Pierre-Louis Bossart + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 +#include +#include + +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 diff --git a/include/sound/compress_offload.h b/include/sound/compress_offload.h new file mode 100644 index 00000000000..423264ddf31 --- /dev/null +++ b/include/sound/compress_offload.h @@ -0,0 +1,137 @@ +/* + * compress_offload.h - compress offload header definations + * + * Copyright (C) 2011 Intel Corporation + * Authors: Vinod Koul + * Pierre-Louis Bossart + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 +/** + * 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 diff --git a/include/sound/snd_compress_params.h b/include/sound/snd_compress_params.h index 7203e5fdaa4..5080e13d1b5 100644 --- a/include/sound/snd_compress_params.h +++ b/include/sound/snd_compress_params.h @@ -3,7 +3,7 @@ * streaming interface * * Copyright (C) 2011 Intel Corporation - * Authors: Pierre-Louis.Bossart + * Authors: Pierre-Louis Bossart * Vinod Koul * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -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) + /* */ @@ -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 - * @maxBitsPerSample: Maximum bits per sample of PCM data - * @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 - * @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: - * @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]; }; diff --git a/sound/compress_offload/core.c b/sound/compress_offload/core.c new file mode 100644 index 00000000000..987594a6b48 --- /dev/null +++ b/sound/compress_offload/core.c @@ -0,0 +1,658 @@ +/* + * core.c - compress offload core + * + * Copyright (C) 2011 Intel Corporation + * Authors: Vinod Koul + * Pierre-Louis Bossart + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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);