Commit 9727b490e543de956b8ba356e2d5499097d0b7a2
Committed by
Takashi Iwai
1 parent
8be69efacd
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
ALSA: compress: add support for gapless playback
this add new API for sound compress to support gapless playback. As noted in Documentation change, we add API to send metadata of encoder and padding delay to DSP. Also add API for indicating EOF and switching to subsequent track Also bump the compress API version Signed-off-by: Jeeja KP <jeeja.kp@intel.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Showing 4 changed files with 180 additions and 1 deletions Inline Diff
Documentation/sound/alsa/compress_offload.txt
1 | compress_offload.txt | 1 | compress_offload.txt |
2 | ===================== | 2 | ===================== |
3 | Pierre-Louis.Bossart <pierre-louis.bossart@linux.intel.com> | 3 | Pierre-Louis.Bossart <pierre-louis.bossart@linux.intel.com> |
4 | Vinod Koul <vinod.koul@linux.intel.com> | 4 | Vinod Koul <vinod.koul@linux.intel.com> |
5 | 5 | ||
6 | Overview | 6 | Overview |
7 | 7 | ||
8 | Since its early days, the ALSA API was defined with PCM support or | 8 | Since its early days, the ALSA API was defined with PCM support or |
9 | constant bitrates payloads such as IEC61937 in mind. Arguments and | 9 | constant bitrates payloads such as IEC61937 in mind. Arguments and |
10 | returned values in frames are the norm, making it a challenge to | 10 | returned values in frames are the norm, making it a challenge to |
11 | extend the existing API to compressed data streams. | 11 | extend the existing API to compressed data streams. |
12 | 12 | ||
13 | In recent years, audio digital signal processors (DSP) were integrated | 13 | In recent years, audio digital signal processors (DSP) were integrated |
14 | in system-on-chip designs, and DSPs are also integrated in audio | 14 | in system-on-chip designs, and DSPs are also integrated in audio |
15 | codecs. Processing compressed data on such DSPs results in a dramatic | 15 | codecs. Processing compressed data on such DSPs results in a dramatic |
16 | reduction of power consumption compared to host-based | 16 | reduction of power consumption compared to host-based |
17 | processing. Support for such hardware has not been very good in Linux, | 17 | processing. Support for such hardware has not been very good in Linux, |
18 | mostly because of a lack of a generic API available in the mainline | 18 | mostly because of a lack of a generic API available in the mainline |
19 | kernel. | 19 | kernel. |
20 | 20 | ||
21 | Rather than requiring a compatibility break with an API change of the | 21 | Rather than requiring a compatibility break with an API change of the |
22 | ALSA PCM interface, a new 'Compressed Data' API is introduced to | 22 | ALSA PCM interface, a new 'Compressed Data' API is introduced to |
23 | provide a control and data-streaming interface for audio DSPs. | 23 | provide a control and data-streaming interface for audio DSPs. |
24 | 24 | ||
25 | The design of this API was inspired by the 2-year experience with the | 25 | The design of this API was inspired by the 2-year experience with the |
26 | Intel Moorestown SOC, with many corrections required to upstream the | 26 | Intel Moorestown SOC, with many corrections required to upstream the |
27 | API in the mainline kernel instead of the staging tree and make it | 27 | API in the mainline kernel instead of the staging tree and make it |
28 | usable by others. | 28 | usable by others. |
29 | 29 | ||
30 | Requirements | 30 | Requirements |
31 | 31 | ||
32 | The main requirements are: | 32 | The main requirements are: |
33 | 33 | ||
34 | - separation between byte counts and time. Compressed formats may have | 34 | - separation between byte counts and time. Compressed formats may have |
35 | a header per file, per frame, or no header at all. The payload size | 35 | a header per file, per frame, or no header at all. The payload size |
36 | may vary from frame-to-frame. As a result, it is not possible to | 36 | may vary from frame-to-frame. As a result, it is not possible to |
37 | estimate reliably the duration of audio buffers when handling | 37 | estimate reliably the duration of audio buffers when handling |
38 | compressed data. Dedicated mechanisms are required to allow for | 38 | compressed data. Dedicated mechanisms are required to allow for |
39 | reliable audio-video synchronization, which requires precise | 39 | reliable audio-video synchronization, which requires precise |
40 | reporting of the number of samples rendered at any given time. | 40 | reporting of the number of samples rendered at any given time. |
41 | 41 | ||
42 | - Handling of multiple formats. PCM data only requires a specification | 42 | - Handling of multiple formats. PCM data only requires a specification |
43 | of the sampling rate, number of channels and bits per sample. In | 43 | of the sampling rate, number of channels and bits per sample. In |
44 | contrast, compressed data comes in a variety of formats. Audio DSPs | 44 | contrast, compressed data comes in a variety of formats. Audio DSPs |
45 | may also provide support for a limited number of audio encoders and | 45 | may also provide support for a limited number of audio encoders and |
46 | decoders embedded in firmware, or may support more choices through | 46 | decoders embedded in firmware, or may support more choices through |
47 | dynamic download of libraries. | 47 | dynamic download of libraries. |
48 | 48 | ||
49 | - Focus on main formats. This API provides support for the most | 49 | - Focus on main formats. This API provides support for the most |
50 | popular formats used for audio and video capture and playback. It is | 50 | popular formats used for audio and video capture and playback. It is |
51 | likely that as audio compression technology advances, new formats | 51 | likely that as audio compression technology advances, new formats |
52 | will be added. | 52 | will be added. |
53 | 53 | ||
54 | - Handling of multiple configurations. Even for a given format like | 54 | - Handling of multiple configurations. Even for a given format like |
55 | AAC, some implementations may support AAC multichannel but HE-AAC | 55 | AAC, some implementations may support AAC multichannel but HE-AAC |
56 | stereo. Likewise WMA10 level M3 may require too much memory and cpu | 56 | stereo. Likewise WMA10 level M3 may require too much memory and cpu |
57 | cycles. The new API needs to provide a generic way of listing these | 57 | cycles. The new API needs to provide a generic way of listing these |
58 | formats. | 58 | formats. |
59 | 59 | ||
60 | - Rendering/Grabbing only. This API does not provide any means of | 60 | - Rendering/Grabbing only. This API does not provide any means of |
61 | hardware acceleration, where PCM samples are provided back to | 61 | hardware acceleration, where PCM samples are provided back to |
62 | user-space for additional processing. This API focuses instead on | 62 | user-space for additional processing. This API focuses instead on |
63 | streaming compressed data to a DSP, with the assumption that the | 63 | streaming compressed data to a DSP, with the assumption that the |
64 | decoded samples are routed to a physical output or logical back-end. | 64 | decoded samples are routed to a physical output or logical back-end. |
65 | 65 | ||
66 | - Complexity hiding. Existing user-space multimedia frameworks all | 66 | - Complexity hiding. Existing user-space multimedia frameworks all |
67 | have existing enums/structures for each compressed format. This new | 67 | have existing enums/structures for each compressed format. This new |
68 | API assumes the existence of a platform-specific compatibility layer | 68 | API assumes the existence of a platform-specific compatibility layer |
69 | to expose, translate and make use of the capabilities of the audio | 69 | to expose, translate and make use of the capabilities of the audio |
70 | DSP, eg. Android HAL or PulseAudio sinks. By construction, regular | 70 | DSP, eg. Android HAL or PulseAudio sinks. By construction, regular |
71 | applications are not supposed to make use of this API. | 71 | applications are not supposed to make use of this API. |
72 | 72 | ||
73 | 73 | ||
74 | Design | 74 | Design |
75 | 75 | ||
76 | The new API shares a number of concepts with with the PCM API for flow | 76 | The new API shares a number of concepts with with the PCM API for flow |
77 | control. Start, pause, resume, drain and stop commands have the same | 77 | control. Start, pause, resume, drain and stop commands have the same |
78 | semantics no matter what the content is. | 78 | semantics no matter what the content is. |
79 | 79 | ||
80 | The concept of memory ring buffer divided in a set of fragments is | 80 | The concept of memory ring buffer divided in a set of fragments is |
81 | borrowed from the ALSA PCM API. However, only sizes in bytes can be | 81 | borrowed from the ALSA PCM API. However, only sizes in bytes can be |
82 | specified. | 82 | specified. |
83 | 83 | ||
84 | Seeks/trick modes are assumed to be handled by the host. | 84 | Seeks/trick modes are assumed to be handled by the host. |
85 | 85 | ||
86 | The notion of rewinds/forwards is not supported. Data committed to the | 86 | The notion of rewinds/forwards is not supported. Data committed to the |
87 | ring buffer cannot be invalidated, except when dropping all buffers. | 87 | ring buffer cannot be invalidated, except when dropping all buffers. |
88 | 88 | ||
89 | The Compressed Data API does not make any assumptions on how the data | 89 | The Compressed Data API does not make any assumptions on how the data |
90 | is transmitted to the audio DSP. DMA transfers from main memory to an | 90 | is transmitted to the audio DSP. DMA transfers from main memory to an |
91 | embedded audio cluster or to a SPI interface for external DSPs are | 91 | embedded audio cluster or to a SPI interface for external DSPs are |
92 | possible. As in the ALSA PCM case, a core set of routines is exposed; | 92 | possible. As in the ALSA PCM case, a core set of routines is exposed; |
93 | each driver implementer will have to write support for a set of | 93 | each driver implementer will have to write support for a set of |
94 | mandatory routines and possibly make use of optional ones. | 94 | mandatory routines and possibly make use of optional ones. |
95 | 95 | ||
96 | The main additions are | 96 | The main additions are |
97 | 97 | ||
98 | - get_caps | 98 | - get_caps |
99 | This routine returns the list of audio formats supported. Querying the | 99 | This routine returns the list of audio formats supported. Querying the |
100 | codecs on a capture stream will return encoders, decoders will be | 100 | codecs on a capture stream will return encoders, decoders will be |
101 | listed for playback streams. | 101 | listed for playback streams. |
102 | 102 | ||
103 | - get_codec_caps For each codec, this routine returns a list of | 103 | - get_codec_caps For each codec, this routine returns a list of |
104 | capabilities. The intent is to make sure all the capabilities | 104 | capabilities. The intent is to make sure all the capabilities |
105 | correspond to valid settings, and to minimize the risks of | 105 | correspond to valid settings, and to minimize the risks of |
106 | configuration failures. For example, for a complex codec such as AAC, | 106 | configuration failures. For example, for a complex codec such as AAC, |
107 | the number of channels supported may depend on a specific profile. If | 107 | the number of channels supported may depend on a specific profile. If |
108 | the capabilities were exposed with a single descriptor, it may happen | 108 | the capabilities were exposed with a single descriptor, it may happen |
109 | that a specific combination of profiles/channels/formats may not be | 109 | that a specific combination of profiles/channels/formats may not be |
110 | supported. Likewise, embedded DSPs have limited memory and cpu cycles, | 110 | supported. Likewise, embedded DSPs have limited memory and cpu cycles, |
111 | it is likely that some implementations make the list of capabilities | 111 | it is likely that some implementations make the list of capabilities |
112 | dynamic and dependent on existing workloads. In addition to codec | 112 | dynamic and dependent on existing workloads. In addition to codec |
113 | settings, this routine returns the minimum buffer size handled by the | 113 | settings, this routine returns the minimum buffer size handled by the |
114 | implementation. This information can be a function of the DMA buffer | 114 | implementation. This information can be a function of the DMA buffer |
115 | sizes, the number of bytes required to synchronize, etc, and can be | 115 | sizes, the number of bytes required to synchronize, etc, and can be |
116 | used by userspace to define how much needs to be written in the ring | 116 | used by userspace to define how much needs to be written in the ring |
117 | buffer before playback can start. | 117 | buffer before playback can start. |
118 | 118 | ||
119 | - set_params | 119 | - set_params |
120 | This routine sets the configuration chosen for a specific codec. The | 120 | This routine sets the configuration chosen for a specific codec. The |
121 | most important field in the parameters is the codec type; in most | 121 | most important field in the parameters is the codec type; in most |
122 | cases decoders will ignore other fields, while encoders will strictly | 122 | cases decoders will ignore other fields, while encoders will strictly |
123 | comply to the settings | 123 | comply to the settings |
124 | 124 | ||
125 | - get_params | 125 | - get_params |
126 | This routines returns the actual settings used by the DSP. Changes to | 126 | This routines returns the actual settings used by the DSP. Changes to |
127 | the settings should remain the exception. | 127 | the settings should remain the exception. |
128 | 128 | ||
129 | - get_timestamp | 129 | - get_timestamp |
130 | The timestamp becomes a multiple field structure. It lists the number | 130 | The timestamp becomes a multiple field structure. It lists the number |
131 | of bytes transferred, the number of samples processed and the number | 131 | of bytes transferred, the number of samples processed and the number |
132 | of samples rendered/grabbed. All these values can be used to determine | 132 | of samples rendered/grabbed. All these values can be used to determine |
133 | the avarage bitrate, figure out if the ring buffer needs to be | 133 | the avarage bitrate, figure out if the ring buffer needs to be |
134 | refilled or the delay due to decoding/encoding/io on the DSP. | 134 | refilled or the delay due to decoding/encoding/io on the DSP. |
135 | 135 | ||
136 | Note that the list of codecs/profiles/modes was derived from the | 136 | Note that the list of codecs/profiles/modes was derived from the |
137 | OpenMAX AL specification instead of reinventing the wheel. | 137 | OpenMAX AL specification instead of reinventing the wheel. |
138 | Modifications include: | 138 | Modifications include: |
139 | - Addition of FLAC and IEC formats | 139 | - Addition of FLAC and IEC formats |
140 | - Merge of encoder/decoder capabilities | 140 | - Merge of encoder/decoder capabilities |
141 | - Profiles/modes listed as bitmasks to make descriptors more compact | 141 | - Profiles/modes listed as bitmasks to make descriptors more compact |
142 | - Addition of set_params for decoders (missing in OpenMAX AL) | 142 | - Addition of set_params for decoders (missing in OpenMAX AL) |
143 | - Addition of AMR/AMR-WB encoding modes (missing in OpenMAX AL) | 143 | - Addition of AMR/AMR-WB encoding modes (missing in OpenMAX AL) |
144 | - Addition of format information for WMA | 144 | - Addition of format information for WMA |
145 | - Addition of encoding options when required (derived from OpenMAX IL) | 145 | - Addition of encoding options when required (derived from OpenMAX IL) |
146 | - Addition of rateControlSupported (missing in OpenMAX AL) | 146 | - Addition of rateControlSupported (missing in OpenMAX AL) |
147 | 147 | ||
148 | Gapless Playback | ||
149 | ================ | ||
150 | When playing thru an album, the decoders have the ability to skip the encoder | ||
151 | delay and padding and directly move from one track content to another. The end | ||
152 | user can perceive this as gapless playback as we dont have silence while | ||
153 | switching from one track to another | ||
154 | |||
155 | Also, there might be low-intensity noises due to encoding. Perfect gapless is | ||
156 | difficult to reach with all types of compressed data, but works fine with most | ||
157 | music content. The decoder needs to know the encoder delay and encoder padding. | ||
158 | So we need to pass this to DSP. This metadata is extracted from ID3/MP4 headers | ||
159 | and are not present by default in the bitstream, hence the need for a new | ||
160 | interface to pass this information to the DSP. Also DSP and userspace needs to | ||
161 | switch from one track to another and start using data for second track. | ||
162 | |||
163 | The main additions are: | ||
164 | |||
165 | - set_metadata | ||
166 | This routine sets the encoder delay and encoder padding. This can be used by | ||
167 | decoder to strip the silence. This needs to be set before the data in the track | ||
168 | is written. | ||
169 | |||
170 | - set_next_track | ||
171 | This routine tells DSP that metadata and write operation sent after this would | ||
172 | correspond to subsequent track | ||
173 | |||
174 | - partial drain | ||
175 | This is called when end of file is reached. The userspace can inform DSP that | ||
176 | EOF is reached and now DSP can start skipping padding delay. Also next write | ||
177 | data would belong to next track | ||
178 | |||
179 | Sequence flow for gapless would be: | ||
180 | - Open | ||
181 | - Get caps / codec caps | ||
182 | - Set params | ||
183 | - Set metadata of the first track | ||
184 | - Fill data of the first track | ||
185 | - Trigger start | ||
186 | - User-space finished sending all, | ||
187 | - Indicaite next track data by sending set_next_track | ||
188 | - Set metadata of the next track | ||
189 | - then call partial_drain to flush most of buffer in DSP | ||
190 | - Fill data of the next track | ||
191 | - DSP switches to second track | ||
192 | (note: order for partial_drain and write for next track can be reversed as well) | ||
193 | |||
148 | Not supported: | 194 | Not supported: |
149 | 195 | ||
150 | - Support for VoIP/circuit-switched calls is not the target of this | 196 | - Support for VoIP/circuit-switched calls is not the target of this |
151 | API. Support for dynamic bit-rate changes would require a tight | 197 | API. Support for dynamic bit-rate changes would require a tight |
152 | coupling between the DSP and the host stack, limiting power savings. | 198 | coupling between the DSP and the host stack, limiting power savings. |
153 | 199 | ||
154 | - Packet-loss concealment is not supported. This would require an | 200 | - Packet-loss concealment is not supported. This would require an |
155 | additional interface to let the decoder synthesize data when frames | 201 | additional interface to let the decoder synthesize data when frames |
156 | are lost during transmission. This may be added in the future. | 202 | are lost during transmission. This may be added in the future. |
157 | 203 | ||
158 | - Volume control/routing is not handled by this API. Devices exposing a | 204 | - Volume control/routing is not handled by this API. Devices exposing a |
159 | compressed data interface will be considered as regular ALSA devices; | 205 | compressed data interface will be considered as regular ALSA devices; |
160 | volume changes and routing information will be provided with regular | 206 | volume changes and routing information will be provided with regular |
161 | ALSA kcontrols. | 207 | ALSA kcontrols. |
162 | 208 | ||
163 | - Embedded audio effects. Such effects should be enabled in the same | 209 | - Embedded audio effects. Such effects should be enabled in the same |
164 | manner, no matter if the input was PCM or compressed. | 210 | manner, no matter if the input was PCM or compressed. |
165 | 211 | ||
166 | - multichannel IEC encoding. Unclear if this is required. | 212 | - multichannel IEC encoding. Unclear if this is required. |
167 | 213 | ||
168 | - Encoding/decoding acceleration is not supported as mentioned | 214 | - Encoding/decoding acceleration is not supported as mentioned |
169 | above. It is possible to route the output of a decoder to a capture | 215 | above. It is possible to route the output of a decoder to a capture |
170 | stream, or even implement transcoding capabilities. This routing | 216 | stream, or even implement transcoding capabilities. This routing |
171 | would be enabled with ALSA kcontrols. | 217 | would be enabled with ALSA kcontrols. |
172 | 218 | ||
173 | - Audio policy/resource management. This API does not provide any | 219 | - Audio policy/resource management. This API does not provide any |
174 | hooks to query the utilization of the audio DSP, nor any premption | 220 | hooks to query the utilization of the audio DSP, nor any premption |
175 | mechanisms. | 221 | mechanisms. |
176 | 222 | ||
177 | - No notion of underun/overrun. Since the bytes written are compressed | 223 | - No notion of underun/overrun. Since the bytes written are compressed |
178 | in nature and data written/read doesn't translate directly to | 224 | in nature and data written/read doesn't translate directly to |
179 | rendered output in time, this does not deal with underrun/overun and | 225 | rendered output in time, this does not deal with underrun/overun and |
180 | maybe dealt in user-library | 226 | maybe dealt in user-library |
181 | 227 | ||
182 | Credits: | 228 | Credits: |
183 | - Mark Brown and Liam Girdwood for discussions on the need for this API | 229 | - Mark Brown and Liam Girdwood for discussions on the need for this API |
184 | - Harsha Priya for her work on intel_sst compressed API | 230 | - Harsha Priya for her work on intel_sst compressed API |
185 | - Rakesh Ughreja for valuable feedback | 231 | - Rakesh Ughreja for valuable feedback |
186 | - Sing Nallasellan, Sikkandar Madar and Prasanna Samaga for | 232 | - Sing Nallasellan, Sikkandar Madar and Prasanna Samaga for |
187 | demonstrating and quantifying the benefits of audio offload on a | 233 | demonstrating and quantifying the benefits of audio offload on a |
188 | real platform. | 234 | real platform. |
189 | 235 |
include/sound/compress_driver.h
1 | /* | 1 | /* |
2 | * compress_driver.h - compress offload driver definations | 2 | * compress_driver.h - compress offload driver definations |
3 | * | 3 | * |
4 | * Copyright (C) 2011 Intel Corporation | 4 | * Copyright (C) 2011 Intel Corporation |
5 | * Authors: Vinod Koul <vinod.koul@linux.intel.com> | 5 | * Authors: Vinod Koul <vinod.koul@linux.intel.com> |
6 | * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> | 6 | * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> |
7 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 7 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
8 | * | 8 | * |
9 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License as published by | 10 | * it under the terms of the GNU General Public License as published by |
11 | * the Free Software Foundation; version 2 of the License. | 11 | * the Free Software Foundation; version 2 of the License. |
12 | * | 12 | * |
13 | * This program is distributed in the hope that it will be useful, but | 13 | * This program is distributed in the hope that it will be useful, but |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * General Public License for more details. | 16 | * General Public License for more details. |
17 | * | 17 | * |
18 | * You should have received a copy of the GNU General Public License along | 18 | * You should have received a copy of the GNU General Public License along |
19 | * with this program; if not, write to the Free Software Foundation, Inc., | 19 | * with this program; if not, write to the Free Software Foundation, Inc., |
20 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | 20 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
21 | * | 21 | * |
22 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 22 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
23 | * | 23 | * |
24 | */ | 24 | */ |
25 | #ifndef __COMPRESS_DRIVER_H | 25 | #ifndef __COMPRESS_DRIVER_H |
26 | #define __COMPRESS_DRIVER_H | 26 | #define __COMPRESS_DRIVER_H |
27 | 27 | ||
28 | #include <linux/types.h> | 28 | #include <linux/types.h> |
29 | #include <linux/sched.h> | 29 | #include <linux/sched.h> |
30 | #include <sound/compress_offload.h> | 30 | #include <sound/compress_offload.h> |
31 | #include <sound/asound.h> | 31 | #include <sound/asound.h> |
32 | #include <sound/pcm.h> | 32 | #include <sound/pcm.h> |
33 | 33 | ||
34 | struct snd_compr_ops; | 34 | struct snd_compr_ops; |
35 | 35 | ||
36 | /** | 36 | /** |
37 | * struct snd_compr_runtime: runtime stream description | 37 | * struct snd_compr_runtime: runtime stream description |
38 | * @state: stream state | 38 | * @state: stream state |
39 | * @ops: pointer to DSP callbacks | 39 | * @ops: pointer to DSP callbacks |
40 | * @buffer: pointer to kernel buffer, valid only when not in mmap mode or | 40 | * @buffer: pointer to kernel buffer, valid only when not in mmap mode or |
41 | * DSP doesn't implement copy | 41 | * DSP doesn't implement copy |
42 | * @buffer_size: size of the above buffer | 42 | * @buffer_size: size of the above buffer |
43 | * @fragment_size: size of buffer fragment in bytes | 43 | * @fragment_size: size of buffer fragment in bytes |
44 | * @fragments: number of such fragments | 44 | * @fragments: number of such fragments |
45 | * @hw_pointer: offset of last location in buffer where DSP copied data | 45 | * @hw_pointer: offset of last location in buffer where DSP copied data |
46 | * @app_pointer: offset of last location in buffer where app wrote data | 46 | * @app_pointer: offset of last location in buffer where app wrote data |
47 | * @total_bytes_available: cumulative number of bytes made available in | 47 | * @total_bytes_available: cumulative number of bytes made available in |
48 | * the ring buffer | 48 | * the ring buffer |
49 | * @total_bytes_transferred: cumulative bytes transferred by offload DSP | 49 | * @total_bytes_transferred: cumulative bytes transferred by offload DSP |
50 | * @sleep: poll sleep | 50 | * @sleep: poll sleep |
51 | */ | 51 | */ |
52 | struct snd_compr_runtime { | 52 | struct snd_compr_runtime { |
53 | snd_pcm_state_t state; | 53 | snd_pcm_state_t state; |
54 | struct snd_compr_ops *ops; | 54 | struct snd_compr_ops *ops; |
55 | void *buffer; | 55 | void *buffer; |
56 | u64 buffer_size; | 56 | u64 buffer_size; |
57 | u32 fragment_size; | 57 | u32 fragment_size; |
58 | u32 fragments; | 58 | u32 fragments; |
59 | u64 hw_pointer; | 59 | u64 hw_pointer; |
60 | u64 app_pointer; | 60 | u64 app_pointer; |
61 | u64 total_bytes_available; | 61 | u64 total_bytes_available; |
62 | u64 total_bytes_transferred; | 62 | u64 total_bytes_transferred; |
63 | wait_queue_head_t sleep; | 63 | wait_queue_head_t sleep; |
64 | void *private_data; | 64 | void *private_data; |
65 | }; | 65 | }; |
66 | 66 | ||
67 | /** | 67 | /** |
68 | * struct snd_compr_stream: compressed stream | 68 | * struct snd_compr_stream: compressed stream |
69 | * @name: device name | 69 | * @name: device name |
70 | * @ops: pointer to DSP callbacks | 70 | * @ops: pointer to DSP callbacks |
71 | * @runtime: pointer to runtime structure | 71 | * @runtime: pointer to runtime structure |
72 | * @device: device pointer | 72 | * @device: device pointer |
73 | * @direction: stream direction, playback/recording | 73 | * @direction: stream direction, playback/recording |
74 | * @metadata_set: metadata set flag, true when set | ||
75 | * @next_track: has userspace signall next track transistion, true when set | ||
74 | * @private_data: pointer to DSP private data | 76 | * @private_data: pointer to DSP private data |
75 | */ | 77 | */ |
76 | struct snd_compr_stream { | 78 | struct snd_compr_stream { |
77 | const char *name; | 79 | const char *name; |
78 | struct snd_compr_ops *ops; | 80 | struct snd_compr_ops *ops; |
79 | struct snd_compr_runtime *runtime; | 81 | struct snd_compr_runtime *runtime; |
80 | struct snd_compr *device; | 82 | struct snd_compr *device; |
81 | enum snd_compr_direction direction; | 83 | enum snd_compr_direction direction; |
84 | bool metadata_set; | ||
85 | bool next_track; | ||
82 | void *private_data; | 86 | void *private_data; |
83 | }; | 87 | }; |
84 | 88 | ||
85 | /** | 89 | /** |
86 | * struct snd_compr_ops: compressed path DSP operations | 90 | * struct snd_compr_ops: compressed path DSP operations |
87 | * @open: Open the compressed stream | 91 | * @open: Open the compressed stream |
88 | * This callback is mandatory and shall keep dsp ready to receive the stream | 92 | * This callback is mandatory and shall keep dsp ready to receive the stream |
89 | * parameter | 93 | * parameter |
90 | * @free: Close the compressed stream, mandatory | 94 | * @free: Close the compressed stream, mandatory |
91 | * @set_params: Sets the compressed stream parameters, mandatory | 95 | * @set_params: Sets the compressed stream parameters, mandatory |
92 | * This can be called in during stream creation only to set codec params | 96 | * This can be called in during stream creation only to set codec params |
93 | * and the stream properties | 97 | * and the stream properties |
94 | * @get_params: retrieve the codec parameters, mandatory | 98 | * @get_params: retrieve the codec parameters, mandatory |
95 | * @trigger: Trigger operations like start, pause, resume, drain, stop. | 99 | * @trigger: Trigger operations like start, pause, resume, drain, stop. |
96 | * This callback is mandatory | 100 | * This callback is mandatory |
97 | * @pointer: Retrieve current h/w pointer information. Mandatory | 101 | * @pointer: Retrieve current h/w pointer information. Mandatory |
98 | * @copy: Copy the compressed data to/from userspace, Optional | 102 | * @copy: Copy the compressed data to/from userspace, Optional |
99 | * Can't be implemented if DSP supports mmap | 103 | * Can't be implemented if DSP supports mmap |
100 | * @mmap: DSP mmap method to mmap DSP memory | 104 | * @mmap: DSP mmap method to mmap DSP memory |
101 | * @ack: Ack for DSP when data is written to audio buffer, Optional | 105 | * @ack: Ack for DSP when data is written to audio buffer, Optional |
102 | * Not valid if copy is implemented | 106 | * Not valid if copy is implemented |
103 | * @get_caps: Retrieve DSP capabilities, mandatory | 107 | * @get_caps: Retrieve DSP capabilities, mandatory |
104 | * @get_codec_caps: Retrieve capabilities for a specific codec, mandatory | 108 | * @get_codec_caps: Retrieve capabilities for a specific codec, mandatory |
105 | */ | 109 | */ |
106 | struct snd_compr_ops { | 110 | struct snd_compr_ops { |
107 | int (*open)(struct snd_compr_stream *stream); | 111 | int (*open)(struct snd_compr_stream *stream); |
108 | int (*free)(struct snd_compr_stream *stream); | 112 | int (*free)(struct snd_compr_stream *stream); |
109 | int (*set_params)(struct snd_compr_stream *stream, | 113 | int (*set_params)(struct snd_compr_stream *stream, |
110 | struct snd_compr_params *params); | 114 | struct snd_compr_params *params); |
111 | int (*get_params)(struct snd_compr_stream *stream, | 115 | int (*get_params)(struct snd_compr_stream *stream, |
112 | struct snd_codec *params); | 116 | struct snd_codec *params); |
117 | int (*set_metadata)(struct snd_compr_stream *stream, | ||
118 | struct snd_compr_metadata *metadata); | ||
119 | int (*get_metadata)(struct snd_compr_stream *stream, | ||
120 | struct snd_compr_metadata *metadata); | ||
113 | int (*trigger)(struct snd_compr_stream *stream, int cmd); | 121 | int (*trigger)(struct snd_compr_stream *stream, int cmd); |
114 | int (*pointer)(struct snd_compr_stream *stream, | 122 | int (*pointer)(struct snd_compr_stream *stream, |
115 | struct snd_compr_tstamp *tstamp); | 123 | struct snd_compr_tstamp *tstamp); |
116 | int (*copy)(struct snd_compr_stream *stream, const char __user *buf, | 124 | int (*copy)(struct snd_compr_stream *stream, const char __user *buf, |
117 | size_t count); | 125 | size_t count); |
118 | int (*mmap)(struct snd_compr_stream *stream, | 126 | int (*mmap)(struct snd_compr_stream *stream, |
119 | struct vm_area_struct *vma); | 127 | struct vm_area_struct *vma); |
120 | int (*ack)(struct snd_compr_stream *stream, size_t bytes); | 128 | int (*ack)(struct snd_compr_stream *stream, size_t bytes); |
121 | int (*get_caps) (struct snd_compr_stream *stream, | 129 | int (*get_caps) (struct snd_compr_stream *stream, |
122 | struct snd_compr_caps *caps); | 130 | struct snd_compr_caps *caps); |
123 | int (*get_codec_caps) (struct snd_compr_stream *stream, | 131 | int (*get_codec_caps) (struct snd_compr_stream *stream, |
124 | struct snd_compr_codec_caps *codec); | 132 | struct snd_compr_codec_caps *codec); |
125 | }; | 133 | }; |
126 | 134 | ||
127 | /** | 135 | /** |
128 | * struct snd_compr: Compressed device | 136 | * struct snd_compr: Compressed device |
129 | * @name: DSP device name | 137 | * @name: DSP device name |
130 | * @dev: Device pointer | 138 | * @dev: Device pointer |
131 | * @ops: pointer to DSP callbacks | 139 | * @ops: pointer to DSP callbacks |
132 | * @private_data: pointer to DSP pvt data | 140 | * @private_data: pointer to DSP pvt data |
133 | * @card: sound card pointer | 141 | * @card: sound card pointer |
134 | * @direction: Playback or capture direction | 142 | * @direction: Playback or capture direction |
135 | * @lock: device lock | 143 | * @lock: device lock |
136 | * @device: device id | 144 | * @device: device id |
137 | */ | 145 | */ |
138 | struct snd_compr { | 146 | struct snd_compr { |
139 | const char *name; | 147 | const char *name; |
140 | struct device *dev; | 148 | struct device *dev; |
141 | struct snd_compr_ops *ops; | 149 | struct snd_compr_ops *ops; |
142 | void *private_data; | 150 | void *private_data; |
143 | struct snd_card *card; | 151 | struct snd_card *card; |
144 | unsigned int direction; | 152 | unsigned int direction; |
145 | struct mutex lock; | 153 | struct mutex lock; |
146 | int device; | 154 | int device; |
147 | }; | 155 | }; |
148 | 156 | ||
149 | /* compress device register APIs */ | 157 | /* compress device register APIs */ |
150 | int snd_compress_register(struct snd_compr *device); | 158 | int snd_compress_register(struct snd_compr *device); |
151 | int snd_compress_deregister(struct snd_compr *device); | 159 | int snd_compress_deregister(struct snd_compr *device); |
152 | int snd_compress_new(struct snd_card *card, int device, | 160 | int snd_compress_new(struct snd_card *card, int device, |
153 | int type, struct snd_compr *compr); | 161 | int type, struct snd_compr *compr); |
154 | 162 | ||
155 | /* dsp driver callback apis | 163 | /* dsp driver callback apis |
156 | * For playback: driver should call snd_compress_fragment_elapsed() to let the | 164 | * For playback: driver should call snd_compress_fragment_elapsed() to let the |
157 | * framework know that a fragment has been consumed from the ring buffer | 165 | * framework know that a fragment has been consumed from the ring buffer |
158 | * | 166 | * |
159 | * For recording: we want to know when a frame is available or when | 167 | * For recording: we want to know when a frame is available or when |
160 | * at least one frame is available so snd_compress_frame_elapsed() | 168 | * at least one frame is available so snd_compress_frame_elapsed() |
161 | * callback should be called when a encodeded frame is available | 169 | * callback should be called when a encodeded frame is available |
162 | */ | 170 | */ |
163 | static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream) | 171 | static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream) |
164 | { | 172 | { |
165 | wake_up(&stream->runtime->sleep); | 173 | wake_up(&stream->runtime->sleep); |
166 | } | 174 | } |
167 | 175 | ||
168 | #endif | 176 | #endif |
169 | 177 |
include/uapi/sound/compress_offload.h
1 | /* | 1 | /* |
2 | * compress_offload.h - compress offload header definations | 2 | * compress_offload.h - compress offload header definations |
3 | * | 3 | * |
4 | * Copyright (C) 2011 Intel Corporation | 4 | * Copyright (C) 2011 Intel Corporation |
5 | * Authors: Vinod Koul <vinod.koul@linux.intel.com> | 5 | * Authors: Vinod Koul <vinod.koul@linux.intel.com> |
6 | * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> | 6 | * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> |
7 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 7 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
8 | * | 8 | * |
9 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License as published by | 10 | * it under the terms of the GNU General Public License as published by |
11 | * the Free Software Foundation; version 2 of the License. | 11 | * the Free Software Foundation; version 2 of the License. |
12 | * | 12 | * |
13 | * This program is distributed in the hope that it will be useful, but | 13 | * This program is distributed in the hope that it will be useful, but |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * General Public License for more details. | 16 | * General Public License for more details. |
17 | * | 17 | * |
18 | * You should have received a copy of the GNU General Public License along | 18 | * You should have received a copy of the GNU General Public License along |
19 | * with this program; if not, write to the Free Software Foundation, Inc., | 19 | * with this program; if not, write to the Free Software Foundation, Inc., |
20 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | 20 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
21 | * | 21 | * |
22 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 22 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
23 | * | 23 | * |
24 | */ | 24 | */ |
25 | #ifndef __COMPRESS_OFFLOAD_H | 25 | #ifndef __COMPRESS_OFFLOAD_H |
26 | #define __COMPRESS_OFFLOAD_H | 26 | #define __COMPRESS_OFFLOAD_H |
27 | 27 | ||
28 | #include <linux/types.h> | 28 | #include <linux/types.h> |
29 | #include <sound/asound.h> | 29 | #include <sound/asound.h> |
30 | #include <sound/compress_params.h> | 30 | #include <sound/compress_params.h> |
31 | 31 | ||
32 | 32 | ||
33 | #define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 0) | 33 | #define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 1) |
34 | /** | 34 | /** |
35 | * struct snd_compressed_buffer: compressed buffer | 35 | * struct snd_compressed_buffer: compressed buffer |
36 | * @fragment_size: size of buffer fragment in bytes | 36 | * @fragment_size: size of buffer fragment in bytes |
37 | * @fragments: number of such fragments | 37 | * @fragments: number of such fragments |
38 | */ | 38 | */ |
39 | struct snd_compressed_buffer { | 39 | struct snd_compressed_buffer { |
40 | __u32 fragment_size; | 40 | __u32 fragment_size; |
41 | __u32 fragments; | 41 | __u32 fragments; |
42 | }; | 42 | }; |
43 | 43 | ||
44 | /** | 44 | /** |
45 | * struct snd_compr_params: compressed stream params | 45 | * struct snd_compr_params: compressed stream params |
46 | * @buffer: buffer description | 46 | * @buffer: buffer description |
47 | * @codec: codec parameters | 47 | * @codec: codec parameters |
48 | * @no_wake_mode: dont wake on fragment elapsed | 48 | * @no_wake_mode: dont wake on fragment elapsed |
49 | */ | 49 | */ |
50 | struct snd_compr_params { | 50 | struct snd_compr_params { |
51 | struct snd_compressed_buffer buffer; | 51 | struct snd_compressed_buffer buffer; |
52 | struct snd_codec codec; | 52 | struct snd_codec codec; |
53 | __u8 no_wake_mode; | 53 | __u8 no_wake_mode; |
54 | }; | 54 | }; |
55 | 55 | ||
56 | /** | 56 | /** |
57 | * struct snd_compr_tstamp: timestamp descriptor | 57 | * struct snd_compr_tstamp: timestamp descriptor |
58 | * @byte_offset: Byte offset in ring buffer to DSP | 58 | * @byte_offset: Byte offset in ring buffer to DSP |
59 | * @copied_total: Total number of bytes copied from/to ring buffer to/by DSP | 59 | * @copied_total: Total number of bytes copied from/to ring buffer to/by DSP |
60 | * @pcm_frames: Frames decoded or encoded by DSP. This field will evolve by | 60 | * @pcm_frames: Frames decoded or encoded by DSP. This field will evolve by |
61 | * large steps and should only be used to monitor encoding/decoding | 61 | * large steps and should only be used to monitor encoding/decoding |
62 | * progress. It shall not be used for timing estimates. | 62 | * progress. It shall not be used for timing estimates. |
63 | * @pcm_io_frames: Frames rendered or received by DSP into a mixer or an audio | 63 | * @pcm_io_frames: Frames rendered or received by DSP into a mixer or an audio |
64 | * output/input. This field should be used for A/V sync or time estimates. | 64 | * output/input. This field should be used for A/V sync or time estimates. |
65 | * @sampling_rate: sampling rate of audio | 65 | * @sampling_rate: sampling rate of audio |
66 | */ | 66 | */ |
67 | struct snd_compr_tstamp { | 67 | struct snd_compr_tstamp { |
68 | __u32 byte_offset; | 68 | __u32 byte_offset; |
69 | __u32 copied_total; | 69 | __u32 copied_total; |
70 | snd_pcm_uframes_t pcm_frames; | 70 | snd_pcm_uframes_t pcm_frames; |
71 | snd_pcm_uframes_t pcm_io_frames; | 71 | snd_pcm_uframes_t pcm_io_frames; |
72 | __u32 sampling_rate; | 72 | __u32 sampling_rate; |
73 | }; | 73 | }; |
74 | 74 | ||
75 | /** | 75 | /** |
76 | * struct snd_compr_avail: avail descriptor | 76 | * struct snd_compr_avail: avail descriptor |
77 | * @avail: Number of bytes available in ring buffer for writing/reading | 77 | * @avail: Number of bytes available in ring buffer for writing/reading |
78 | * @tstamp: timestamp infomation | 78 | * @tstamp: timestamp infomation |
79 | */ | 79 | */ |
80 | struct snd_compr_avail { | 80 | struct snd_compr_avail { |
81 | __u64 avail; | 81 | __u64 avail; |
82 | struct snd_compr_tstamp tstamp; | 82 | struct snd_compr_tstamp tstamp; |
83 | }; | 83 | }; |
84 | 84 | ||
85 | enum snd_compr_direction { | 85 | enum snd_compr_direction { |
86 | SND_COMPRESS_PLAYBACK = 0, | 86 | SND_COMPRESS_PLAYBACK = 0, |
87 | SND_COMPRESS_CAPTURE | 87 | SND_COMPRESS_CAPTURE |
88 | }; | 88 | }; |
89 | 89 | ||
90 | /** | 90 | /** |
91 | * struct snd_compr_caps: caps descriptor | 91 | * struct snd_compr_caps: caps descriptor |
92 | * @codecs: pointer to array of codecs | 92 | * @codecs: pointer to array of codecs |
93 | * @direction: direction supported. Of type snd_compr_direction | 93 | * @direction: direction supported. Of type snd_compr_direction |
94 | * @min_fragment_size: minimum fragment supported by DSP | 94 | * @min_fragment_size: minimum fragment supported by DSP |
95 | * @max_fragment_size: maximum fragment supported by DSP | 95 | * @max_fragment_size: maximum fragment supported by DSP |
96 | * @min_fragments: min fragments supported by DSP | 96 | * @min_fragments: min fragments supported by DSP |
97 | * @max_fragments: max fragments supported by DSP | 97 | * @max_fragments: max fragments supported by DSP |
98 | * @num_codecs: number of codecs supported | 98 | * @num_codecs: number of codecs supported |
99 | * @reserved: reserved field | 99 | * @reserved: reserved field |
100 | */ | 100 | */ |
101 | struct snd_compr_caps { | 101 | struct snd_compr_caps { |
102 | __u32 num_codecs; | 102 | __u32 num_codecs; |
103 | __u32 direction; | 103 | __u32 direction; |
104 | __u32 min_fragment_size; | 104 | __u32 min_fragment_size; |
105 | __u32 max_fragment_size; | 105 | __u32 max_fragment_size; |
106 | __u32 min_fragments; | 106 | __u32 min_fragments; |
107 | __u32 max_fragments; | 107 | __u32 max_fragments; |
108 | __u32 codecs[MAX_NUM_CODECS]; | 108 | __u32 codecs[MAX_NUM_CODECS]; |
109 | __u32 reserved[11]; | 109 | __u32 reserved[11]; |
110 | }; | 110 | }; |
111 | 111 | ||
112 | /** | 112 | /** |
113 | * struct snd_compr_codec_caps: query capability of codec | 113 | * struct snd_compr_codec_caps: query capability of codec |
114 | * @codec: codec for which capability is queried | 114 | * @codec: codec for which capability is queried |
115 | * @num_descriptors: number of codec descriptors | 115 | * @num_descriptors: number of codec descriptors |
116 | * @descriptor: array of codec capability descriptor | 116 | * @descriptor: array of codec capability descriptor |
117 | */ | 117 | */ |
118 | struct snd_compr_codec_caps { | 118 | struct snd_compr_codec_caps { |
119 | __u32 codec; | 119 | __u32 codec; |
120 | __u32 num_descriptors; | 120 | __u32 num_descriptors; |
121 | struct snd_codec_desc descriptor[MAX_NUM_CODEC_DESCRIPTORS]; | 121 | struct snd_codec_desc descriptor[MAX_NUM_CODEC_DESCRIPTORS]; |
122 | }; | 122 | }; |
123 | 123 | ||
124 | /** | 124 | /** |
125 | * @SNDRV_COMPRESS_ENCODER_PADDING: no of samples appended by the encoder at the | ||
126 | * end of the track | ||
127 | * @SNDRV_COMPRESS_ENCODER_DELAY: no of samples inserted by the encoder at the | ||
128 | * beginning of the track | ||
129 | */ | ||
130 | enum { | ||
131 | SNDRV_COMPRESS_ENCODER_PADDING = 1, | ||
132 | SNDRV_COMPRESS_ENCODER_DELAY = 2, | ||
133 | }; | ||
134 | |||
135 | /** | ||
136 | * struct snd_compr_metadata: compressed stream metadata | ||
137 | * @key: key id | ||
138 | * @value: key value | ||
139 | */ | ||
140 | struct snd_compr_metadata { | ||
141 | __u32 key; | ||
142 | __u32 value[8]; | ||
143 | }; | ||
144 | |||
145 | /** | ||
125 | * compress path ioctl definitions | 146 | * compress path ioctl definitions |
126 | * SNDRV_COMPRESS_GET_CAPS: Query capability of DSP | 147 | * SNDRV_COMPRESS_GET_CAPS: Query capability of DSP |
127 | * SNDRV_COMPRESS_GET_CODEC_CAPS: Query capability of a codec | 148 | * SNDRV_COMPRESS_GET_CODEC_CAPS: Query capability of a codec |
128 | * SNDRV_COMPRESS_SET_PARAMS: Set codec and stream parameters | 149 | * SNDRV_COMPRESS_SET_PARAMS: Set codec and stream parameters |
129 | * Note: only codec params can be changed runtime and stream params cant be | 150 | * Note: only codec params can be changed runtime and stream params cant be |
130 | * SNDRV_COMPRESS_GET_PARAMS: Query codec params | 151 | * SNDRV_COMPRESS_GET_PARAMS: Query codec params |
131 | * SNDRV_COMPRESS_TSTAMP: get the current timestamp value | 152 | * SNDRV_COMPRESS_TSTAMP: get the current timestamp value |
132 | * SNDRV_COMPRESS_AVAIL: get the current buffer avail value. | 153 | * SNDRV_COMPRESS_AVAIL: get the current buffer avail value. |
133 | * This also queries the tstamp properties | 154 | * This also queries the tstamp properties |
134 | * SNDRV_COMPRESS_PAUSE: Pause the running stream | 155 | * SNDRV_COMPRESS_PAUSE: Pause the running stream |
135 | * SNDRV_COMPRESS_RESUME: resume a paused stream | 156 | * SNDRV_COMPRESS_RESUME: resume a paused stream |
136 | * SNDRV_COMPRESS_START: Start a stream | 157 | * SNDRV_COMPRESS_START: Start a stream |
137 | * SNDRV_COMPRESS_STOP: stop a running stream, discarding ring buffer content | 158 | * SNDRV_COMPRESS_STOP: stop a running stream, discarding ring buffer content |
138 | * and the buffers currently with DSP | 159 | * and the buffers currently with DSP |
139 | * SNDRV_COMPRESS_DRAIN: Play till end of buffers and stop after that | 160 | * SNDRV_COMPRESS_DRAIN: Play till end of buffers and stop after that |
140 | * SNDRV_COMPRESS_IOCTL_VERSION: Query the API version | 161 | * SNDRV_COMPRESS_IOCTL_VERSION: Query the API version |
141 | */ | 162 | */ |
142 | #define SNDRV_COMPRESS_IOCTL_VERSION _IOR('C', 0x00, int) | 163 | #define SNDRV_COMPRESS_IOCTL_VERSION _IOR('C', 0x00, int) |
143 | #define SNDRV_COMPRESS_GET_CAPS _IOWR('C', 0x10, struct snd_compr_caps) | 164 | #define SNDRV_COMPRESS_GET_CAPS _IOWR('C', 0x10, struct snd_compr_caps) |
144 | #define SNDRV_COMPRESS_GET_CODEC_CAPS _IOWR('C', 0x11,\ | 165 | #define SNDRV_COMPRESS_GET_CODEC_CAPS _IOWR('C', 0x11,\ |
145 | struct snd_compr_codec_caps) | 166 | struct snd_compr_codec_caps) |
146 | #define SNDRV_COMPRESS_SET_PARAMS _IOW('C', 0x12, struct snd_compr_params) | 167 | #define SNDRV_COMPRESS_SET_PARAMS _IOW('C', 0x12, struct snd_compr_params) |
147 | #define SNDRV_COMPRESS_GET_PARAMS _IOR('C', 0x13, struct snd_codec) | 168 | #define SNDRV_COMPRESS_GET_PARAMS _IOR('C', 0x13, struct snd_codec) |
169 | #define SNDRV_COMPRESS_SET_METADATA _IOW('C', 0x14,\ | ||
170 | struct snd_compr_metadata) | ||
171 | #define SNDRV_COMPRESS_GET_METADATA _IOWR('C', 0x15,\ | ||
172 | struct snd_compr_metadata) | ||
148 | #define SNDRV_COMPRESS_TSTAMP _IOR('C', 0x20, struct snd_compr_tstamp) | 173 | #define SNDRV_COMPRESS_TSTAMP _IOR('C', 0x20, struct snd_compr_tstamp) |
149 | #define SNDRV_COMPRESS_AVAIL _IOR('C', 0x21, struct snd_compr_avail) | 174 | #define SNDRV_COMPRESS_AVAIL _IOR('C', 0x21, struct snd_compr_avail) |
150 | #define SNDRV_COMPRESS_PAUSE _IO('C', 0x30) | 175 | #define SNDRV_COMPRESS_PAUSE _IO('C', 0x30) |
151 | #define SNDRV_COMPRESS_RESUME _IO('C', 0x31) | 176 | #define SNDRV_COMPRESS_RESUME _IO('C', 0x31) |
152 | #define SNDRV_COMPRESS_START _IO('C', 0x32) | 177 | #define SNDRV_COMPRESS_START _IO('C', 0x32) |
153 | #define SNDRV_COMPRESS_STOP _IO('C', 0x33) | 178 | #define SNDRV_COMPRESS_STOP _IO('C', 0x33) |
154 | #define SNDRV_COMPRESS_DRAIN _IO('C', 0x34) | 179 | #define SNDRV_COMPRESS_DRAIN _IO('C', 0x34) |
180 | #define SNDRV_COMPRESS_NEXT_TRACK _IO('C', 0x35) | ||
181 | #define SNDRV_COMPRESS_PARTIAL_DRAIN _IO('C', 0x36) | ||
155 | /* | 182 | /* |
156 | * TODO | 183 | * TODO |
157 | * 1. add mmap support | 184 | * 1. add mmap support |
158 | * | 185 | * |
159 | */ | 186 | */ |
160 | #define SND_COMPR_TRIGGER_DRAIN 7 /*FIXME move this to pcm.h */ | 187 | #define SND_COMPR_TRIGGER_DRAIN 7 /*FIXME move this to pcm.h */ |
188 | #define SND_COMPR_TRIGGER_NEXT_TRACK 8 | ||
189 | #define SND_COMPR_TRIGGER_PARTIAL_DRAIN 9 | ||
161 | #endif | 190 | #endif |
162 | 191 |
sound/core/compress_offload.c
1 | /* | 1 | /* |
2 | * compress_core.c - compress offload core | 2 | * compress_core.c - compress offload core |
3 | * | 3 | * |
4 | * Copyright (C) 2011 Intel Corporation | 4 | * Copyright (C) 2011 Intel Corporation |
5 | * Authors: Vinod Koul <vinod.koul@linux.intel.com> | 5 | * Authors: Vinod Koul <vinod.koul@linux.intel.com> |
6 | * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> | 6 | * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> |
7 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 7 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
8 | * | 8 | * |
9 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License as published by | 10 | * it under the terms of the GNU General Public License as published by |
11 | * the Free Software Foundation; version 2 of the License. | 11 | * the Free Software Foundation; version 2 of the License. |
12 | * | 12 | * |
13 | * This program is distributed in the hope that it will be useful, but | 13 | * This program is distributed in the hope that it will be useful, but |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * General Public License for more details. | 16 | * General Public License for more details. |
17 | * | 17 | * |
18 | * You should have received a copy of the GNU General Public License along | 18 | * You should have received a copy of the GNU General Public License along |
19 | * with this program; if not, write to the Free Software Foundation, Inc., | 19 | * with this program; if not, write to the Free Software Foundation, Inc., |
20 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | 20 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
21 | * | 21 | * |
22 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 22 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
23 | * | 23 | * |
24 | */ | 24 | */ |
25 | #define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__ | 25 | #define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__ |
26 | #define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt) | 26 | #define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt) |
27 | 27 | ||
28 | #include <linux/file.h> | 28 | #include <linux/file.h> |
29 | #include <linux/fs.h> | 29 | #include <linux/fs.h> |
30 | #include <linux/list.h> | 30 | #include <linux/list.h> |
31 | #include <linux/mm.h> | 31 | #include <linux/mm.h> |
32 | #include <linux/mutex.h> | 32 | #include <linux/mutex.h> |
33 | #include <linux/poll.h> | 33 | #include <linux/poll.h> |
34 | #include <linux/slab.h> | 34 | #include <linux/slab.h> |
35 | #include <linux/sched.h> | 35 | #include <linux/sched.h> |
36 | #include <linux/uio.h> | 36 | #include <linux/uio.h> |
37 | #include <linux/uaccess.h> | 37 | #include <linux/uaccess.h> |
38 | #include <linux/module.h> | 38 | #include <linux/module.h> |
39 | #include <sound/core.h> | 39 | #include <sound/core.h> |
40 | #include <sound/initval.h> | 40 | #include <sound/initval.h> |
41 | #include <sound/compress_params.h> | 41 | #include <sound/compress_params.h> |
42 | #include <sound/compress_offload.h> | 42 | #include <sound/compress_offload.h> |
43 | #include <sound/compress_driver.h> | 43 | #include <sound/compress_driver.h> |
44 | 44 | ||
45 | /* TODO: | 45 | /* TODO: |
46 | * - add substream support for multiple devices in case of | 46 | * - add substream support for multiple devices in case of |
47 | * SND_DYNAMIC_MINORS is not used | 47 | * SND_DYNAMIC_MINORS is not used |
48 | * - Multiple node representation | 48 | * - Multiple node representation |
49 | * driver should be able to register multiple nodes | 49 | * driver should be able to register multiple nodes |
50 | */ | 50 | */ |
51 | 51 | ||
52 | static DEFINE_MUTEX(device_mutex); | 52 | static DEFINE_MUTEX(device_mutex); |
53 | 53 | ||
54 | struct snd_compr_file { | 54 | struct snd_compr_file { |
55 | unsigned long caps; | 55 | unsigned long caps; |
56 | struct snd_compr_stream stream; | 56 | struct snd_compr_stream stream; |
57 | }; | 57 | }; |
58 | 58 | ||
59 | /* | 59 | /* |
60 | * a note on stream states used: | 60 | * a note on stream states used: |
61 | * we use follwing states in the compressed core | 61 | * we use follwing states in the compressed core |
62 | * SNDRV_PCM_STATE_OPEN: When stream has been opened. | 62 | * SNDRV_PCM_STATE_OPEN: When stream has been opened. |
63 | * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by | 63 | * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by |
64 | * calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this | 64 | * calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this |
65 | * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain. | 65 | * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain. |
66 | * SNDRV_PCM_STATE_RUNNING: When stream has been started and is | 66 | * SNDRV_PCM_STATE_RUNNING: When stream has been started and is |
67 | * decoding/encoding and rendering/capturing data. | 67 | * decoding/encoding and rendering/capturing data. |
68 | * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done | 68 | * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done |
69 | * by calling SNDRV_COMPRESS_DRAIN. | 69 | * by calling SNDRV_COMPRESS_DRAIN. |
70 | * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling | 70 | * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling |
71 | * SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling | 71 | * SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling |
72 | * SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively. | 72 | * SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively. |
73 | */ | 73 | */ |
74 | static int snd_compr_open(struct inode *inode, struct file *f) | 74 | static int snd_compr_open(struct inode *inode, struct file *f) |
75 | { | 75 | { |
76 | struct snd_compr *compr; | 76 | struct snd_compr *compr; |
77 | struct snd_compr_file *data; | 77 | struct snd_compr_file *data; |
78 | struct snd_compr_runtime *runtime; | 78 | struct snd_compr_runtime *runtime; |
79 | enum snd_compr_direction dirn; | 79 | enum snd_compr_direction dirn; |
80 | int maj = imajor(inode); | 80 | int maj = imajor(inode); |
81 | int ret; | 81 | int ret; |
82 | 82 | ||
83 | if ((f->f_flags & O_ACCMODE) == O_WRONLY) | 83 | if ((f->f_flags & O_ACCMODE) == O_WRONLY) |
84 | dirn = SND_COMPRESS_PLAYBACK; | 84 | dirn = SND_COMPRESS_PLAYBACK; |
85 | else if ((f->f_flags & O_ACCMODE) == O_RDONLY) | 85 | else if ((f->f_flags & O_ACCMODE) == O_RDONLY) |
86 | dirn = SND_COMPRESS_CAPTURE; | 86 | dirn = SND_COMPRESS_CAPTURE; |
87 | else | 87 | else |
88 | return -EINVAL; | 88 | return -EINVAL; |
89 | 89 | ||
90 | if (maj == snd_major) | 90 | if (maj == snd_major) |
91 | compr = snd_lookup_minor_data(iminor(inode), | 91 | compr = snd_lookup_minor_data(iminor(inode), |
92 | SNDRV_DEVICE_TYPE_COMPRESS); | 92 | SNDRV_DEVICE_TYPE_COMPRESS); |
93 | else | 93 | else |
94 | return -EBADFD; | 94 | return -EBADFD; |
95 | 95 | ||
96 | if (compr == NULL) { | 96 | if (compr == NULL) { |
97 | pr_err("no device data!!!\n"); | 97 | pr_err("no device data!!!\n"); |
98 | return -ENODEV; | 98 | return -ENODEV; |
99 | } | 99 | } |
100 | 100 | ||
101 | if (dirn != compr->direction) { | 101 | if (dirn != compr->direction) { |
102 | pr_err("this device doesn't support this direction\n"); | 102 | pr_err("this device doesn't support this direction\n"); |
103 | snd_card_unref(compr->card); | 103 | snd_card_unref(compr->card); |
104 | return -EINVAL; | 104 | return -EINVAL; |
105 | } | 105 | } |
106 | 106 | ||
107 | data = kzalloc(sizeof(*data), GFP_KERNEL); | 107 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
108 | if (!data) { | 108 | if (!data) { |
109 | snd_card_unref(compr->card); | 109 | snd_card_unref(compr->card); |
110 | return -ENOMEM; | 110 | return -ENOMEM; |
111 | } | 111 | } |
112 | data->stream.ops = compr->ops; | 112 | data->stream.ops = compr->ops; |
113 | data->stream.direction = dirn; | 113 | data->stream.direction = dirn; |
114 | data->stream.private_data = compr->private_data; | 114 | data->stream.private_data = compr->private_data; |
115 | data->stream.device = compr; | 115 | data->stream.device = compr; |
116 | runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); | 116 | runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); |
117 | if (!runtime) { | 117 | if (!runtime) { |
118 | kfree(data); | 118 | kfree(data); |
119 | snd_card_unref(compr->card); | 119 | snd_card_unref(compr->card); |
120 | return -ENOMEM; | 120 | return -ENOMEM; |
121 | } | 121 | } |
122 | runtime->state = SNDRV_PCM_STATE_OPEN; | 122 | runtime->state = SNDRV_PCM_STATE_OPEN; |
123 | init_waitqueue_head(&runtime->sleep); | 123 | init_waitqueue_head(&runtime->sleep); |
124 | data->stream.runtime = runtime; | 124 | data->stream.runtime = runtime; |
125 | f->private_data = (void *)data; | 125 | f->private_data = (void *)data; |
126 | mutex_lock(&compr->lock); | 126 | mutex_lock(&compr->lock); |
127 | ret = compr->ops->open(&data->stream); | 127 | ret = compr->ops->open(&data->stream); |
128 | mutex_unlock(&compr->lock); | 128 | mutex_unlock(&compr->lock); |
129 | if (ret) { | 129 | if (ret) { |
130 | kfree(runtime); | 130 | kfree(runtime); |
131 | kfree(data); | 131 | kfree(data); |
132 | } | 132 | } |
133 | snd_card_unref(compr->card); | 133 | snd_card_unref(compr->card); |
134 | return 0; | 134 | return 0; |
135 | } | 135 | } |
136 | 136 | ||
137 | static int snd_compr_free(struct inode *inode, struct file *f) | 137 | static int snd_compr_free(struct inode *inode, struct file *f) |
138 | { | 138 | { |
139 | struct snd_compr_file *data = f->private_data; | 139 | struct snd_compr_file *data = f->private_data; |
140 | data->stream.ops->free(&data->stream); | 140 | data->stream.ops->free(&data->stream); |
141 | kfree(data->stream.runtime->buffer); | 141 | kfree(data->stream.runtime->buffer); |
142 | kfree(data->stream.runtime); | 142 | kfree(data->stream.runtime); |
143 | kfree(data); | 143 | kfree(data); |
144 | return 0; | 144 | return 0; |
145 | } | 145 | } |
146 | 146 | ||
147 | static int snd_compr_update_tstamp(struct snd_compr_stream *stream, | 147 | static int snd_compr_update_tstamp(struct snd_compr_stream *stream, |
148 | struct snd_compr_tstamp *tstamp) | 148 | struct snd_compr_tstamp *tstamp) |
149 | { | 149 | { |
150 | if (!stream->ops->pointer) | 150 | if (!stream->ops->pointer) |
151 | return -ENOTSUPP; | 151 | return -ENOTSUPP; |
152 | stream->ops->pointer(stream, tstamp); | 152 | stream->ops->pointer(stream, tstamp); |
153 | pr_debug("dsp consumed till %d total %d bytes\n", | 153 | pr_debug("dsp consumed till %d total %d bytes\n", |
154 | tstamp->byte_offset, tstamp->copied_total); | 154 | tstamp->byte_offset, tstamp->copied_total); |
155 | stream->runtime->hw_pointer = tstamp->byte_offset; | 155 | stream->runtime->hw_pointer = tstamp->byte_offset; |
156 | stream->runtime->total_bytes_transferred = tstamp->copied_total; | 156 | stream->runtime->total_bytes_transferred = tstamp->copied_total; |
157 | return 0; | 157 | return 0; |
158 | } | 158 | } |
159 | 159 | ||
160 | static size_t snd_compr_calc_avail(struct snd_compr_stream *stream, | 160 | static size_t snd_compr_calc_avail(struct snd_compr_stream *stream, |
161 | struct snd_compr_avail *avail) | 161 | struct snd_compr_avail *avail) |
162 | { | 162 | { |
163 | long avail_calc; /*this needs to be signed variable */ | 163 | long avail_calc; /*this needs to be signed variable */ |
164 | 164 | ||
165 | memset(avail, 0, sizeof(*avail)); | 165 | memset(avail, 0, sizeof(*avail)); |
166 | snd_compr_update_tstamp(stream, &avail->tstamp); | 166 | snd_compr_update_tstamp(stream, &avail->tstamp); |
167 | /* Still need to return avail even if tstamp can't be filled in */ | 167 | /* Still need to return avail even if tstamp can't be filled in */ |
168 | 168 | ||
169 | /* FIXME: This needs to be different for capture stream, | 169 | /* FIXME: This needs to be different for capture stream, |
170 | available is # of compressed data, for playback it's | 170 | available is # of compressed data, for playback it's |
171 | remainder of buffer */ | 171 | remainder of buffer */ |
172 | 172 | ||
173 | if (stream->runtime->total_bytes_available == 0 && | 173 | if (stream->runtime->total_bytes_available == 0 && |
174 | stream->runtime->state == SNDRV_PCM_STATE_SETUP) { | 174 | stream->runtime->state == SNDRV_PCM_STATE_SETUP) { |
175 | pr_debug("detected init and someone forgot to do a write\n"); | 175 | pr_debug("detected init and someone forgot to do a write\n"); |
176 | return stream->runtime->buffer_size; | 176 | return stream->runtime->buffer_size; |
177 | } | 177 | } |
178 | pr_debug("app wrote %lld, DSP consumed %lld\n", | 178 | pr_debug("app wrote %lld, DSP consumed %lld\n", |
179 | stream->runtime->total_bytes_available, | 179 | stream->runtime->total_bytes_available, |
180 | stream->runtime->total_bytes_transferred); | 180 | stream->runtime->total_bytes_transferred); |
181 | if (stream->runtime->total_bytes_available == | 181 | if (stream->runtime->total_bytes_available == |
182 | stream->runtime->total_bytes_transferred) { | 182 | stream->runtime->total_bytes_transferred) { |
183 | pr_debug("both pointers are same, returning full avail\n"); | 183 | pr_debug("both pointers are same, returning full avail\n"); |
184 | return stream->runtime->buffer_size; | 184 | return stream->runtime->buffer_size; |
185 | } | 185 | } |
186 | 186 | ||
187 | /* FIXME: this routine isn't consistent, in one test we use | 187 | /* FIXME: this routine isn't consistent, in one test we use |
188 | * cumulative values and in the other byte offsets. Do we | 188 | * cumulative values and in the other byte offsets. Do we |
189 | * really need the byte offsets if the cumulative values have | 189 | * really need the byte offsets if the cumulative values have |
190 | * been updated? In the PCM interface app_ptr and hw_ptr are | 190 | * been updated? In the PCM interface app_ptr and hw_ptr are |
191 | * already cumulative */ | 191 | * already cumulative */ |
192 | 192 | ||
193 | avail_calc = stream->runtime->buffer_size - | 193 | avail_calc = stream->runtime->buffer_size - |
194 | (stream->runtime->app_pointer - stream->runtime->hw_pointer); | 194 | (stream->runtime->app_pointer - stream->runtime->hw_pointer); |
195 | pr_debug("calc avail as %ld, app_ptr %lld, hw+ptr %lld\n", avail_calc, | 195 | pr_debug("calc avail as %ld, app_ptr %lld, hw+ptr %lld\n", avail_calc, |
196 | stream->runtime->app_pointer, | 196 | stream->runtime->app_pointer, |
197 | stream->runtime->hw_pointer); | 197 | stream->runtime->hw_pointer); |
198 | if (avail_calc >= stream->runtime->buffer_size) | 198 | if (avail_calc >= stream->runtime->buffer_size) |
199 | avail_calc -= stream->runtime->buffer_size; | 199 | avail_calc -= stream->runtime->buffer_size; |
200 | pr_debug("ret avail as %ld\n", avail_calc); | 200 | pr_debug("ret avail as %ld\n", avail_calc); |
201 | avail->avail = avail_calc; | 201 | avail->avail = avail_calc; |
202 | return avail_calc; | 202 | return avail_calc; |
203 | } | 203 | } |
204 | 204 | ||
205 | static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream) | 205 | static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream) |
206 | { | 206 | { |
207 | struct snd_compr_avail avail; | 207 | struct snd_compr_avail avail; |
208 | 208 | ||
209 | return snd_compr_calc_avail(stream, &avail); | 209 | return snd_compr_calc_avail(stream, &avail); |
210 | } | 210 | } |
211 | 211 | ||
212 | static int | 212 | static int |
213 | snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg) | 213 | snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg) |
214 | { | 214 | { |
215 | struct snd_compr_avail ioctl_avail; | 215 | struct snd_compr_avail ioctl_avail; |
216 | size_t avail; | 216 | size_t avail; |
217 | 217 | ||
218 | avail = snd_compr_calc_avail(stream, &ioctl_avail); | 218 | avail = snd_compr_calc_avail(stream, &ioctl_avail); |
219 | ioctl_avail.avail = avail; | 219 | ioctl_avail.avail = avail; |
220 | 220 | ||
221 | if (copy_to_user((__u64 __user *)arg, | 221 | if (copy_to_user((__u64 __user *)arg, |
222 | &ioctl_avail, sizeof(ioctl_avail))) | 222 | &ioctl_avail, sizeof(ioctl_avail))) |
223 | return -EFAULT; | 223 | return -EFAULT; |
224 | return 0; | 224 | return 0; |
225 | } | 225 | } |
226 | 226 | ||
227 | static int snd_compr_write_data(struct snd_compr_stream *stream, | 227 | static int snd_compr_write_data(struct snd_compr_stream *stream, |
228 | const char __user *buf, size_t count) | 228 | const char __user *buf, size_t count) |
229 | { | 229 | { |
230 | void *dstn; | 230 | void *dstn; |
231 | size_t copy; | 231 | size_t copy; |
232 | struct snd_compr_runtime *runtime = stream->runtime; | 232 | struct snd_compr_runtime *runtime = stream->runtime; |
233 | 233 | ||
234 | dstn = runtime->buffer + runtime->app_pointer; | 234 | dstn = runtime->buffer + runtime->app_pointer; |
235 | pr_debug("copying %ld at %lld\n", | 235 | pr_debug("copying %ld at %lld\n", |
236 | (unsigned long)count, runtime->app_pointer); | 236 | (unsigned long)count, runtime->app_pointer); |
237 | if (count < runtime->buffer_size - runtime->app_pointer) { | 237 | if (count < runtime->buffer_size - runtime->app_pointer) { |
238 | if (copy_from_user(dstn, buf, count)) | 238 | if (copy_from_user(dstn, buf, count)) |
239 | return -EFAULT; | 239 | return -EFAULT; |
240 | runtime->app_pointer += count; | 240 | runtime->app_pointer += count; |
241 | } else { | 241 | } else { |
242 | copy = runtime->buffer_size - runtime->app_pointer; | 242 | copy = runtime->buffer_size - runtime->app_pointer; |
243 | if (copy_from_user(dstn, buf, copy)) | 243 | if (copy_from_user(dstn, buf, copy)) |
244 | return -EFAULT; | 244 | return -EFAULT; |
245 | if (copy_from_user(runtime->buffer, buf + copy, count - copy)) | 245 | if (copy_from_user(runtime->buffer, buf + copy, count - copy)) |
246 | return -EFAULT; | 246 | return -EFAULT; |
247 | runtime->app_pointer = count - copy; | 247 | runtime->app_pointer = count - copy; |
248 | } | 248 | } |
249 | /* if DSP cares, let it know data has been written */ | 249 | /* if DSP cares, let it know data has been written */ |
250 | if (stream->ops->ack) | 250 | if (stream->ops->ack) |
251 | stream->ops->ack(stream, count); | 251 | stream->ops->ack(stream, count); |
252 | return count; | 252 | return count; |
253 | } | 253 | } |
254 | 254 | ||
255 | static ssize_t snd_compr_write(struct file *f, const char __user *buf, | 255 | static ssize_t snd_compr_write(struct file *f, const char __user *buf, |
256 | size_t count, loff_t *offset) | 256 | size_t count, loff_t *offset) |
257 | { | 257 | { |
258 | struct snd_compr_file *data = f->private_data; | 258 | struct snd_compr_file *data = f->private_data; |
259 | struct snd_compr_stream *stream; | 259 | struct snd_compr_stream *stream; |
260 | size_t avail; | 260 | size_t avail; |
261 | int retval; | 261 | int retval; |
262 | 262 | ||
263 | if (snd_BUG_ON(!data)) | 263 | if (snd_BUG_ON(!data)) |
264 | return -EFAULT; | 264 | return -EFAULT; |
265 | 265 | ||
266 | stream = &data->stream; | 266 | stream = &data->stream; |
267 | mutex_lock(&stream->device->lock); | 267 | mutex_lock(&stream->device->lock); |
268 | /* write is allowed when stream is running or has been steup */ | 268 | /* write is allowed when stream is running or has been steup */ |
269 | if (stream->runtime->state != SNDRV_PCM_STATE_SETUP && | 269 | if (stream->runtime->state != SNDRV_PCM_STATE_SETUP && |
270 | stream->runtime->state != SNDRV_PCM_STATE_RUNNING) { | 270 | stream->runtime->state != SNDRV_PCM_STATE_RUNNING) { |
271 | mutex_unlock(&stream->device->lock); | 271 | mutex_unlock(&stream->device->lock); |
272 | return -EBADFD; | 272 | return -EBADFD; |
273 | } | 273 | } |
274 | 274 | ||
275 | avail = snd_compr_get_avail(stream); | 275 | avail = snd_compr_get_avail(stream); |
276 | pr_debug("avail returned %ld\n", (unsigned long)avail); | 276 | pr_debug("avail returned %ld\n", (unsigned long)avail); |
277 | /* calculate how much we can write to buffer */ | 277 | /* calculate how much we can write to buffer */ |
278 | if (avail > count) | 278 | if (avail > count) |
279 | avail = count; | 279 | avail = count; |
280 | 280 | ||
281 | if (stream->ops->copy) | 281 | if (stream->ops->copy) |
282 | retval = stream->ops->copy(stream, buf, avail); | 282 | retval = stream->ops->copy(stream, buf, avail); |
283 | else | 283 | else |
284 | retval = snd_compr_write_data(stream, buf, avail); | 284 | retval = snd_compr_write_data(stream, buf, avail); |
285 | if (retval > 0) | 285 | if (retval > 0) |
286 | stream->runtime->total_bytes_available += retval; | 286 | stream->runtime->total_bytes_available += retval; |
287 | 287 | ||
288 | /* while initiating the stream, write should be called before START | 288 | /* while initiating the stream, write should be called before START |
289 | * call, so in setup move state */ | 289 | * call, so in setup move state */ |
290 | if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) { | 290 | if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) { |
291 | stream->runtime->state = SNDRV_PCM_STATE_PREPARED; | 291 | stream->runtime->state = SNDRV_PCM_STATE_PREPARED; |
292 | pr_debug("stream prepared, Houston we are good to go\n"); | 292 | pr_debug("stream prepared, Houston we are good to go\n"); |
293 | } | 293 | } |
294 | 294 | ||
295 | mutex_unlock(&stream->device->lock); | 295 | mutex_unlock(&stream->device->lock); |
296 | return retval; | 296 | return retval; |
297 | } | 297 | } |
298 | 298 | ||
299 | 299 | ||
300 | static ssize_t snd_compr_read(struct file *f, char __user *buf, | 300 | static ssize_t snd_compr_read(struct file *f, char __user *buf, |
301 | size_t count, loff_t *offset) | 301 | size_t count, loff_t *offset) |
302 | { | 302 | { |
303 | return -ENXIO; | 303 | return -ENXIO; |
304 | } | 304 | } |
305 | 305 | ||
306 | static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma) | 306 | static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma) |
307 | { | 307 | { |
308 | return -ENXIO; | 308 | return -ENXIO; |
309 | } | 309 | } |
310 | 310 | ||
311 | static inline int snd_compr_get_poll(struct snd_compr_stream *stream) | 311 | static inline int snd_compr_get_poll(struct snd_compr_stream *stream) |
312 | { | 312 | { |
313 | if (stream->direction == SND_COMPRESS_PLAYBACK) | 313 | if (stream->direction == SND_COMPRESS_PLAYBACK) |
314 | return POLLOUT | POLLWRNORM; | 314 | return POLLOUT | POLLWRNORM; |
315 | else | 315 | else |
316 | return POLLIN | POLLRDNORM; | 316 | return POLLIN | POLLRDNORM; |
317 | } | 317 | } |
318 | 318 | ||
319 | static unsigned int snd_compr_poll(struct file *f, poll_table *wait) | 319 | static unsigned int snd_compr_poll(struct file *f, poll_table *wait) |
320 | { | 320 | { |
321 | struct snd_compr_file *data = f->private_data; | 321 | struct snd_compr_file *data = f->private_data; |
322 | struct snd_compr_stream *stream; | 322 | struct snd_compr_stream *stream; |
323 | size_t avail; | 323 | size_t avail; |
324 | int retval = 0; | 324 | int retval = 0; |
325 | 325 | ||
326 | if (snd_BUG_ON(!data)) | 326 | if (snd_BUG_ON(!data)) |
327 | return -EFAULT; | 327 | return -EFAULT; |
328 | stream = &data->stream; | 328 | stream = &data->stream; |
329 | if (snd_BUG_ON(!stream)) | 329 | if (snd_BUG_ON(!stream)) |
330 | return -EFAULT; | 330 | return -EFAULT; |
331 | 331 | ||
332 | mutex_lock(&stream->device->lock); | 332 | mutex_lock(&stream->device->lock); |
333 | if (stream->runtime->state == SNDRV_PCM_STATE_PAUSED || | 333 | if (stream->runtime->state == SNDRV_PCM_STATE_PAUSED || |
334 | stream->runtime->state == SNDRV_PCM_STATE_OPEN) { | 334 | stream->runtime->state == SNDRV_PCM_STATE_OPEN) { |
335 | retval = -EBADFD; | 335 | retval = -EBADFD; |
336 | goto out; | 336 | goto out; |
337 | } | 337 | } |
338 | poll_wait(f, &stream->runtime->sleep, wait); | 338 | poll_wait(f, &stream->runtime->sleep, wait); |
339 | 339 | ||
340 | avail = snd_compr_get_avail(stream); | 340 | avail = snd_compr_get_avail(stream); |
341 | pr_debug("avail is %ld\n", (unsigned long)avail); | 341 | pr_debug("avail is %ld\n", (unsigned long)avail); |
342 | /* check if we have at least one fragment to fill */ | 342 | /* check if we have at least one fragment to fill */ |
343 | switch (stream->runtime->state) { | 343 | switch (stream->runtime->state) { |
344 | case SNDRV_PCM_STATE_DRAINING: | 344 | case SNDRV_PCM_STATE_DRAINING: |
345 | /* stream has been woken up after drain is complete | 345 | /* stream has been woken up after drain is complete |
346 | * draining done so set stream state to stopped | 346 | * draining done so set stream state to stopped |
347 | */ | 347 | */ |
348 | retval = snd_compr_get_poll(stream); | 348 | retval = snd_compr_get_poll(stream); |
349 | stream->runtime->state = SNDRV_PCM_STATE_SETUP; | 349 | stream->runtime->state = SNDRV_PCM_STATE_SETUP; |
350 | break; | 350 | break; |
351 | case SNDRV_PCM_STATE_RUNNING: | 351 | case SNDRV_PCM_STATE_RUNNING: |
352 | case SNDRV_PCM_STATE_PREPARED: | 352 | case SNDRV_PCM_STATE_PREPARED: |
353 | case SNDRV_PCM_STATE_PAUSED: | 353 | case SNDRV_PCM_STATE_PAUSED: |
354 | if (avail >= stream->runtime->fragment_size) | 354 | if (avail >= stream->runtime->fragment_size) |
355 | retval = snd_compr_get_poll(stream); | 355 | retval = snd_compr_get_poll(stream); |
356 | break; | 356 | break; |
357 | default: | 357 | default: |
358 | if (stream->direction == SND_COMPRESS_PLAYBACK) | 358 | if (stream->direction == SND_COMPRESS_PLAYBACK) |
359 | retval = POLLOUT | POLLWRNORM | POLLERR; | 359 | retval = POLLOUT | POLLWRNORM | POLLERR; |
360 | else | 360 | else |
361 | retval = POLLIN | POLLRDNORM | POLLERR; | 361 | retval = POLLIN | POLLRDNORM | POLLERR; |
362 | break; | 362 | break; |
363 | } | 363 | } |
364 | out: | 364 | out: |
365 | mutex_unlock(&stream->device->lock); | 365 | mutex_unlock(&stream->device->lock); |
366 | return retval; | 366 | return retval; |
367 | } | 367 | } |
368 | 368 | ||
369 | static int | 369 | static int |
370 | snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg) | 370 | snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg) |
371 | { | 371 | { |
372 | int retval; | 372 | int retval; |
373 | struct snd_compr_caps caps; | 373 | struct snd_compr_caps caps; |
374 | 374 | ||
375 | if (!stream->ops->get_caps) | 375 | if (!stream->ops->get_caps) |
376 | return -ENXIO; | 376 | return -ENXIO; |
377 | 377 | ||
378 | retval = stream->ops->get_caps(stream, &caps); | 378 | retval = stream->ops->get_caps(stream, &caps); |
379 | if (retval) | 379 | if (retval) |
380 | goto out; | 380 | goto out; |
381 | if (copy_to_user((void __user *)arg, &caps, sizeof(caps))) | 381 | if (copy_to_user((void __user *)arg, &caps, sizeof(caps))) |
382 | retval = -EFAULT; | 382 | retval = -EFAULT; |
383 | out: | 383 | out: |
384 | return retval; | 384 | return retval; |
385 | } | 385 | } |
386 | 386 | ||
387 | static int | 387 | static int |
388 | snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg) | 388 | snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg) |
389 | { | 389 | { |
390 | int retval; | 390 | int retval; |
391 | struct snd_compr_codec_caps *caps; | 391 | struct snd_compr_codec_caps *caps; |
392 | 392 | ||
393 | if (!stream->ops->get_codec_caps) | 393 | if (!stream->ops->get_codec_caps) |
394 | return -ENXIO; | 394 | return -ENXIO; |
395 | 395 | ||
396 | caps = kmalloc(sizeof(*caps), GFP_KERNEL); | 396 | caps = kmalloc(sizeof(*caps), GFP_KERNEL); |
397 | if (!caps) | 397 | if (!caps) |
398 | return -ENOMEM; | 398 | return -ENOMEM; |
399 | 399 | ||
400 | retval = stream->ops->get_codec_caps(stream, caps); | 400 | retval = stream->ops->get_codec_caps(stream, caps); |
401 | if (retval) | 401 | if (retval) |
402 | goto out; | 402 | goto out; |
403 | if (copy_to_user((void __user *)arg, caps, sizeof(*caps))) | 403 | if (copy_to_user((void __user *)arg, caps, sizeof(*caps))) |
404 | retval = -EFAULT; | 404 | retval = -EFAULT; |
405 | 405 | ||
406 | out: | 406 | out: |
407 | kfree(caps); | 407 | kfree(caps); |
408 | return retval; | 408 | return retval; |
409 | } | 409 | } |
410 | 410 | ||
411 | /* revisit this with snd_pcm_preallocate_xxx */ | 411 | /* revisit this with snd_pcm_preallocate_xxx */ |
412 | static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, | 412 | static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, |
413 | struct snd_compr_params *params) | 413 | struct snd_compr_params *params) |
414 | { | 414 | { |
415 | unsigned int buffer_size; | 415 | unsigned int buffer_size; |
416 | void *buffer; | 416 | void *buffer; |
417 | 417 | ||
418 | buffer_size = params->buffer.fragment_size * params->buffer.fragments; | 418 | buffer_size = params->buffer.fragment_size * params->buffer.fragments; |
419 | if (stream->ops->copy) { | 419 | if (stream->ops->copy) { |
420 | buffer = NULL; | 420 | buffer = NULL; |
421 | /* if copy is defined the driver will be required to copy | 421 | /* if copy is defined the driver will be required to copy |
422 | * the data from core | 422 | * the data from core |
423 | */ | 423 | */ |
424 | } else { | 424 | } else { |
425 | buffer = kmalloc(buffer_size, GFP_KERNEL); | 425 | buffer = kmalloc(buffer_size, GFP_KERNEL); |
426 | if (!buffer) | 426 | if (!buffer) |
427 | return -ENOMEM; | 427 | return -ENOMEM; |
428 | } | 428 | } |
429 | stream->runtime->fragment_size = params->buffer.fragment_size; | 429 | stream->runtime->fragment_size = params->buffer.fragment_size; |
430 | stream->runtime->fragments = params->buffer.fragments; | 430 | stream->runtime->fragments = params->buffer.fragments; |
431 | stream->runtime->buffer = buffer; | 431 | stream->runtime->buffer = buffer; |
432 | stream->runtime->buffer_size = buffer_size; | 432 | stream->runtime->buffer_size = buffer_size; |
433 | return 0; | 433 | return 0; |
434 | } | 434 | } |
435 | 435 | ||
436 | static int snd_compress_check_input(struct snd_compr_params *params) | 436 | static int snd_compress_check_input(struct snd_compr_params *params) |
437 | { | 437 | { |
438 | /* first let's check the buffer parameter's */ | 438 | /* first let's check the buffer parameter's */ |
439 | if (params->buffer.fragment_size == 0 || | 439 | if (params->buffer.fragment_size == 0 || |
440 | params->buffer.fragments > SIZE_MAX / params->buffer.fragment_size) | 440 | params->buffer.fragments > SIZE_MAX / params->buffer.fragment_size) |
441 | return -EINVAL; | 441 | return -EINVAL; |
442 | 442 | ||
443 | /* now codec parameters */ | 443 | /* now codec parameters */ |
444 | if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX) | 444 | if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX) |
445 | return -EINVAL; | 445 | return -EINVAL; |
446 | 446 | ||
447 | if (params->codec.ch_in == 0 || params->codec.ch_out == 0) | 447 | if (params->codec.ch_in == 0 || params->codec.ch_out == 0) |
448 | return -EINVAL; | 448 | return -EINVAL; |
449 | 449 | ||
450 | if (!(params->codec.sample_rate & SNDRV_PCM_RATE_8000_192000)) | 450 | if (!(params->codec.sample_rate & SNDRV_PCM_RATE_8000_192000)) |
451 | return -EINVAL; | 451 | return -EINVAL; |
452 | 452 | ||
453 | return 0; | 453 | return 0; |
454 | } | 454 | } |
455 | 455 | ||
456 | static int | 456 | static int |
457 | snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) | 457 | snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) |
458 | { | 458 | { |
459 | struct snd_compr_params *params; | 459 | struct snd_compr_params *params; |
460 | int retval; | 460 | int retval; |
461 | 461 | ||
462 | if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { | 462 | if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { |
463 | /* | 463 | /* |
464 | * we should allow parameter change only when stream has been | 464 | * we should allow parameter change only when stream has been |
465 | * opened not in other cases | 465 | * opened not in other cases |
466 | */ | 466 | */ |
467 | params = kmalloc(sizeof(*params), GFP_KERNEL); | 467 | params = kmalloc(sizeof(*params), GFP_KERNEL); |
468 | if (!params) | 468 | if (!params) |
469 | return -ENOMEM; | 469 | return -ENOMEM; |
470 | if (copy_from_user(params, (void __user *)arg, sizeof(*params))) { | 470 | if (copy_from_user(params, (void __user *)arg, sizeof(*params))) { |
471 | retval = -EFAULT; | 471 | retval = -EFAULT; |
472 | goto out; | 472 | goto out; |
473 | } | 473 | } |
474 | 474 | ||
475 | retval = snd_compress_check_input(params); | 475 | retval = snd_compress_check_input(params); |
476 | if (retval) | 476 | if (retval) |
477 | goto out; | 477 | goto out; |
478 | 478 | ||
479 | retval = snd_compr_allocate_buffer(stream, params); | 479 | retval = snd_compr_allocate_buffer(stream, params); |
480 | if (retval) { | 480 | if (retval) { |
481 | retval = -ENOMEM; | 481 | retval = -ENOMEM; |
482 | goto out; | 482 | goto out; |
483 | } | 483 | } |
484 | 484 | ||
485 | retval = stream->ops->set_params(stream, params); | 485 | retval = stream->ops->set_params(stream, params); |
486 | if (retval) | 486 | if (retval) |
487 | goto out; | 487 | goto out; |
488 | stream->runtime->state = SNDRV_PCM_STATE_SETUP; | 488 | stream->runtime->state = SNDRV_PCM_STATE_SETUP; |
489 | stream->metadata_set = false; | ||
490 | stream->next_track = false; | ||
489 | } else { | 491 | } else { |
490 | return -EPERM; | 492 | return -EPERM; |
491 | } | 493 | } |
492 | out: | 494 | out: |
493 | kfree(params); | 495 | kfree(params); |
494 | return retval; | 496 | return retval; |
495 | } | 497 | } |
496 | 498 | ||
497 | static int | 499 | static int |
498 | snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg) | 500 | snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg) |
499 | { | 501 | { |
500 | struct snd_codec *params; | 502 | struct snd_codec *params; |
501 | int retval; | 503 | int retval; |
502 | 504 | ||
503 | if (!stream->ops->get_params) | 505 | if (!stream->ops->get_params) |
504 | return -EBADFD; | 506 | return -EBADFD; |
505 | 507 | ||
506 | params = kmalloc(sizeof(*params), GFP_KERNEL); | 508 | params = kmalloc(sizeof(*params), GFP_KERNEL); |
507 | if (!params) | 509 | if (!params) |
508 | return -ENOMEM; | 510 | return -ENOMEM; |
509 | retval = stream->ops->get_params(stream, params); | 511 | retval = stream->ops->get_params(stream, params); |
510 | if (retval) | 512 | if (retval) |
511 | goto out; | 513 | goto out; |
512 | if (copy_to_user((char __user *)arg, params, sizeof(*params))) | 514 | if (copy_to_user((char __user *)arg, params, sizeof(*params))) |
513 | retval = -EFAULT; | 515 | retval = -EFAULT; |
514 | 516 | ||
515 | out: | 517 | out: |
516 | kfree(params); | 518 | kfree(params); |
517 | return retval; | 519 | return retval; |
518 | } | 520 | } |
519 | 521 | ||
522 | static int | ||
523 | snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg) | ||
524 | { | ||
525 | struct snd_compr_metadata metadata; | ||
526 | int retval; | ||
527 | |||
528 | if (!stream->ops->get_metadata) | ||
529 | return -ENXIO; | ||
530 | |||
531 | if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) | ||
532 | return -EFAULT; | ||
533 | |||
534 | retval = stream->ops->get_metadata(stream, &metadata); | ||
535 | if (retval != 0) | ||
536 | return retval; | ||
537 | |||
538 | if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata))) | ||
539 | return -EFAULT; | ||
540 | |||
541 | return 0; | ||
542 | } | ||
543 | |||
544 | static int | ||
545 | snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg) | ||
546 | { | ||
547 | struct snd_compr_metadata metadata; | ||
548 | int retval; | ||
549 | |||
550 | if (!stream->ops->set_metadata) | ||
551 | return -ENXIO; | ||
552 | /* | ||
553 | * we should allow parameter change only when stream has been | ||
554 | * opened not in other cases | ||
555 | */ | ||
556 | if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata))) | ||
557 | return -EFAULT; | ||
558 | |||
559 | retval = stream->ops->set_metadata(stream, &metadata); | ||
560 | stream->metadata_set = true; | ||
561 | |||
562 | return retval; | ||
563 | } | ||
564 | |||
520 | static inline int | 565 | static inline int |
521 | snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg) | 566 | snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg) |
522 | { | 567 | { |
523 | struct snd_compr_tstamp tstamp = {0}; | 568 | struct snd_compr_tstamp tstamp = {0}; |
524 | int ret; | 569 | int ret; |
525 | 570 | ||
526 | ret = snd_compr_update_tstamp(stream, &tstamp); | 571 | ret = snd_compr_update_tstamp(stream, &tstamp); |
527 | if (ret == 0) | 572 | if (ret == 0) |
528 | ret = copy_to_user((struct snd_compr_tstamp __user *)arg, | 573 | ret = copy_to_user((struct snd_compr_tstamp __user *)arg, |
529 | &tstamp, sizeof(tstamp)) ? -EFAULT : 0; | 574 | &tstamp, sizeof(tstamp)) ? -EFAULT : 0; |
530 | return ret; | 575 | return ret; |
531 | } | 576 | } |
532 | 577 | ||
533 | static int snd_compr_pause(struct snd_compr_stream *stream) | 578 | static int snd_compr_pause(struct snd_compr_stream *stream) |
534 | { | 579 | { |
535 | int retval; | 580 | int retval; |
536 | 581 | ||
537 | if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) | 582 | if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) |
538 | return -EPERM; | 583 | return -EPERM; |
539 | retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); | 584 | retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); |
540 | if (!retval) | 585 | if (!retval) |
541 | stream->runtime->state = SNDRV_PCM_STATE_PAUSED; | 586 | stream->runtime->state = SNDRV_PCM_STATE_PAUSED; |
542 | return retval; | 587 | return retval; |
543 | } | 588 | } |
544 | 589 | ||
545 | static int snd_compr_resume(struct snd_compr_stream *stream) | 590 | static int snd_compr_resume(struct snd_compr_stream *stream) |
546 | { | 591 | { |
547 | int retval; | 592 | int retval; |
548 | 593 | ||
549 | if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED) | 594 | if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED) |
550 | return -EPERM; | 595 | return -EPERM; |
551 | retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); | 596 | retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); |
552 | if (!retval) | 597 | if (!retval) |
553 | stream->runtime->state = SNDRV_PCM_STATE_RUNNING; | 598 | stream->runtime->state = SNDRV_PCM_STATE_RUNNING; |
554 | return retval; | 599 | return retval; |
555 | } | 600 | } |
556 | 601 | ||
557 | static int snd_compr_start(struct snd_compr_stream *stream) | 602 | static int snd_compr_start(struct snd_compr_stream *stream) |
558 | { | 603 | { |
559 | int retval; | 604 | int retval; |
560 | 605 | ||
561 | if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED) | 606 | if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED) |
562 | return -EPERM; | 607 | return -EPERM; |
563 | retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START); | 608 | retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START); |
564 | if (!retval) | 609 | if (!retval) |
565 | stream->runtime->state = SNDRV_PCM_STATE_RUNNING; | 610 | stream->runtime->state = SNDRV_PCM_STATE_RUNNING; |
566 | return retval; | 611 | return retval; |
567 | } | 612 | } |
568 | 613 | ||
569 | static int snd_compr_stop(struct snd_compr_stream *stream) | 614 | static int snd_compr_stop(struct snd_compr_stream *stream) |
570 | { | 615 | { |
571 | int retval; | 616 | int retval; |
572 | 617 | ||
573 | if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || | 618 | if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || |
574 | stream->runtime->state == SNDRV_PCM_STATE_SETUP) | 619 | stream->runtime->state == SNDRV_PCM_STATE_SETUP) |
575 | return -EPERM; | 620 | return -EPERM; |
576 | retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); | 621 | retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); |
577 | if (!retval) { | 622 | if (!retval) { |
578 | stream->runtime->state = SNDRV_PCM_STATE_SETUP; | 623 | stream->runtime->state = SNDRV_PCM_STATE_SETUP; |
579 | wake_up(&stream->runtime->sleep); | 624 | wake_up(&stream->runtime->sleep); |
580 | stream->runtime->hw_pointer = 0; | 625 | stream->runtime->hw_pointer = 0; |
581 | stream->runtime->app_pointer = 0; | 626 | stream->runtime->app_pointer = 0; |
582 | stream->runtime->total_bytes_available = 0; | 627 | stream->runtime->total_bytes_available = 0; |
583 | stream->runtime->total_bytes_transferred = 0; | 628 | stream->runtime->total_bytes_transferred = 0; |
584 | } | 629 | } |
585 | return retval; | 630 | return retval; |
586 | } | 631 | } |
587 | 632 | ||
588 | static int snd_compr_drain(struct snd_compr_stream *stream) | 633 | static int snd_compr_drain(struct snd_compr_stream *stream) |
589 | { | 634 | { |
590 | int retval; | 635 | int retval; |
591 | 636 | ||
592 | if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || | 637 | if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || |
593 | stream->runtime->state == SNDRV_PCM_STATE_SETUP) | 638 | stream->runtime->state == SNDRV_PCM_STATE_SETUP) |
594 | return -EPERM; | 639 | return -EPERM; |
595 | retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); | 640 | retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); |
596 | if (!retval) { | 641 | if (!retval) { |
597 | stream->runtime->state = SNDRV_PCM_STATE_DRAINING; | 642 | stream->runtime->state = SNDRV_PCM_STATE_DRAINING; |
598 | wake_up(&stream->runtime->sleep); | 643 | wake_up(&stream->runtime->sleep); |
599 | } | 644 | } |
600 | return retval; | 645 | return retval; |
601 | } | 646 | } |
602 | 647 | ||
648 | static int snd_compr_next_track(struct snd_compr_stream *stream) | ||
649 | { | ||
650 | int retval; | ||
651 | |||
652 | /* only a running stream can transition to next track */ | ||
653 | if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) | ||
654 | return -EPERM; | ||
655 | |||
656 | /* you can signal next track isf this is intended to be a gapless stream | ||
657 | * and current track metadata is set | ||
658 | */ | ||
659 | if (stream->metadata_set == false) | ||
660 | return -EPERM; | ||
661 | |||
662 | retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK); | ||
663 | if (retval != 0) | ||
664 | return retval; | ||
665 | stream->metadata_set = false; | ||
666 | stream->next_track = true; | ||
667 | return 0; | ||
668 | } | ||
669 | |||
670 | static int snd_compr_partial_drain(struct snd_compr_stream *stream) | ||
671 | { | ||
672 | int retval; | ||
673 | if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || | ||
674 | stream->runtime->state == SNDRV_PCM_STATE_SETUP) | ||
675 | return -EPERM; | ||
676 | /* stream can be drained only when next track has been signalled */ | ||
677 | if (stream->next_track == false) | ||
678 | return -EPERM; | ||
679 | |||
680 | retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); | ||
681 | |||
682 | stream->next_track = false; | ||
683 | return retval; | ||
684 | } | ||
685 | |||
603 | static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) | 686 | static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) |
604 | { | 687 | { |
605 | struct snd_compr_file *data = f->private_data; | 688 | struct snd_compr_file *data = f->private_data; |
606 | struct snd_compr_stream *stream; | 689 | struct snd_compr_stream *stream; |
607 | int retval = -ENOTTY; | 690 | int retval = -ENOTTY; |
608 | 691 | ||
609 | if (snd_BUG_ON(!data)) | 692 | if (snd_BUG_ON(!data)) |
610 | return -EFAULT; | 693 | return -EFAULT; |
611 | stream = &data->stream; | 694 | stream = &data->stream; |
612 | if (snd_BUG_ON(!stream)) | 695 | if (snd_BUG_ON(!stream)) |
613 | return -EFAULT; | 696 | return -EFAULT; |
614 | mutex_lock(&stream->device->lock); | 697 | mutex_lock(&stream->device->lock); |
615 | switch (_IOC_NR(cmd)) { | 698 | switch (_IOC_NR(cmd)) { |
616 | case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION): | 699 | case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION): |
617 | put_user(SNDRV_COMPRESS_VERSION, | 700 | put_user(SNDRV_COMPRESS_VERSION, |
618 | (int __user *)arg) ? -EFAULT : 0; | 701 | (int __user *)arg) ? -EFAULT : 0; |
619 | break; | 702 | break; |
620 | case _IOC_NR(SNDRV_COMPRESS_GET_CAPS): | 703 | case _IOC_NR(SNDRV_COMPRESS_GET_CAPS): |
621 | retval = snd_compr_get_caps(stream, arg); | 704 | retval = snd_compr_get_caps(stream, arg); |
622 | break; | 705 | break; |
623 | case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS): | 706 | case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS): |
624 | retval = snd_compr_get_codec_caps(stream, arg); | 707 | retval = snd_compr_get_codec_caps(stream, arg); |
625 | break; | 708 | break; |
626 | case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS): | 709 | case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS): |
627 | retval = snd_compr_set_params(stream, arg); | 710 | retval = snd_compr_set_params(stream, arg); |
628 | break; | 711 | break; |
629 | case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS): | 712 | case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS): |
630 | retval = snd_compr_get_params(stream, arg); | 713 | retval = snd_compr_get_params(stream, arg); |
631 | break; | 714 | break; |
715 | case _IOC_NR(SNDRV_COMPRESS_SET_METADATA): | ||
716 | retval = snd_compr_set_metadata(stream, arg); | ||
717 | break; | ||
718 | case _IOC_NR(SNDRV_COMPRESS_GET_METADATA): | ||
719 | retval = snd_compr_get_metadata(stream, arg); | ||
720 | break; | ||
632 | case _IOC_NR(SNDRV_COMPRESS_TSTAMP): | 721 | case _IOC_NR(SNDRV_COMPRESS_TSTAMP): |
633 | retval = snd_compr_tstamp(stream, arg); | 722 | retval = snd_compr_tstamp(stream, arg); |
634 | break; | 723 | break; |
635 | case _IOC_NR(SNDRV_COMPRESS_AVAIL): | 724 | case _IOC_NR(SNDRV_COMPRESS_AVAIL): |
636 | retval = snd_compr_ioctl_avail(stream, arg); | 725 | retval = snd_compr_ioctl_avail(stream, arg); |
637 | break; | 726 | break; |
638 | case _IOC_NR(SNDRV_COMPRESS_PAUSE): | 727 | case _IOC_NR(SNDRV_COMPRESS_PAUSE): |
639 | retval = snd_compr_pause(stream); | 728 | retval = snd_compr_pause(stream); |
640 | break; | 729 | break; |
641 | case _IOC_NR(SNDRV_COMPRESS_RESUME): | 730 | case _IOC_NR(SNDRV_COMPRESS_RESUME): |
642 | retval = snd_compr_resume(stream); | 731 | retval = snd_compr_resume(stream); |
643 | break; | 732 | break; |
644 | case _IOC_NR(SNDRV_COMPRESS_START): | 733 | case _IOC_NR(SNDRV_COMPRESS_START): |
645 | retval = snd_compr_start(stream); | 734 | retval = snd_compr_start(stream); |
646 | break; | 735 | break; |
647 | case _IOC_NR(SNDRV_COMPRESS_STOP): | 736 | case _IOC_NR(SNDRV_COMPRESS_STOP): |
648 | retval = snd_compr_stop(stream); | 737 | retval = snd_compr_stop(stream); |
649 | break; | 738 | break; |
650 | case _IOC_NR(SNDRV_COMPRESS_DRAIN): | 739 | case _IOC_NR(SNDRV_COMPRESS_DRAIN): |
651 | retval = snd_compr_drain(stream); | 740 | retval = snd_compr_drain(stream); |
652 | break; | 741 | break; |
742 | case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN): | ||
743 | retval = snd_compr_partial_drain(stream); | ||
744 | break; | ||
745 | case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK): | ||
746 | retval = snd_compr_next_track(stream); | ||
747 | break; | ||
748 | |||
653 | } | 749 | } |
654 | mutex_unlock(&stream->device->lock); | 750 | mutex_unlock(&stream->device->lock); |
655 | return retval; | 751 | return retval; |
656 | } | 752 | } |
657 | 753 | ||
658 | static const struct file_operations snd_compr_file_ops = { | 754 | static const struct file_operations snd_compr_file_ops = { |
659 | .owner = THIS_MODULE, | 755 | .owner = THIS_MODULE, |
660 | .open = snd_compr_open, | 756 | .open = snd_compr_open, |
661 | .release = snd_compr_free, | 757 | .release = snd_compr_free, |
662 | .write = snd_compr_write, | 758 | .write = snd_compr_write, |
663 | .read = snd_compr_read, | 759 | .read = snd_compr_read, |
664 | .unlocked_ioctl = snd_compr_ioctl, | 760 | .unlocked_ioctl = snd_compr_ioctl, |
665 | .mmap = snd_compr_mmap, | 761 | .mmap = snd_compr_mmap, |
666 | .poll = snd_compr_poll, | 762 | .poll = snd_compr_poll, |
667 | }; | 763 | }; |
668 | 764 | ||
669 | static int snd_compress_dev_register(struct snd_device *device) | 765 | static int snd_compress_dev_register(struct snd_device *device) |
670 | { | 766 | { |
671 | int ret = -EINVAL; | 767 | int ret = -EINVAL; |
672 | char str[16]; | 768 | char str[16]; |
673 | struct snd_compr *compr; | 769 | struct snd_compr *compr; |
674 | 770 | ||
675 | if (snd_BUG_ON(!device || !device->device_data)) | 771 | if (snd_BUG_ON(!device || !device->device_data)) |
676 | return -EBADFD; | 772 | return -EBADFD; |
677 | compr = device->device_data; | 773 | compr = device->device_data; |
678 | 774 | ||
679 | sprintf(str, "comprC%iD%i", compr->card->number, compr->device); | 775 | sprintf(str, "comprC%iD%i", compr->card->number, compr->device); |
680 | pr_debug("reg %s for device %s, direction %d\n", str, compr->name, | 776 | pr_debug("reg %s for device %s, direction %d\n", str, compr->name, |
681 | compr->direction); | 777 | compr->direction); |
682 | /* register compressed device */ | 778 | /* register compressed device */ |
683 | ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card, | 779 | ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card, |
684 | compr->device, &snd_compr_file_ops, compr, str); | 780 | compr->device, &snd_compr_file_ops, compr, str); |
685 | if (ret < 0) { | 781 | if (ret < 0) { |
686 | pr_err("snd_register_device failed\n %d", ret); | 782 | pr_err("snd_register_device failed\n %d", ret); |
687 | return ret; | 783 | return ret; |
688 | } | 784 | } |
689 | return ret; | 785 | return ret; |
690 | 786 | ||
691 | } | 787 | } |
692 | 788 | ||
693 | static int snd_compress_dev_disconnect(struct snd_device *device) | 789 | static int snd_compress_dev_disconnect(struct snd_device *device) |
694 | { | 790 | { |
695 | struct snd_compr *compr; | 791 | struct snd_compr *compr; |
696 | 792 | ||
697 | compr = device->device_data; | 793 | compr = device->device_data; |
698 | snd_unregister_device(compr->direction, compr->card, compr->device); | 794 | snd_unregister_device(compr->direction, compr->card, compr->device); |
699 | return 0; | 795 | return 0; |
700 | } | 796 | } |
701 | 797 | ||
702 | /* | 798 | /* |
703 | * snd_compress_new: create new compress device | 799 | * snd_compress_new: create new compress device |
704 | * @card: sound card pointer | 800 | * @card: sound card pointer |
705 | * @device: device number | 801 | * @device: device number |
706 | * @dirn: device direction, should be of type enum snd_compr_direction | 802 | * @dirn: device direction, should be of type enum snd_compr_direction |
707 | * @compr: compress device pointer | 803 | * @compr: compress device pointer |
708 | */ | 804 | */ |
709 | int snd_compress_new(struct snd_card *card, int device, | 805 | int snd_compress_new(struct snd_card *card, int device, |
710 | int dirn, struct snd_compr *compr) | 806 | int dirn, struct snd_compr *compr) |
711 | { | 807 | { |
712 | static struct snd_device_ops ops = { | 808 | static struct snd_device_ops ops = { |
713 | .dev_free = NULL, | 809 | .dev_free = NULL, |
714 | .dev_register = snd_compress_dev_register, | 810 | .dev_register = snd_compress_dev_register, |
715 | .dev_disconnect = snd_compress_dev_disconnect, | 811 | .dev_disconnect = snd_compress_dev_disconnect, |
716 | }; | 812 | }; |
717 | 813 | ||
718 | compr->card = card; | 814 | compr->card = card; |
719 | compr->device = device; | 815 | compr->device = device; |
720 | compr->direction = dirn; | 816 | compr->direction = dirn; |
721 | return snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops); | 817 | return snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops); |
722 | } | 818 | } |
723 | EXPORT_SYMBOL_GPL(snd_compress_new); | 819 | EXPORT_SYMBOL_GPL(snd_compress_new); |
724 | 820 | ||
725 | static int snd_compress_add_device(struct snd_compr *device) | 821 | static int snd_compress_add_device(struct snd_compr *device) |
726 | { | 822 | { |
727 | int ret; | 823 | int ret; |
728 | 824 | ||
729 | if (!device->card) | 825 | if (!device->card) |
730 | return -EINVAL; | 826 | return -EINVAL; |
731 | 827 | ||
732 | /* register the card */ | 828 | /* register the card */ |
733 | ret = snd_card_register(device->card); | 829 | ret = snd_card_register(device->card); |
734 | if (ret) | 830 | if (ret) |
735 | goto out; | 831 | goto out; |
736 | return 0; | 832 | return 0; |
737 | 833 | ||
738 | out: | 834 | out: |
739 | pr_err("failed with %d\n", ret); | 835 | pr_err("failed with %d\n", ret); |
740 | return ret; | 836 | return ret; |
741 | 837 | ||
742 | } | 838 | } |
743 | 839 | ||
744 | static int snd_compress_remove_device(struct snd_compr *device) | 840 | static int snd_compress_remove_device(struct snd_compr *device) |
745 | { | 841 | { |
746 | return snd_card_free(device->card); | 842 | return snd_card_free(device->card); |
747 | } | 843 | } |
748 | 844 | ||
749 | /** | 845 | /** |
750 | * snd_compress_register - register compressed device | 846 | * snd_compress_register - register compressed device |
751 | * | 847 | * |
752 | * @device: compressed device to register | 848 | * @device: compressed device to register |
753 | */ | 849 | */ |
754 | int snd_compress_register(struct snd_compr *device) | 850 | int snd_compress_register(struct snd_compr *device) |
755 | { | 851 | { |
756 | int retval; | 852 | int retval; |
757 | 853 | ||
758 | if (device->name == NULL || device->dev == NULL || device->ops == NULL) | 854 | if (device->name == NULL || device->dev == NULL || device->ops == NULL) |
759 | return -EINVAL; | 855 | return -EINVAL; |
760 | 856 | ||
761 | pr_debug("Registering compressed device %s\n", device->name); | 857 | pr_debug("Registering compressed device %s\n", device->name); |
762 | if (snd_BUG_ON(!device->ops->open)) | 858 | if (snd_BUG_ON(!device->ops->open)) |
763 | return -EINVAL; | 859 | return -EINVAL; |
764 | if (snd_BUG_ON(!device->ops->free)) | 860 | if (snd_BUG_ON(!device->ops->free)) |
765 | return -EINVAL; | 861 | return -EINVAL; |
766 | if (snd_BUG_ON(!device->ops->set_params)) | 862 | if (snd_BUG_ON(!device->ops->set_params)) |
767 | return -EINVAL; | 863 | return -EINVAL; |
768 | if (snd_BUG_ON(!device->ops->trigger)) | 864 | if (snd_BUG_ON(!device->ops->trigger)) |
769 | return -EINVAL; | 865 | return -EINVAL; |
770 | 866 | ||
771 | mutex_init(&device->lock); | 867 | mutex_init(&device->lock); |
772 | 868 | ||
773 | /* register a compressed card */ | 869 | /* register a compressed card */ |
774 | mutex_lock(&device_mutex); | 870 | mutex_lock(&device_mutex); |
775 | retval = snd_compress_add_device(device); | 871 | retval = snd_compress_add_device(device); |
776 | mutex_unlock(&device_mutex); | 872 | mutex_unlock(&device_mutex); |
777 | return retval; | 873 | return retval; |
778 | } | 874 | } |
779 | EXPORT_SYMBOL_GPL(snd_compress_register); | 875 | EXPORT_SYMBOL_GPL(snd_compress_register); |
780 | 876 | ||
781 | int snd_compress_deregister(struct snd_compr *device) | 877 | int snd_compress_deregister(struct snd_compr *device) |
782 | { | 878 | { |
783 | pr_debug("Removing compressed device %s\n", device->name); | 879 | pr_debug("Removing compressed device %s\n", device->name); |
784 | mutex_lock(&device_mutex); | 880 | mutex_lock(&device_mutex); |
785 | snd_compress_remove_device(device); | 881 | snd_compress_remove_device(device); |
786 | mutex_unlock(&device_mutex); | 882 | mutex_unlock(&device_mutex); |
787 | return 0; | 883 | return 0; |
788 | } | 884 | } |
789 | EXPORT_SYMBOL_GPL(snd_compress_deregister); | 885 | EXPORT_SYMBOL_GPL(snd_compress_deregister); |
790 | 886 | ||
791 | static int __init snd_compress_init(void) | 887 | static int __init snd_compress_init(void) |
792 | { | 888 | { |
793 | return 0; | 889 | return 0; |
794 | } | 890 | } |
795 | 891 | ||
796 | static void __exit snd_compress_exit(void) | 892 | static void __exit snd_compress_exit(void) |
797 | { | 893 | { |
798 | } | 894 | } |
799 | 895 | ||
800 | module_init(snd_compress_init); | 896 | module_init(snd_compress_init); |
801 | module_exit(snd_compress_exit); | 897 | module_exit(snd_compress_exit); |
802 | 898 | ||
803 | MODULE_DESCRIPTION("ALSA Compressed offload framework"); | 899 | MODULE_DESCRIPTION("ALSA Compressed offload framework"); |
804 | MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>"); | 900 | MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>"); |
805 | MODULE_LICENSE("GPL v2"); | 901 | MODULE_LICENSE("GPL v2"); |
806 | 902 |