diff options
author | Takashi Iwai <tiwai@suse.de> | 2013-10-22 04:02:57 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2013-10-22 04:02:57 -0400 |
commit | 861e66d3418a90f57b31a50110fc70b23569c551 (patch) | |
tree | ffa0d5bf8dc3e4b82b88d8942390a11d827423f9 /sound/firewire/amdtp.c | |
parent | b55447a7301b12d509df4b2909ed38d125ad83d4 (diff) | |
parent | b20be8de1b3972ccf9af72850b045214faa8d830 (diff) |
Merge branch 'dice-driver-playback-only' of git://git.alsa-project.org/alsa-kprivate into for-next
Diffstat (limited to 'sound/firewire/amdtp.c')
-rw-r--r-- | sound/firewire/amdtp.c | 209 |
1 files changed, 155 insertions, 54 deletions
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 4b08b25a4db8..d3226892ad6b 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c | |||
@@ -42,9 +42,6 @@ static void pcm_period_tasklet(unsigned long data); | |||
42 | int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, | 42 | int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, |
43 | enum cip_out_flags flags) | 43 | enum cip_out_flags flags) |
44 | { | 44 | { |
45 | if (flags != CIP_NONBLOCKING) | ||
46 | return -EINVAL; | ||
47 | |||
48 | s->unit = fw_unit_get(unit); | 45 | s->unit = fw_unit_get(unit); |
49 | s->flags = flags; | 46 | s->flags = flags; |
50 | s->context = ERR_PTR(-1); | 47 | s->context = ERR_PTR(-1); |
@@ -62,73 +59,91 @@ EXPORT_SYMBOL(amdtp_out_stream_init); | |||
62 | */ | 59 | */ |
63 | void amdtp_out_stream_destroy(struct amdtp_out_stream *s) | 60 | void amdtp_out_stream_destroy(struct amdtp_out_stream *s) |
64 | { | 61 | { |
65 | WARN_ON(!IS_ERR(s->context)); | 62 | WARN_ON(amdtp_out_stream_running(s)); |
66 | mutex_destroy(&s->mutex); | 63 | mutex_destroy(&s->mutex); |
67 | fw_unit_put(s->unit); | 64 | fw_unit_put(s->unit); |
68 | } | 65 | } |
69 | EXPORT_SYMBOL(amdtp_out_stream_destroy); | 66 | EXPORT_SYMBOL(amdtp_out_stream_destroy); |
70 | 67 | ||
68 | const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = { | ||
69 | [CIP_SFC_32000] = 8, | ||
70 | [CIP_SFC_44100] = 8, | ||
71 | [CIP_SFC_48000] = 8, | ||
72 | [CIP_SFC_88200] = 16, | ||
73 | [CIP_SFC_96000] = 16, | ||
74 | [CIP_SFC_176400] = 32, | ||
75 | [CIP_SFC_192000] = 32, | ||
76 | }; | ||
77 | EXPORT_SYMBOL(amdtp_syt_intervals); | ||
78 | |||
71 | /** | 79 | /** |
72 | * amdtp_out_stream_set_rate - set the sample rate | 80 | * amdtp_out_stream_set_parameters - set stream parameters |
73 | * @s: the AMDTP output stream to configure | 81 | * @s: the AMDTP output stream to configure |
74 | * @rate: the sample rate | 82 | * @rate: the sample rate |
83 | * @pcm_channels: the number of PCM samples in each data block, to be encoded | ||
84 | * as AM824 multi-bit linear audio | ||
85 | * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) | ||
75 | * | 86 | * |
76 | * The sample rate must be set before the stream is started, and must not be | 87 | * The parameters must be set before the stream is started, and must not be |
77 | * changed while the stream is running. | 88 | * changed while the stream is running. |
78 | */ | 89 | */ |
79 | void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) | 90 | void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s, |
91 | unsigned int rate, | ||
92 | unsigned int pcm_channels, | ||
93 | unsigned int midi_ports) | ||
80 | { | 94 | { |
81 | static const struct { | 95 | static const unsigned int rates[] = { |
82 | unsigned int rate; | 96 | [CIP_SFC_32000] = 32000, |
83 | unsigned int syt_interval; | 97 | [CIP_SFC_44100] = 44100, |
84 | } rate_info[] = { | 98 | [CIP_SFC_48000] = 48000, |
85 | [CIP_SFC_32000] = { 32000, 8, }, | 99 | [CIP_SFC_88200] = 88200, |
86 | [CIP_SFC_44100] = { 44100, 8, }, | 100 | [CIP_SFC_96000] = 96000, |
87 | [CIP_SFC_48000] = { 48000, 8, }, | 101 | [CIP_SFC_176400] = 176400, |
88 | [CIP_SFC_88200] = { 88200, 16, }, | 102 | [CIP_SFC_192000] = 192000, |
89 | [CIP_SFC_96000] = { 96000, 16, }, | ||
90 | [CIP_SFC_176400] = { 176400, 32, }, | ||
91 | [CIP_SFC_192000] = { 192000, 32, }, | ||
92 | }; | 103 | }; |
93 | unsigned int sfc; | 104 | unsigned int sfc; |
94 | 105 | ||
95 | if (WARN_ON(!IS_ERR(s->context))) | 106 | if (WARN_ON(amdtp_out_stream_running(s))) |
96 | return; | 107 | return; |
97 | 108 | ||
98 | for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc) | 109 | for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc) |
99 | if (rate_info[sfc].rate == rate) { | 110 | if (rates[sfc] == rate) |
100 | s->sfc = sfc; | 111 | goto sfc_found; |
101 | s->syt_interval = rate_info[sfc].syt_interval; | ||
102 | return; | ||
103 | } | ||
104 | WARN_ON(1); | 112 | WARN_ON(1); |
113 | return; | ||
114 | |||
115 | sfc_found: | ||
116 | s->dual_wire = (s->flags & CIP_HI_DUALWIRE) && sfc > CIP_SFC_96000; | ||
117 | if (s->dual_wire) { | ||
118 | sfc -= 2; | ||
119 | rate /= 2; | ||
120 | pcm_channels *= 2; | ||
121 | } | ||
122 | s->sfc = sfc; | ||
123 | s->data_block_quadlets = pcm_channels + DIV_ROUND_UP(midi_ports, 8); | ||
124 | s->pcm_channels = pcm_channels; | ||
125 | s->midi_ports = midi_ports; | ||
126 | |||
127 | s->syt_interval = amdtp_syt_intervals[sfc]; | ||
128 | |||
129 | /* default buffering in the device */ | ||
130 | s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; | ||
131 | if (s->flags & CIP_BLOCKING) | ||
132 | /* additional buffering needed to adjust for no-data packets */ | ||
133 | s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; | ||
105 | } | 134 | } |
106 | EXPORT_SYMBOL(amdtp_out_stream_set_rate); | 135 | EXPORT_SYMBOL(amdtp_out_stream_set_parameters); |
107 | 136 | ||
108 | /** | 137 | /** |
109 | * amdtp_out_stream_get_max_payload - get the stream's packet size | 138 | * amdtp_out_stream_get_max_payload - get the stream's packet size |
110 | * @s: the AMDTP output stream | 139 | * @s: the AMDTP output stream |
111 | * | 140 | * |
112 | * This function must not be called before the stream has been configured | 141 | * This function must not be called before the stream has been configured |
113 | * with amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and | 142 | * with amdtp_out_stream_set_parameters(). |
114 | * amdtp_out_stream_set_midi(). | ||
115 | */ | 143 | */ |
116 | unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s) | 144 | unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s) |
117 | { | 145 | { |
118 | static const unsigned int max_data_blocks[] = { | 146 | return 8 + s->syt_interval * s->data_block_quadlets * 4; |
119 | [CIP_SFC_32000] = 4, | ||
120 | [CIP_SFC_44100] = 6, | ||
121 | [CIP_SFC_48000] = 6, | ||
122 | [CIP_SFC_88200] = 12, | ||
123 | [CIP_SFC_96000] = 12, | ||
124 | [CIP_SFC_176400] = 23, | ||
125 | [CIP_SFC_192000] = 24, | ||
126 | }; | ||
127 | |||
128 | s->data_block_quadlets = s->pcm_channels; | ||
129 | s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8); | ||
130 | |||
131 | return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets; | ||
132 | } | 147 | } |
133 | EXPORT_SYMBOL(amdtp_out_stream_get_max_payload); | 148 | EXPORT_SYMBOL(amdtp_out_stream_get_max_payload); |
134 | 149 | ||
@@ -138,19 +153,26 @@ static void amdtp_write_s16(struct amdtp_out_stream *s, | |||
138 | static void amdtp_write_s32(struct amdtp_out_stream *s, | 153 | static void amdtp_write_s32(struct amdtp_out_stream *s, |
139 | struct snd_pcm_substream *pcm, | 154 | struct snd_pcm_substream *pcm, |
140 | __be32 *buffer, unsigned int frames); | 155 | __be32 *buffer, unsigned int frames); |
156 | static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s, | ||
157 | struct snd_pcm_substream *pcm, | ||
158 | __be32 *buffer, unsigned int frames); | ||
159 | static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s, | ||
160 | struct snd_pcm_substream *pcm, | ||
161 | __be32 *buffer, unsigned int frames); | ||
141 | 162 | ||
142 | /** | 163 | /** |
143 | * amdtp_out_stream_set_pcm_format - set the PCM format | 164 | * amdtp_out_stream_set_pcm_format - set the PCM format |
144 | * @s: the AMDTP output stream to configure | 165 | * @s: the AMDTP output stream to configure |
145 | * @format: the format of the ALSA PCM device | 166 | * @format: the format of the ALSA PCM device |
146 | * | 167 | * |
147 | * The sample format must be set before the stream is started, and must not be | 168 | * The sample format must be set after the other paramters (rate/PCM channels/ |
148 | * changed while the stream is running. | 169 | * MIDI) and before the stream is started, and must not be changed while the |
170 | * stream is running. | ||
149 | */ | 171 | */ |
150 | void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, | 172 | void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, |
151 | snd_pcm_format_t format) | 173 | snd_pcm_format_t format) |
152 | { | 174 | { |
153 | if (WARN_ON(!IS_ERR(s->context))) | 175 | if (WARN_ON(amdtp_out_stream_running(s))) |
154 | return; | 176 | return; |
155 | 177 | ||
156 | switch (format) { | 178 | switch (format) { |
@@ -158,10 +180,16 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, | |||
158 | WARN_ON(1); | 180 | WARN_ON(1); |
159 | /* fall through */ | 181 | /* fall through */ |
160 | case SNDRV_PCM_FORMAT_S16: | 182 | case SNDRV_PCM_FORMAT_S16: |
161 | s->transfer_samples = amdtp_write_s16; | 183 | if (s->dual_wire) |
184 | s->transfer_samples = amdtp_write_s16_dualwire; | ||
185 | else | ||
186 | s->transfer_samples = amdtp_write_s16; | ||
162 | break; | 187 | break; |
163 | case SNDRV_PCM_FORMAT_S32: | 188 | case SNDRV_PCM_FORMAT_S32: |
164 | s->transfer_samples = amdtp_write_s32; | 189 | if (s->dual_wire) |
190 | s->transfer_samples = amdtp_write_s32_dualwire; | ||
191 | else | ||
192 | s->transfer_samples = amdtp_write_s32; | ||
165 | break; | 193 | break; |
166 | } | 194 | } |
167 | } | 195 | } |
@@ -248,7 +276,7 @@ static unsigned int calculate_syt(struct amdtp_out_stream *s, | |||
248 | s->last_syt_offset = syt_offset; | 276 | s->last_syt_offset = syt_offset; |
249 | 277 | ||
250 | if (syt_offset < TICKS_PER_CYCLE) { | 278 | if (syt_offset < TICKS_PER_CYCLE) { |
251 | syt_offset += TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; | 279 | syt_offset += s->transfer_delay; |
252 | syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; | 280 | syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; |
253 | syt += syt_offset % TICKS_PER_CYCLE; | 281 | syt += syt_offset % TICKS_PER_CYCLE; |
254 | 282 | ||
@@ -310,6 +338,68 @@ static void amdtp_write_s16(struct amdtp_out_stream *s, | |||
310 | } | 338 | } |
311 | } | 339 | } |
312 | 340 | ||
341 | static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s, | ||
342 | struct snd_pcm_substream *pcm, | ||
343 | __be32 *buffer, unsigned int frames) | ||
344 | { | ||
345 | struct snd_pcm_runtime *runtime = pcm->runtime; | ||
346 | unsigned int channels, frame_adjust_1, frame_adjust_2, i, c; | ||
347 | const u32 *src; | ||
348 | |||
349 | channels = s->pcm_channels; | ||
350 | src = (void *)runtime->dma_area + | ||
351 | s->pcm_buffer_pointer * (runtime->frame_bits / 8); | ||
352 | frame_adjust_1 = channels - 1; | ||
353 | frame_adjust_2 = 1 - (s->data_block_quadlets - channels); | ||
354 | |||
355 | channels /= 2; | ||
356 | for (i = 0; i < frames; ++i) { | ||
357 | for (c = 0; c < channels; ++c) { | ||
358 | *buffer = cpu_to_be32((*src >> 8) | 0x40000000); | ||
359 | src++; | ||
360 | buffer += 2; | ||
361 | } | ||
362 | buffer -= frame_adjust_1; | ||
363 | for (c = 0; c < channels; ++c) { | ||
364 | *buffer = cpu_to_be32((*src >> 8) | 0x40000000); | ||
365 | src++; | ||
366 | buffer += 2; | ||
367 | } | ||
368 | buffer -= frame_adjust_2; | ||
369 | } | ||
370 | } | ||
371 | |||
372 | static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s, | ||
373 | struct snd_pcm_substream *pcm, | ||
374 | __be32 *buffer, unsigned int frames) | ||
375 | { | ||
376 | struct snd_pcm_runtime *runtime = pcm->runtime; | ||
377 | unsigned int channels, frame_adjust_1, frame_adjust_2, i, c; | ||
378 | const u16 *src; | ||
379 | |||
380 | channels = s->pcm_channels; | ||
381 | src = (void *)runtime->dma_area + | ||
382 | s->pcm_buffer_pointer * (runtime->frame_bits / 8); | ||
383 | frame_adjust_1 = channels - 1; | ||
384 | frame_adjust_2 = 1 - (s->data_block_quadlets - channels); | ||
385 | |||
386 | channels /= 2; | ||
387 | for (i = 0; i < frames; ++i) { | ||
388 | for (c = 0; c < channels; ++c) { | ||
389 | *buffer = cpu_to_be32((*src << 8) | 0x40000000); | ||
390 | src++; | ||
391 | buffer += 2; | ||
392 | } | ||
393 | buffer -= frame_adjust_1; | ||
394 | for (c = 0; c < channels; ++c) { | ||
395 | *buffer = cpu_to_be32((*src << 8) | 0x40000000); | ||
396 | src++; | ||
397 | buffer += 2; | ||
398 | } | ||
399 | buffer -= frame_adjust_2; | ||
400 | } | ||
401 | } | ||
402 | |||
313 | static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s, | 403 | static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s, |
314 | __be32 *buffer, unsigned int frames) | 404 | __be32 *buffer, unsigned int frames) |
315 | { | 405 | { |
@@ -344,8 +434,17 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) | |||
344 | return; | 434 | return; |
345 | index = s->packet_index; | 435 | index = s->packet_index; |
346 | 436 | ||
347 | data_blocks = calculate_data_blocks(s); | ||
348 | syt = calculate_syt(s, cycle); | 437 | syt = calculate_syt(s, cycle); |
438 | if (!(s->flags & CIP_BLOCKING)) { | ||
439 | data_blocks = calculate_data_blocks(s); | ||
440 | } else { | ||
441 | if (syt != 0xffff) { | ||
442 | data_blocks = s->syt_interval; | ||
443 | } else { | ||
444 | data_blocks = 0; | ||
445 | syt = 0xffffff; | ||
446 | } | ||
447 | } | ||
349 | 448 | ||
350 | buffer = s->buffer.packets[index].buffer; | 449 | buffer = s->buffer.packets[index].buffer; |
351 | buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | | 450 | buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | |
@@ -386,6 +485,9 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) | |||
386 | s->packet_index = index; | 485 | s->packet_index = index; |
387 | 486 | ||
388 | if (pcm) { | 487 | if (pcm) { |
488 | if (s->dual_wire) | ||
489 | data_blocks *= 2; | ||
490 | |||
389 | ptr = s->pcm_buffer_pointer + data_blocks; | 491 | ptr = s->pcm_buffer_pointer + data_blocks; |
390 | if (ptr >= pcm->runtime->buffer_size) | 492 | if (ptr >= pcm->runtime->buffer_size) |
391 | ptr -= pcm->runtime->buffer_size; | 493 | ptr -= pcm->runtime->buffer_size; |
@@ -455,9 +557,8 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s) | |||
455 | * @speed: firewire speed code | 557 | * @speed: firewire speed code |
456 | * | 558 | * |
457 | * The stream cannot be started until it has been configured with | 559 | * The stream cannot be started until it has been configured with |
458 | * amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and | 560 | * amdtp_out_stream_set_parameters() and amdtp_out_stream_set_pcm_format(), |
459 | * amdtp_out_stream_set_midi(); and it must be started before any | 561 | * and it must be started before any PCM or MIDI device can be started. |
460 | * PCM or MIDI device can be started. | ||
461 | */ | 562 | */ |
462 | int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) | 563 | int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) |
463 | { | 564 | { |
@@ -477,7 +578,7 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) | |||
477 | 578 | ||
478 | mutex_lock(&s->mutex); | 579 | mutex_lock(&s->mutex); |
479 | 580 | ||
480 | if (WARN_ON(!IS_ERR(s->context) || | 581 | if (WARN_ON(amdtp_out_stream_running(s) || |
481 | (!s->pcm_channels && !s->midi_ports))) { | 582 | (!s->pcm_channels && !s->midi_ports))) { |
482 | err = -EBADFD; | 583 | err = -EBADFD; |
483 | goto err_unlock; | 584 | goto err_unlock; |
@@ -573,7 +674,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s) | |||
573 | { | 674 | { |
574 | mutex_lock(&s->mutex); | 675 | mutex_lock(&s->mutex); |
575 | 676 | ||
576 | if (IS_ERR(s->context)) { | 677 | if (!amdtp_out_stream_running(s)) { |
577 | mutex_unlock(&s->mutex); | 678 | mutex_unlock(&s->mutex); |
578 | return; | 679 | return; |
579 | } | 680 | } |