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:
committed by
QuIC Gerrit Code Review
commit
6886534566
@@ -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
|
||||
|
||||
150
include/sound/compress_driver.h
Normal file
150
include/sound/compress_driver.h
Normal 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
|
||||
137
include/sound/compress_offload.h
Normal file
137
include/sound/compress_offload.h
Normal 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
|
||||
@@ -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
|
||||
@@ -189,7 +190,7 @@
|
||||
/*
|
||||
* 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
|
||||
* 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];
|
||||
};
|
||||
|
||||
658
sound/compress_offload/core.c
Normal file
658
sound/compress_offload/core.c
Normal 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);
|
||||
Reference in New Issue
Block a user