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