2 * oxfw_pcm.c - a part of driver for OXFW970/971 based devices
4 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
5 * Licensed under the terms of the GNU General Public License, version 2.
10 static int hw_rule_rate(struct snd_pcm_hw_params
*params
,
11 struct snd_pcm_hw_rule
*rule
)
13 u8
**formats
= rule
->private;
14 struct snd_interval
*r
=
15 hw_param_interval(params
, SNDRV_PCM_HW_PARAM_RATE
);
16 const struct snd_interval
*c
=
17 hw_param_interval_c(params
, SNDRV_PCM_HW_PARAM_CHANNELS
);
18 struct snd_interval t
= {
19 .min
= UINT_MAX
, .max
= 0, .integer
= 1
21 struct snd_oxfw_stream_formation formation
;
24 for (i
= 0; i
< SND_OXFW_STREAM_FORMAT_ENTRIES
; i
++) {
25 if (formats
[i
] == NULL
)
28 err
= snd_oxfw_stream_parse_format(formats
[i
], &formation
);
31 if (!snd_interval_test(c
, formation
.pcm
))
34 t
.min
= min(t
.min
, formation
.rate
);
35 t
.max
= max(t
.max
, formation
.rate
);
38 return snd_interval_refine(r
, &t
);
41 static int hw_rule_channels(struct snd_pcm_hw_params
*params
,
42 struct snd_pcm_hw_rule
*rule
)
44 u8
**formats
= rule
->private;
45 struct snd_interval
*c
=
46 hw_param_interval(params
, SNDRV_PCM_HW_PARAM_CHANNELS
);
47 const struct snd_interval
*r
=
48 hw_param_interval_c(params
, SNDRV_PCM_HW_PARAM_RATE
);
49 struct snd_oxfw_stream_formation formation
;
51 unsigned int count
, list
[SND_OXFW_STREAM_FORMAT_ENTRIES
] = {0};
54 for (i
= 0; i
< SND_OXFW_STREAM_FORMAT_ENTRIES
; i
++) {
55 if (formats
[i
] == NULL
)
58 err
= snd_oxfw_stream_parse_format(formats
[i
], &formation
);
61 if (!snd_interval_test(r
, formation
.rate
))
63 if (list
[count
] == formation
.pcm
)
66 for (j
= 0; j
< ARRAY_SIZE(list
); j
++) {
67 if (list
[j
] == formation
.pcm
)
70 if (j
== ARRAY_SIZE(list
)) {
71 list
[count
] = formation
.pcm
;
72 if (++count
== ARRAY_SIZE(list
))
77 return snd_interval_list(c
, count
, list
, 0);
80 static void limit_channels_and_rates(struct snd_pcm_hardware
*hw
, u8
**formats
)
82 struct snd_oxfw_stream_formation formation
;
85 hw
->channels_min
= UINT_MAX
;
88 hw
->rate_min
= UINT_MAX
;
92 for (i
= 0; i
< SND_OXFW_STREAM_FORMAT_ENTRIES
; i
++) {
93 if (formats
[i
] == NULL
)
96 err
= snd_oxfw_stream_parse_format(formats
[i
], &formation
);
100 hw
->channels_min
= min(hw
->channels_min
, formation
.pcm
);
101 hw
->channels_max
= max(hw
->channels_max
, formation
.pcm
);
103 hw
->rate_min
= min(hw
->rate_min
, formation
.rate
);
104 hw
->rate_max
= max(hw
->rate_max
, formation
.rate
);
105 hw
->rates
|= snd_pcm_rate_to_rate_bit(formation
.rate
);
109 static void limit_period_and_buffer(struct snd_pcm_hardware
*hw
)
111 hw
->periods_min
= 2; /* SNDRV_PCM_INFO_BATCH */
112 hw
->periods_max
= UINT_MAX
;
114 hw
->period_bytes_min
= 4 * hw
->channels_max
; /* bytes for a frame */
116 /* Just to prevent from allocating much pages. */
117 hw
->period_bytes_max
= hw
->period_bytes_min
* 2048;
118 hw
->buffer_bytes_max
= hw
->period_bytes_max
* hw
->periods_min
;
121 static int init_hw_params(struct snd_oxfw
*oxfw
,
122 struct snd_pcm_substream
*substream
)
124 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
126 struct amdtp_stream
*stream
;
129 runtime
->hw
.info
= SNDRV_PCM_INFO_BATCH
|
130 SNDRV_PCM_INFO_BLOCK_TRANSFER
|
131 SNDRV_PCM_INFO_INTERLEAVED
|
132 SNDRV_PCM_INFO_JOINT_DUPLEX
|
133 SNDRV_PCM_INFO_MMAP
|
134 SNDRV_PCM_INFO_MMAP_VALID
;
136 if (substream
->stream
== SNDRV_PCM_STREAM_CAPTURE
) {
137 runtime
->hw
.formats
= AM824_IN_PCM_FORMAT_BITS
;
138 stream
= &oxfw
->tx_stream
;
139 formats
= oxfw
->tx_stream_formats
;
141 runtime
->hw
.formats
= AM824_OUT_PCM_FORMAT_BITS
;
142 stream
= &oxfw
->rx_stream
;
143 formats
= oxfw
->rx_stream_formats
;
146 limit_channels_and_rates(&runtime
->hw
, formats
);
147 limit_period_and_buffer(&runtime
->hw
);
149 err
= snd_pcm_hw_rule_add(runtime
, 0, SNDRV_PCM_HW_PARAM_CHANNELS
,
150 hw_rule_channels
, formats
,
151 SNDRV_PCM_HW_PARAM_RATE
, -1);
155 err
= snd_pcm_hw_rule_add(runtime
, 0, SNDRV_PCM_HW_PARAM_RATE
,
156 hw_rule_rate
, formats
,
157 SNDRV_PCM_HW_PARAM_CHANNELS
, -1);
161 err
= amdtp_am824_add_pcm_hw_constraints(stream
, runtime
);
166 static int limit_to_current_params(struct snd_pcm_substream
*substream
)
168 struct snd_oxfw
*oxfw
= substream
->private_data
;
169 struct snd_oxfw_stream_formation formation
;
170 enum avc_general_plug_dir dir
;
173 if (substream
->stream
== SNDRV_PCM_STREAM_CAPTURE
)
174 dir
= AVC_GENERAL_PLUG_DIR_OUT
;
176 dir
= AVC_GENERAL_PLUG_DIR_IN
;
178 err
= snd_oxfw_stream_get_current_formation(oxfw
, dir
, &formation
);
182 substream
->runtime
->hw
.channels_min
= formation
.pcm
;
183 substream
->runtime
->hw
.channels_max
= formation
.pcm
;
184 substream
->runtime
->hw
.rate_min
= formation
.rate
;
185 substream
->runtime
->hw
.rate_max
= formation
.rate
;
190 static int pcm_open(struct snd_pcm_substream
*substream
)
192 struct snd_oxfw
*oxfw
= substream
->private_data
;
195 err
= snd_oxfw_stream_lock_try(oxfw
);
199 err
= init_hw_params(oxfw
, substream
);
204 * When any PCM streams are already running, the available sampling
205 * rate is limited at current value.
207 if (amdtp_stream_pcm_running(&oxfw
->tx_stream
) ||
208 amdtp_stream_pcm_running(&oxfw
->rx_stream
)) {
209 err
= limit_to_current_params(substream
);
214 snd_pcm_set_sync(substream
);
218 snd_oxfw_stream_lock_release(oxfw
);
222 static int pcm_close(struct snd_pcm_substream
*substream
)
224 struct snd_oxfw
*oxfw
= substream
->private_data
;
226 snd_oxfw_stream_lock_release(oxfw
);
230 static int pcm_capture_hw_params(struct snd_pcm_substream
*substream
,
231 struct snd_pcm_hw_params
*hw_params
)
233 struct snd_oxfw
*oxfw
= substream
->private_data
;
236 err
= snd_pcm_lib_alloc_vmalloc_buffer(substream
,
237 params_buffer_bytes(hw_params
));
241 if (substream
->runtime
->status
->state
== SNDRV_PCM_STATE_OPEN
) {
242 mutex_lock(&oxfw
->mutex
);
243 oxfw
->capture_substreams
++;
244 mutex_unlock(&oxfw
->mutex
);
247 amdtp_am824_set_pcm_format(&oxfw
->tx_stream
, params_format(hw_params
));
251 static int pcm_playback_hw_params(struct snd_pcm_substream
*substream
,
252 struct snd_pcm_hw_params
*hw_params
)
254 struct snd_oxfw
*oxfw
= substream
->private_data
;
257 err
= snd_pcm_lib_alloc_vmalloc_buffer(substream
,
258 params_buffer_bytes(hw_params
));
262 if (substream
->runtime
->status
->state
== SNDRV_PCM_STATE_OPEN
) {
263 mutex_lock(&oxfw
->mutex
);
264 oxfw
->playback_substreams
++;
265 mutex_unlock(&oxfw
->mutex
);
268 amdtp_am824_set_pcm_format(&oxfw
->rx_stream
, params_format(hw_params
));
273 static int pcm_capture_hw_free(struct snd_pcm_substream
*substream
)
275 struct snd_oxfw
*oxfw
= substream
->private_data
;
277 mutex_lock(&oxfw
->mutex
);
279 if (substream
->runtime
->status
->state
!= SNDRV_PCM_STATE_OPEN
)
280 oxfw
->capture_substreams
--;
282 snd_oxfw_stream_stop_simplex(oxfw
, &oxfw
->tx_stream
);
284 mutex_unlock(&oxfw
->mutex
);
286 return snd_pcm_lib_free_vmalloc_buffer(substream
);
288 static int pcm_playback_hw_free(struct snd_pcm_substream
*substream
)
290 struct snd_oxfw
*oxfw
= substream
->private_data
;
292 mutex_lock(&oxfw
->mutex
);
294 if (substream
->runtime
->status
->state
!= SNDRV_PCM_STATE_OPEN
)
295 oxfw
->playback_substreams
--;
297 snd_oxfw_stream_stop_simplex(oxfw
, &oxfw
->rx_stream
);
299 mutex_unlock(&oxfw
->mutex
);
301 return snd_pcm_lib_free_vmalloc_buffer(substream
);
304 static int pcm_capture_prepare(struct snd_pcm_substream
*substream
)
306 struct snd_oxfw
*oxfw
= substream
->private_data
;
307 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
310 mutex_lock(&oxfw
->mutex
);
311 err
= snd_oxfw_stream_start_simplex(oxfw
, &oxfw
->tx_stream
,
312 runtime
->rate
, runtime
->channels
);
313 mutex_unlock(&oxfw
->mutex
);
317 amdtp_stream_pcm_prepare(&oxfw
->tx_stream
);
321 static int pcm_playback_prepare(struct snd_pcm_substream
*substream
)
323 struct snd_oxfw
*oxfw
= substream
->private_data
;
324 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
327 mutex_lock(&oxfw
->mutex
);
328 err
= snd_oxfw_stream_start_simplex(oxfw
, &oxfw
->rx_stream
,
329 runtime
->rate
, runtime
->channels
);
330 mutex_unlock(&oxfw
->mutex
);
334 amdtp_stream_pcm_prepare(&oxfw
->rx_stream
);
339 static int pcm_capture_trigger(struct snd_pcm_substream
*substream
, int cmd
)
341 struct snd_oxfw
*oxfw
= substream
->private_data
;
342 struct snd_pcm_substream
*pcm
;
345 case SNDRV_PCM_TRIGGER_START
:
348 case SNDRV_PCM_TRIGGER_STOP
:
354 amdtp_stream_pcm_trigger(&oxfw
->tx_stream
, pcm
);
357 static int pcm_playback_trigger(struct snd_pcm_substream
*substream
, int cmd
)
359 struct snd_oxfw
*oxfw
= substream
->private_data
;
360 struct snd_pcm_substream
*pcm
;
363 case SNDRV_PCM_TRIGGER_START
:
366 case SNDRV_PCM_TRIGGER_STOP
:
372 amdtp_stream_pcm_trigger(&oxfw
->rx_stream
, pcm
);
376 static snd_pcm_uframes_t
pcm_capture_pointer(struct snd_pcm_substream
*sbstm
)
378 struct snd_oxfw
*oxfw
= sbstm
->private_data
;
380 return amdtp_stream_pcm_pointer(&oxfw
->tx_stream
);
382 static snd_pcm_uframes_t
pcm_playback_pointer(struct snd_pcm_substream
*sbstm
)
384 struct snd_oxfw
*oxfw
= sbstm
->private_data
;
386 return amdtp_stream_pcm_pointer(&oxfw
->rx_stream
);
389 int snd_oxfw_create_pcm(struct snd_oxfw
*oxfw
)
391 static const struct snd_pcm_ops capture_ops
= {
394 .ioctl
= snd_pcm_lib_ioctl
,
395 .hw_params
= pcm_capture_hw_params
,
396 .hw_free
= pcm_capture_hw_free
,
397 .prepare
= pcm_capture_prepare
,
398 .trigger
= pcm_capture_trigger
,
399 .pointer
= pcm_capture_pointer
,
400 .page
= snd_pcm_lib_get_vmalloc_page
,
401 .mmap
= snd_pcm_lib_mmap_vmalloc
,
403 static const struct snd_pcm_ops playback_ops
= {
406 .ioctl
= snd_pcm_lib_ioctl
,
407 .hw_params
= pcm_playback_hw_params
,
408 .hw_free
= pcm_playback_hw_free
,
409 .prepare
= pcm_playback_prepare
,
410 .trigger
= pcm_playback_trigger
,
411 .pointer
= pcm_playback_pointer
,
412 .page
= snd_pcm_lib_get_vmalloc_page
,
413 .mmap
= snd_pcm_lib_mmap_vmalloc
,
416 unsigned int cap
= 0;
419 if (oxfw
->has_output
)
422 err
= snd_pcm_new(oxfw
->card
, oxfw
->card
->driver
, 0, 1, cap
, &pcm
);
426 pcm
->private_data
= oxfw
;
427 strcpy(pcm
->name
, oxfw
->card
->shortname
);
428 snd_pcm_set_ops(pcm
, SNDRV_PCM_STREAM_PLAYBACK
, &playback_ops
);
430 snd_pcm_set_ops(pcm
, SNDRV_PCM_STREAM_CAPTURE
, &capture_ops
);