diff options
Diffstat (limited to 'sound/firewire/amdtp-stream.h')
| -rw-r--r-- | sound/firewire/amdtp-stream.h | 211 |
1 files changed, 168 insertions, 43 deletions
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index e45de3eecfe3..ec10270c2cce 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -32,7 +32,14 @@ * allows 5 times as large as IEC 61883-6 defines. * @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include * valid EOH. - * @CIP_NO_HEADERS: a lack of headers in packets + * @CIP_NO_HEADER: a lack of headers in packets + * @CIP_UNALIGHED_DBC: Only for in-stream. The value of dbc is not alighed to + * the value of current SYT_INTERVAL; e.g. initial value is not zero. + * @CIP_UNAWARE_SYT: For outgoing packet, the value in SYT field of CIP is 0xffff. + * For incoming packet, the value in SYT field of CIP is not handled. + * @CIP_DBC_IS_PAYLOAD_QUADLETS: Available for incoming packet, and only effective with + * CIP_DBC_IS_END_EVENT flag. The value of dbc field is the number of accumulated quadlets + * in CIP payload, instead of the number of accumulated data blocks. */ enum cip_flags { CIP_NONBLOCKING = 0x00, @@ -45,6 +52,9 @@ enum cip_flags { CIP_JUMBO_PAYLOAD = 0x40, CIP_HEADER_WITHOUT_EOH = 0x80, CIP_NO_HEADER = 0x100, + CIP_UNALIGHED_DBC = 0x200, + CIP_UNAWARE_SYT = 0x400, + CIP_DBC_IS_PAYLOAD_QUADLETS = 0x800, }; /** @@ -91,27 +101,81 @@ enum amdtp_stream_direction { AMDTP_IN_STREAM }; +struct pkt_desc { + u32 cycle; + u32 syt; + unsigned int data_blocks; + unsigned int data_block_counter; + __be32 *ctx_payload; + struct list_head link; +}; + struct amdtp_stream; -typedef unsigned int (*amdtp_stream_process_data_blocks_t)( - struct amdtp_stream *s, - __be32 *buffer, - unsigned int data_blocks, - unsigned int *syt); +typedef void (*amdtp_stream_process_ctx_payloads_t)(struct amdtp_stream *s, + const struct pkt_desc *desc, + unsigned int count, + struct snd_pcm_substream *pcm); + +struct amdtp_domain; struct amdtp_stream { struct fw_unit *unit; - enum cip_flags flags; + // The combination of cip_flags enumeration-constants. + unsigned int flags; enum amdtp_stream_direction direction; struct mutex mutex; /* For packet processing. */ struct fw_iso_context *context; struct iso_packets_buffer buffer; + unsigned int queue_size; int packet_index; + struct pkt_desc *packet_descs; + struct list_head packet_descs_list; + struct pkt_desc *packet_descs_cursor; int tag; - int (*handle_packet)(struct amdtp_stream *s, - unsigned int payload_quadlets, unsigned int cycle, - unsigned int index); - unsigned int max_payload_length; + union { + struct { + unsigned int ctx_header_size; + + // limit for payload of iso packet. + unsigned int max_ctx_payload_length; + + // For quirks of CIP headers. + // Fixed interval of dbc between previos/current + // packets. + unsigned int dbc_interval; + + // The device starts multiplexing events to the packet. + bool event_starts; + + struct { + struct seq_desc *descs; + unsigned int size; + unsigned int pos; + } cache; + } tx; + struct { + // To generate CIP header. + unsigned int fdf; + + // To generate constant hardware IRQ. + unsigned int event_count; + + // To calculate CIP data blocks and tstamp. + struct { + struct seq_desc *descs; + unsigned int size; + unsigned int pos; + } seq; + + unsigned int data_block_state; + unsigned int syt_offset_state; + unsigned int last_syt_offset; + + struct amdtp_stream *replay_target; + unsigned int cache_pos; + } rx; + } ctx_data; /* For CIP headers. */ unsigned int source_node_id_field; @@ -119,57 +183,53 @@ struct amdtp_stream { unsigned int data_block_counter; unsigned int sph; unsigned int fmt; - unsigned int fdf; - /* quirk: fixed interval of dbc between previos/current packets. */ - unsigned int tx_dbc_interval; - /* quirk: indicate the value of dbc field in a first packet. */ - unsigned int tx_first_dbc; - /* Internal flags. */ + // Internal flags. + unsigned int transfer_delay; enum cip_sfc sfc; unsigned int syt_interval; - unsigned int transfer_delay; - unsigned int data_block_state; - unsigned int last_syt_offset; - unsigned int syt_offset_state; /* For a PCM substream processing. */ struct snd_pcm_substream *pcm; - struct tasklet_struct period_tasklet; + struct work_struct period_work; snd_pcm_uframes_t pcm_buffer_pointer; unsigned int pcm_period_pointer; + unsigned int pcm_frame_multiplier; - /* To wait for first packet. */ - bool callbacked; - wait_queue_head_t callback_wait; - u32 start_cycle; + // To start processing content of packets at the same cycle in several contexts for + // each direction. + bool ready_processing; + wait_queue_head_t ready_wait; + unsigned int next_cycle; /* For backends to process data blocks. */ void *protocol; - amdtp_stream_process_data_blocks_t process_data_blocks; + amdtp_stream_process_ctx_payloads_t process_ctx_payloads; + + // For domain. + int channel; + int speed; + struct list_head list; + struct amdtp_domain *domain; }; int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, - enum amdtp_stream_direction dir, enum cip_flags flags, + enum amdtp_stream_direction dir, unsigned int flags, unsigned int fmt, - amdtp_stream_process_data_blocks_t process_data_blocks, + amdtp_stream_process_ctx_payloads_t process_ctx_payloads, unsigned int protocol_size); void amdtp_stream_destroy(struct amdtp_stream *s); int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, - unsigned int data_block_quadlets); + unsigned int data_block_quadlets, unsigned int pcm_frame_multiplier); unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); -int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed); void amdtp_stream_update(struct amdtp_stream *s); -void amdtp_stream_stop(struct amdtp_stream *s); int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); void amdtp_stream_pcm_prepare(struct amdtp_stream *s); -unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s); -int amdtp_stream_pcm_ack(struct amdtp_stream *s); void amdtp_stream_pcm_abort(struct amdtp_stream *s); extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT]; @@ -224,24 +284,89 @@ static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s, WRITE_ONCE(s->pcm, pcm); } +/** + * amdtp_stream_next_packet_desc - retrieve next descriptor for amdtp packet. + * @s: the AMDTP stream + * @desc: the descriptor of packet + * + * This macro computes next descriptor so that the list of descriptors behaves circular queue. + */ +#define amdtp_stream_next_packet_desc(s, desc) \ + list_next_entry_circular(desc, &s->packet_descs_list, link) + static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) { return sfc & 1; } +struct seq_desc { + unsigned int syt_offset; + unsigned int data_blocks; +}; + +struct amdtp_domain { + struct list_head streams; + + unsigned int events_per_period; + unsigned int events_per_buffer; + + struct amdtp_stream *irq_target; + + struct { + unsigned int tx_init_skip; + unsigned int tx_start; + unsigned int rx_start; + } processing_cycle; + + struct { + bool enable:1; + bool on_the_fly:1; + } replay; +}; + +int amdtp_domain_init(struct amdtp_domain *d); +void amdtp_domain_destroy(struct amdtp_domain *d); + +int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s, + int channel, int speed); + +int amdtp_domain_start(struct amdtp_domain *d, unsigned int tx_init_skip_cycles, bool replay_seq, + bool replay_on_the_fly); +void amdtp_domain_stop(struct amdtp_domain *d); + +static inline int amdtp_domain_set_events_per_period(struct amdtp_domain *d, + unsigned int events_per_period, + unsigned int events_per_buffer) +{ + d->events_per_period = events_per_period; + d->events_per_buffer = events_per_buffer; + + return 0; +} + +unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d, + struct amdtp_stream *s); +int amdtp_domain_stream_pcm_ack(struct amdtp_domain *d, struct amdtp_stream *s); + /** - * amdtp_stream_wait_callback - sleep till callbacked or timeout - * @s: the AMDTP stream - * @timeout: msec till timeout + * amdtp_domain_wait_ready - sleep till being ready to process packets or timeout + * @d: the AMDTP domain + * @timeout_ms: msec till timeout * - * If this function return false, the AMDTP stream should be stopped. + * If this function return false, the AMDTP domain should be stopped. */ -static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s, - unsigned int timeout) +static inline bool amdtp_domain_wait_ready(struct amdtp_domain *d, unsigned int timeout_ms) { - return wait_event_timeout(s->callback_wait, - s->callbacked == true, - msecs_to_jiffies(timeout)) > 0; + struct amdtp_stream *s; + + list_for_each_entry(s, &d->streams, list) { + unsigned int j = msecs_to_jiffies(timeout_ms); + + if (wait_event_interruptible_timeout(s->ready_wait, s->ready_processing, j) <= 0) + return false; + } + + return true; } #endif |
