diff options
-rw-r--r-- | sound/firewire/amdtp.c | 229 | ||||
-rw-r--r-- | sound/firewire/amdtp.h | 2 |
2 files changed, 214 insertions, 17 deletions
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 790aa86da825..be1aabcda0d2 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c | |||
@@ -39,6 +39,7 @@ | |||
39 | * only "Clock-based rate control mode" is supported | 39 | * only "Clock-based rate control mode" is supported |
40 | */ | 40 | */ |
41 | #define AMDTP_FDF_AM824 (0 << (CIP_FDF_SFC_SHIFT + 3)) | 41 | #define AMDTP_FDF_AM824 (0 << (CIP_FDF_SFC_SHIFT + 3)) |
42 | #define AMDTP_FDF_NO_DATA 0xff | ||
42 | #define AMDTP_DBS_MASK 0x00ff0000 | 43 | #define AMDTP_DBS_MASK 0x00ff0000 |
43 | #define AMDTP_DBS_SHIFT 16 | 44 | #define AMDTP_DBS_SHIFT 16 |
44 | #define AMDTP_DBC_MASK 0x000000ff | 45 | #define AMDTP_DBC_MASK 0x000000ff |
@@ -47,6 +48,7 @@ | |||
47 | #define INTERRUPT_INTERVAL 16 | 48 | #define INTERRUPT_INTERVAL 16 |
48 | #define QUEUE_LENGTH 48 | 49 | #define QUEUE_LENGTH 48 |
49 | 50 | ||
51 | #define IN_PACKET_HEADER_SIZE 4 | ||
50 | #define OUT_PACKET_HEADER_SIZE 0 | 52 | #define OUT_PACKET_HEADER_SIZE 0 |
51 | 53 | ||
52 | static void pcm_period_tasklet(unsigned long data); | 54 | static void pcm_period_tasklet(unsigned long data); |
@@ -179,6 +181,12 @@ static void amdtp_write_s16_dualwire(struct amdtp_stream *s, | |||
179 | static void amdtp_write_s32_dualwire(struct amdtp_stream *s, | 181 | static void amdtp_write_s32_dualwire(struct amdtp_stream *s, |
180 | struct snd_pcm_substream *pcm, | 182 | struct snd_pcm_substream *pcm, |
181 | __be32 *buffer, unsigned int frames); | 183 | __be32 *buffer, unsigned int frames); |
184 | static void amdtp_read_s32(struct amdtp_stream *s, | ||
185 | struct snd_pcm_substream *pcm, | ||
186 | __be32 *buffer, unsigned int frames); | ||
187 | static void amdtp_read_s32_dualwire(struct amdtp_stream *s, | ||
188 | struct snd_pcm_substream *pcm, | ||
189 | __be32 *buffer, unsigned int frames); | ||
182 | 190 | ||
183 | /** | 191 | /** |
184 | * amdtp_stream_set_pcm_format - set the PCM format | 192 | * amdtp_stream_set_pcm_format - set the PCM format |
@@ -200,16 +208,27 @@ void amdtp_stream_set_pcm_format(struct amdtp_stream *s, | |||
200 | WARN_ON(1); | 208 | WARN_ON(1); |
201 | /* fall through */ | 209 | /* fall through */ |
202 | case SNDRV_PCM_FORMAT_S16: | 210 | case SNDRV_PCM_FORMAT_S16: |
203 | if (s->dual_wire) | 211 | if (s->direction == AMDTP_OUT_STREAM) { |
204 | s->transfer_samples = amdtp_write_s16_dualwire; | 212 | if (s->dual_wire) |
205 | else | 213 | s->transfer_samples = amdtp_write_s16_dualwire; |
206 | s->transfer_samples = amdtp_write_s16; | 214 | else |
207 | break; | 215 | s->transfer_samples = amdtp_write_s16; |
216 | break; | ||
217 | } | ||
218 | WARN_ON(1); | ||
219 | /* fall through */ | ||
208 | case SNDRV_PCM_FORMAT_S32: | 220 | case SNDRV_PCM_FORMAT_S32: |
209 | if (s->dual_wire) | 221 | if (s->direction == AMDTP_OUT_STREAM) { |
210 | s->transfer_samples = amdtp_write_s32_dualwire; | 222 | if (s->dual_wire) |
211 | else | 223 | s->transfer_samples = amdtp_write_s32_dualwire; |
212 | s->transfer_samples = amdtp_write_s32; | 224 | else |
225 | s->transfer_samples = amdtp_write_s32; | ||
226 | } else { | ||
227 | if (s->dual_wire) | ||
228 | s->transfer_samples = amdtp_read_s32_dualwire; | ||
229 | else | ||
230 | s->transfer_samples = amdtp_read_s32; | ||
231 | } | ||
213 | break; | 232 | break; |
214 | } | 233 | } |
215 | } | 234 | } |
@@ -420,6 +439,59 @@ static void amdtp_write_s16_dualwire(struct amdtp_stream *s, | |||
420 | } | 439 | } |
421 | } | 440 | } |
422 | 441 | ||
442 | static void amdtp_read_s32(struct amdtp_stream *s, | ||
443 | struct snd_pcm_substream *pcm, | ||
444 | __be32 *buffer, unsigned int frames) | ||
445 | { | ||
446 | struct snd_pcm_runtime *runtime = pcm->runtime; | ||
447 | unsigned int channels, remaining_frames, i, c; | ||
448 | u32 *dst; | ||
449 | |||
450 | channels = s->pcm_channels; | ||
451 | dst = (void *)runtime->dma_area + | ||
452 | frames_to_bytes(runtime, s->pcm_buffer_pointer); | ||
453 | remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; | ||
454 | |||
455 | for (i = 0; i < frames; ++i) { | ||
456 | for (c = 0; c < channels; ++c) { | ||
457 | *dst = be32_to_cpu(buffer[c]) << 8; | ||
458 | dst++; | ||
459 | } | ||
460 | buffer += s->data_block_quadlets; | ||
461 | if (--remaining_frames == 0) | ||
462 | dst = (void *)runtime->dma_area; | ||
463 | } | ||
464 | } | ||
465 | |||
466 | static void amdtp_read_s32_dualwire(struct amdtp_stream *s, | ||
467 | struct snd_pcm_substream *pcm, | ||
468 | __be32 *buffer, unsigned int frames) | ||
469 | { | ||
470 | struct snd_pcm_runtime *runtime = pcm->runtime; | ||
471 | unsigned int channels, remaining_frames, i, c; | ||
472 | u32 *dst; | ||
473 | |||
474 | dst = (void *)runtime->dma_area + | ||
475 | frames_to_bytes(runtime, s->pcm_buffer_pointer); | ||
476 | remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; | ||
477 | channels = s->pcm_channels / 2; | ||
478 | |||
479 | for (i = 0; i < frames; ++i) { | ||
480 | for (c = 0; c < channels; ++c) { | ||
481 | *dst = be32_to_cpu(buffer[c * 2]) << 8; | ||
482 | dst++; | ||
483 | } | ||
484 | buffer += 1; | ||
485 | for (c = 0; c < channels; ++c) { | ||
486 | *dst = be32_to_cpu(buffer[c * 2]) << 8; | ||
487 | dst++; | ||
488 | } | ||
489 | buffer += s->data_block_quadlets - 1; | ||
490 | if (--remaining_frames == 0) | ||
491 | dst = (void *)runtime->dma_area; | ||
492 | } | ||
493 | } | ||
494 | |||
423 | static void amdtp_fill_pcm_silence(struct amdtp_stream *s, | 495 | static void amdtp_fill_pcm_silence(struct amdtp_stream *s, |
424 | __be32 *buffer, unsigned int frames) | 496 | __be32 *buffer, unsigned int frames) |
425 | { | 497 | { |
@@ -504,6 +576,12 @@ static inline int queue_out_packet(struct amdtp_stream *s, | |||
504 | payload_length, skip); | 576 | payload_length, skip); |
505 | } | 577 | } |
506 | 578 | ||
579 | static inline int queue_in_packet(struct amdtp_stream *s) | ||
580 | { | ||
581 | return queue_packet(s, IN_PACKET_HEADER_SIZE, | ||
582 | amdtp_stream_get_max_payload(s), false); | ||
583 | } | ||
584 | |||
507 | static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle) | 585 | static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle) |
508 | { | 586 | { |
509 | __be32 *buffer; | 587 | __be32 *buffer; |
@@ -552,6 +630,80 @@ static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle) | |||
552 | update_pcm_pointers(s, pcm, data_blocks); | 630 | update_pcm_pointers(s, pcm, data_blocks); |
553 | } | 631 | } |
554 | 632 | ||
633 | static void handle_in_packet(struct amdtp_stream *s, | ||
634 | unsigned int payload_quadlets, | ||
635 | __be32 *buffer) | ||
636 | { | ||
637 | u32 cip_header[2]; | ||
638 | unsigned int data_blocks, data_block_quadlets, data_block_counter; | ||
639 | struct snd_pcm_substream *pcm = NULL; | ||
640 | |||
641 | cip_header[0] = be32_to_cpu(buffer[0]); | ||
642 | cip_header[1] = be32_to_cpu(buffer[1]); | ||
643 | |||
644 | /* | ||
645 | * This module supports 'Two-quadlet CIP header with SYT field'. | ||
646 | * For convinience, also check FMT field is AM824 or not. | ||
647 | */ | ||
648 | if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) || | ||
649 | ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) || | ||
650 | ((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) { | ||
651 | dev_info_ratelimited(&s->unit->device, | ||
652 | "Invalid CIP header for AMDTP: %08X:%08X\n", | ||
653 | cip_header[0], cip_header[1]); | ||
654 | goto end; | ||
655 | } | ||
656 | |||
657 | /* Calculate data blocks */ | ||
658 | if (payload_quadlets < 3 || | ||
659 | ((cip_header[1] & CIP_FDF_MASK) == | ||
660 | (AMDTP_FDF_NO_DATA << CIP_FDF_SFC_SHIFT))) { | ||
661 | data_blocks = 0; | ||
662 | } else { | ||
663 | data_block_quadlets = | ||
664 | (cip_header[0] & AMDTP_DBS_MASK) >> AMDTP_DBS_SHIFT; | ||
665 | /* avoid division by zero */ | ||
666 | if (data_block_quadlets == 0) { | ||
667 | dev_info_ratelimited(&s->unit->device, | ||
668 | "Detect invalid value in dbs field: %08X\n", | ||
669 | cip_header[0]); | ||
670 | goto err; | ||
671 | } | ||
672 | |||
673 | data_blocks = (payload_quadlets - 2) / data_block_quadlets; | ||
674 | } | ||
675 | |||
676 | /* Check data block counter continuity */ | ||
677 | data_block_counter = cip_header[0] & AMDTP_DBC_MASK; | ||
678 | if (data_block_counter != s->data_block_counter) { | ||
679 | dev_info(&s->unit->device, | ||
680 | "Detect discontinuity of CIP: %02X %02X\n", | ||
681 | s->data_block_counter, data_block_counter); | ||
682 | goto err; | ||
683 | } | ||
684 | |||
685 | if (data_blocks > 0) { | ||
686 | buffer += 2; | ||
687 | |||
688 | pcm = ACCESS_ONCE(s->pcm); | ||
689 | if (pcm) | ||
690 | s->transfer_samples(s, pcm, buffer, data_blocks); | ||
691 | } | ||
692 | |||
693 | s->data_block_counter = (data_block_counter + data_blocks) & 0xff; | ||
694 | end: | ||
695 | if (queue_in_packet(s) < 0) | ||
696 | goto err; | ||
697 | |||
698 | if (pcm) | ||
699 | update_pcm_pointers(s, pcm, data_blocks); | ||
700 | |||
701 | return; | ||
702 | err: | ||
703 | s->packet_index = -1; | ||
704 | amdtp_stream_pcm_abort(s); | ||
705 | } | ||
706 | |||
555 | static void out_stream_callback(struct fw_iso_context *context, u32 cycle, | 707 | static void out_stream_callback(struct fw_iso_context *context, u32 cycle, |
556 | size_t header_length, void *header, | 708 | size_t header_length, void *header, |
557 | void *private_data) | 709 | void *private_data) |
@@ -571,6 +723,31 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle, | |||
571 | fw_iso_context_queue_flush(s->context); | 723 | fw_iso_context_queue_flush(s->context); |
572 | } | 724 | } |
573 | 725 | ||
726 | static void in_stream_callback(struct fw_iso_context *context, u32 cycle, | ||
727 | size_t header_length, void *header, | ||
728 | void *private_data) | ||
729 | { | ||
730 | struct amdtp_stream *s = private_data; | ||
731 | unsigned int p, packets, payload_quadlets; | ||
732 | __be32 *buffer, *headers = header; | ||
733 | |||
734 | /* The number of packets in buffer */ | ||
735 | packets = header_length / IN_PACKET_HEADER_SIZE; | ||
736 | |||
737 | for (p = 0; p < packets; p++) { | ||
738 | if (s->packet_index < 0) | ||
739 | return; | ||
740 | buffer = s->buffer.packets[s->packet_index].buffer; | ||
741 | |||
742 | /* The number of quadlets in this packet */ | ||
743 | payload_quadlets = | ||
744 | (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; | ||
745 | handle_in_packet(s, payload_quadlets, buffer); | ||
746 | } | ||
747 | |||
748 | fw_iso_context_queue_flush(s->context); | ||
749 | } | ||
750 | |||
574 | /** | 751 | /** |
575 | * amdtp_stream_start - start transferring packets | 752 | * amdtp_stream_start - start transferring packets |
576 | * @s: the AMDTP stream to start | 753 | * @s: the AMDTP stream to start |
@@ -595,7 +772,10 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) | |||
595 | [CIP_SFC_88200] = { 0, 67 }, | 772 | [CIP_SFC_88200] = { 0, 67 }, |
596 | [CIP_SFC_176400] = { 0, 67 }, | 773 | [CIP_SFC_176400] = { 0, 67 }, |
597 | }; | 774 | }; |
598 | int err; | 775 | unsigned int header_size; |
776 | enum dma_data_direction dir; | ||
777 | fw_iso_callback_t cb; | ||
778 | int type, err; | ||
599 | 779 | ||
600 | mutex_lock(&s->mutex); | 780 | mutex_lock(&s->mutex); |
601 | 781 | ||
@@ -610,16 +790,26 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) | |||
610 | s->syt_offset_state = initial_state[s->sfc].syt_offset; | 790 | s->syt_offset_state = initial_state[s->sfc].syt_offset; |
611 | s->last_syt_offset = TICKS_PER_CYCLE; | 791 | s->last_syt_offset = TICKS_PER_CYCLE; |
612 | 792 | ||
793 | /* initialize packet buffer */ | ||
794 | if (s->direction == AMDTP_IN_STREAM) { | ||
795 | dir = DMA_FROM_DEVICE; | ||
796 | type = FW_ISO_CONTEXT_RECEIVE; | ||
797 | header_size = IN_PACKET_HEADER_SIZE; | ||
798 | cb = in_stream_callback; | ||
799 | } else { | ||
800 | dir = DMA_TO_DEVICE; | ||
801 | type = FW_ISO_CONTEXT_TRANSMIT; | ||
802 | header_size = OUT_PACKET_HEADER_SIZE; | ||
803 | cb = out_stream_callback; | ||
804 | } | ||
613 | err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, | 805 | err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, |
614 | amdtp_stream_get_max_payload(s), | 806 | amdtp_stream_get_max_payload(s), dir); |
615 | DMA_TO_DEVICE); | ||
616 | if (err < 0) | 807 | if (err < 0) |
617 | goto err_unlock; | 808 | goto err_unlock; |
618 | 809 | ||
619 | s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, | 810 | s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, |
620 | FW_ISO_CONTEXT_TRANSMIT, | 811 | type, channel, speed, header_size, |
621 | channel, speed, 0, | 812 | cb, s); |
622 | out_stream_callback, s); | ||
623 | if (IS_ERR(s->context)) { | 813 | if (IS_ERR(s->context)) { |
624 | err = PTR_ERR(s->context); | 814 | err = PTR_ERR(s->context); |
625 | if (err == -EBUSY) | 815 | if (err == -EBUSY) |
@@ -632,12 +822,17 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) | |||
632 | 822 | ||
633 | s->packet_index = 0; | 823 | s->packet_index = 0; |
634 | do { | 824 | do { |
635 | err = queue_out_packet(s, 0, true); | 825 | if (s->direction == AMDTP_IN_STREAM) |
826 | err = queue_in_packet(s); | ||
827 | else | ||
828 | err = queue_out_packet(s, 0, true); | ||
636 | if (err < 0) | 829 | if (err < 0) |
637 | goto err_context; | 830 | goto err_context; |
638 | } while (s->packet_index > 0); | 831 | } while (s->packet_index > 0); |
639 | 832 | ||
640 | err = fw_iso_context_start(s->context, -1, 0, 0); | 833 | /* NOTE: TAG1 matches CIP. This just affects in stream. */ |
834 | err = fw_iso_context_start(s->context, -1, 0, | ||
835 | FW_ISO_CONTEXT_MATCH_TAG1); | ||
641 | if (err < 0) | 836 | if (err < 0) |
642 | goto err_context; | 837 | goto err_context; |
643 | 838 | ||
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 019134edb9f7..c831aaa2a811 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h | |||
@@ -41,6 +41,8 @@ enum cip_sfc { | |||
41 | CIP_SFC_COUNT | 41 | CIP_SFC_COUNT |
42 | }; | 42 | }; |
43 | 43 | ||
44 | #define AMDTP_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32 | ||
45 | |||
44 | #define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ | 46 | #define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ |
45 | SNDRV_PCM_FMTBIT_S32) | 47 | SNDRV_PCM_FMTBIT_S32) |
46 | 48 | ||