Commit a7304e3b authored by Clemens Ladisch's avatar Clemens Ladisch

ALSA: dice: support dual-wire stream format at 192 kHz

Change the AMDTP streaming code to handle the non-standard stream format
that DICE devices use at sample rates greater than 96 kHz.
Signed-off-by: default avatarClemens Ladisch <clemens@ladisch.de>
parent cbab328d
...@@ -65,42 +65,66 @@ void amdtp_out_stream_destroy(struct amdtp_out_stream *s) ...@@ -65,42 +65,66 @@ void amdtp_out_stream_destroy(struct amdtp_out_stream *s)
} }
EXPORT_SYMBOL(amdtp_out_stream_destroy); EXPORT_SYMBOL(amdtp_out_stream_destroy);
unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
[CIP_SFC_32000] = 8,
[CIP_SFC_44100] = 8,
[CIP_SFC_48000] = 8,
[CIP_SFC_88200] = 16,
[CIP_SFC_96000] = 16,
[CIP_SFC_176400] = 32,
[CIP_SFC_192000] = 32,
};
EXPORT_SYMBOL(amdtp_syt_intervals);
/** /**
* amdtp_out_stream_set_rate - set the sample rate * amdtp_out_stream_set_parameters - set stream parameters
* @s: the AMDTP output stream to configure * @s: the AMDTP output stream to configure
* @rate: the sample rate * @rate: the sample rate
* @pcm_channels: the number of PCM samples in each data block, to be encoded
* as AM824 multi-bit linear audio
* @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
* *
* The sample rate must be set before the stream is started, and must not be * The parameters must be set before the stream is started, and must not be
* changed while the stream is running. * changed while the stream is running.
*/ */
void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
unsigned int rate,
unsigned int pcm_channels,
unsigned int midi_ports)
{ {
static const struct { static const unsigned int rates[] = {
unsigned int rate; [CIP_SFC_32000] = 32000,
unsigned int syt_interval; [CIP_SFC_44100] = 44100,
} rate_info[] = { [CIP_SFC_48000] = 48000,
[CIP_SFC_32000] = { 32000, 8, }, [CIP_SFC_88200] = 88200,
[CIP_SFC_44100] = { 44100, 8, }, [CIP_SFC_96000] = 96000,
[CIP_SFC_48000] = { 48000, 8, }, [CIP_SFC_176400] = 176400,
[CIP_SFC_88200] = { 88200, 16, }, [CIP_SFC_192000] = 192000,
[CIP_SFC_96000] = { 96000, 16, },
[CIP_SFC_176400] = { 176400, 32, },
[CIP_SFC_192000] = { 192000, 32, },
}; };
unsigned int sfc; unsigned int sfc;
if (WARN_ON(amdtp_out_stream_running(s))) if (WARN_ON(amdtp_out_stream_running(s)))
return; return;
for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc) for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc)
if (rate_info[sfc].rate == rate) if (rates[sfc] == rate)
goto sfc_found; goto sfc_found;
WARN_ON(1); WARN_ON(1);
return; return;
sfc_found: sfc_found:
s->dual_wire = (s->flags & CIP_HI_DUALWIRE) && sfc > CIP_SFC_96000;
if (s->dual_wire) {
sfc -= 2;
rate /= 2;
pcm_channels *= 2;
}
s->sfc = sfc; s->sfc = sfc;
s->syt_interval = rate_info[sfc].syt_interval; s->data_block_quadlets = pcm_channels + DIV_ROUND_UP(midi_ports, 8);
s->pcm_channels = pcm_channels;
s->midi_ports = midi_ports;
s->syt_interval = amdtp_syt_intervals[sfc];
/* default buffering in the device */ /* default buffering in the device */
s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
...@@ -108,21 +132,17 @@ void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) ...@@ -108,21 +132,17 @@ void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate)
/* additional buffering needed to adjust for no-data packets */ /* additional buffering needed to adjust for no-data packets */
s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
} }
EXPORT_SYMBOL(amdtp_out_stream_set_rate); EXPORT_SYMBOL(amdtp_out_stream_set_parameters);
/** /**
* amdtp_out_stream_get_max_payload - get the stream's packet size * amdtp_out_stream_get_max_payload - get the stream's packet size
* @s: the AMDTP output stream * @s: the AMDTP output stream
* *
* This function must not be called before the stream has been configured * This function must not be called before the stream has been configured
* with amdtp_out_stream_set_rate(), amdtp_out_stream_set_pcm(), and * with amdtp_out_stream_set_parameters().
* amdtp_out_stream_set_midi().
*/ */
unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s) unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s)
{ {
s->data_block_quadlets = s->pcm_channels;
s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8);
return 8 + s->syt_interval * s->data_block_quadlets * 4; return 8 + s->syt_interval * s->data_block_quadlets * 4;
} }
EXPORT_SYMBOL(amdtp_out_stream_get_max_payload); EXPORT_SYMBOL(amdtp_out_stream_get_max_payload);
...@@ -133,14 +153,21 @@ static void amdtp_write_s16(struct amdtp_out_stream *s, ...@@ -133,14 +153,21 @@ static void amdtp_write_s16(struct amdtp_out_stream *s,
static void amdtp_write_s32(struct amdtp_out_stream *s, static void amdtp_write_s32(struct amdtp_out_stream *s,
struct snd_pcm_substream *pcm, struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames); __be32 *buffer, unsigned int frames);
static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames);
static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames);
/** /**
* amdtp_out_stream_set_pcm_format - set the PCM format * amdtp_out_stream_set_pcm_format - set the PCM format
* @s: the AMDTP output stream to configure * @s: the AMDTP output stream to configure
* @format: the format of the ALSA PCM device * @format: the format of the ALSA PCM device
* *
* The sample format must be set before the stream is started, and must not be * The sample format must be set after the other paramters (rate/PCM channels/
* changed while the stream is running. * MIDI) and before the stream is started, and must not be changed while the
* stream is running.
*/ */
void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
snd_pcm_format_t format) snd_pcm_format_t format)
...@@ -153,10 +180,16 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, ...@@ -153,10 +180,16 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
WARN_ON(1); WARN_ON(1);
/* fall through */ /* fall through */
case SNDRV_PCM_FORMAT_S16: case SNDRV_PCM_FORMAT_S16:
s->transfer_samples = amdtp_write_s16; if (s->dual_wire)
s->transfer_samples = amdtp_write_s16_dualwire;
else
s->transfer_samples = amdtp_write_s16;
break; break;
case SNDRV_PCM_FORMAT_S32: case SNDRV_PCM_FORMAT_S32:
s->transfer_samples = amdtp_write_s32; if (s->dual_wire)
s->transfer_samples = amdtp_write_s32_dualwire;
else
s->transfer_samples = amdtp_write_s32;
break; break;
} }
} }
...@@ -305,6 +338,68 @@ static void amdtp_write_s16(struct amdtp_out_stream *s, ...@@ -305,6 +338,68 @@ static void amdtp_write_s16(struct amdtp_out_stream *s,
} }
} }
static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, frame_adjust_1, frame_adjust_2, i, c;
const u32 *src;
channels = s->pcm_channels;
src = (void *)runtime->dma_area +
s->pcm_buffer_pointer * (runtime->frame_bits / 8);
frame_adjust_1 = channels - 1;
frame_adjust_2 = 1 - (s->data_block_quadlets - channels);
channels /= 2;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
*buffer = cpu_to_be32((*src >> 8) | 0x40000000);
src++;
buffer += 2;
}
buffer -= frame_adjust_1;
for (c = 0; c < channels; ++c) {
*buffer = cpu_to_be32((*src >> 8) | 0x40000000);
src++;
buffer += 2;
}
buffer -= frame_adjust_2;
}
}
static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, frame_adjust_1, frame_adjust_2, i, c;
const u16 *src;
channels = s->pcm_channels;
src = (void *)runtime->dma_area +
s->pcm_buffer_pointer * (runtime->frame_bits / 8);
frame_adjust_1 = channels - 1;
frame_adjust_2 = 1 - (s->data_block_quadlets - channels);
channels /= 2;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
*buffer = cpu_to_be32((*src << 8) | 0x40000000);
src++;
buffer += 2;
}
buffer -= frame_adjust_1;
for (c = 0; c < channels; ++c) {
*buffer = cpu_to_be32((*src << 8) | 0x40000000);
src++;
buffer += 2;
}
buffer -= frame_adjust_2;
}
}
static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s, static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s,
__be32 *buffer, unsigned int frames) __be32 *buffer, unsigned int frames)
{ {
...@@ -390,6 +485,9 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) ...@@ -390,6 +485,9 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
s->packet_index = index; s->packet_index = index;
if (pcm) { if (pcm) {
if (s->dual_wire)
data_blocks *= 2;
ptr = s->pcm_buffer_pointer + data_blocks; ptr = s->pcm_buffer_pointer + data_blocks;
if (ptr >= pcm->runtime->buffer_size) if (ptr >= pcm->runtime->buffer_size)
ptr -= pcm->runtime->buffer_size; ptr -= pcm->runtime->buffer_size;
...@@ -459,8 +557,7 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s) ...@@ -459,8 +557,7 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s)
* @speed: firewire speed code * @speed: firewire speed code
* *
* The stream cannot be started until it has been configured with * The stream cannot be started until it has been configured with
* amdtp_out_stream_set_rate(), amdtp_out_stream_set_pcm(), * amdtp_out_stream_set_parameters() and amdtp_out_stream_set_pcm_format(),
* amdtp_out_stream_set_midi(), and amdtp_out_stream_set_format();
* and it must be started before any PCM or MIDI device can be started. * and it must be started before any PCM or MIDI device can be started.
*/ */
int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
......
...@@ -15,10 +15,15 @@ ...@@ -15,10 +15,15 @@
* @CIP_BLOCKING: In blocking mode, each packet contains either zero or * @CIP_BLOCKING: In blocking mode, each packet contains either zero or
* SYT_INTERVAL samples, with these two types alternating so that * SYT_INTERVAL samples, with these two types alternating so that
* the overall sample rate comes out right. * the overall sample rate comes out right.
* @CIP_HI_DUALWIRE: At rates above 96 kHz, pretend that the stream runs
* at half the actual sample rate with twice the number of channels;
* two samples of a channel are stored consecutively in the packet.
* Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size.
*/ */
enum cip_out_flags { enum cip_out_flags {
CIP_NONBLOCKING = 0x00, CIP_NONBLOCKING = 0x00,
CIP_BLOCKING = 0x01, CIP_BLOCKING = 0x01,
CIP_HI_DUALWIRE = 0x02,
}; };
/** /**
...@@ -32,6 +37,7 @@ enum cip_sfc { ...@@ -32,6 +37,7 @@ enum cip_sfc {
CIP_SFC_96000 = 4, CIP_SFC_96000 = 4,
CIP_SFC_176400 = 5, CIP_SFC_176400 = 5,
CIP_SFC_192000 = 6, CIP_SFC_192000 = 6,
CIP_SFC_COUNT
}; };
#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ #define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \
...@@ -48,6 +54,7 @@ struct amdtp_out_stream { ...@@ -48,6 +54,7 @@ struct amdtp_out_stream {
struct mutex mutex; struct mutex mutex;
enum cip_sfc sfc; enum cip_sfc sfc;
bool dual_wire;
unsigned int data_block_quadlets; unsigned int data_block_quadlets;
unsigned int pcm_channels; unsigned int pcm_channels;
unsigned int midi_ports; unsigned int midi_ports;
...@@ -80,7 +87,10 @@ int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, ...@@ -80,7 +87,10 @@ int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
enum cip_out_flags flags); enum cip_out_flags flags);
void amdtp_out_stream_destroy(struct amdtp_out_stream *s); void amdtp_out_stream_destroy(struct amdtp_out_stream *s);
void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate); void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
unsigned int rate,
unsigned int pcm_channels,
unsigned int midi_ports);
unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s); unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s);
int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed); int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed);
...@@ -93,38 +103,13 @@ void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s); ...@@ -93,38 +103,13 @@ void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s);
unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s); unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s);
void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s); void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s);
extern unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s) static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s)
{ {
return !IS_ERR(s->context); return !IS_ERR(s->context);
} }
/**
* amdtp_out_stream_set_pcm - configure format of PCM samples
* @s: the AMDTP output stream to be configured
* @pcm_channels: the number of PCM samples in each data block, to be encoded
* as AM824 multi-bit linear audio
*
* This function must not be called while the stream is running.
*/
static inline void amdtp_out_stream_set_pcm(struct amdtp_out_stream *s,
unsigned int pcm_channels)
{
s->pcm_channels = pcm_channels;
}
/**
* amdtp_out_stream_set_midi - configure format of MIDI data
* @s: the AMDTP output stream to be configured
* @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
*
* This function must not be called while the stream is running.
*/
static inline void amdtp_out_stream_set_midi(struct amdtp_out_stream *s,
unsigned int midi_ports)
{
s->midi_ports = midi_ports;
}
/** /**
* amdtp_out_streaming_error - check for streaming error * amdtp_out_streaming_error - check for streaming error
* @s: the AMDTP output stream * @s: the AMDTP output stream
......
...@@ -375,7 +375,7 @@ static int dice_open(struct snd_pcm_substream *substream) ...@@ -375,7 +375,7 @@ static int dice_open(struct snd_pcm_substream *substream)
struct dice *dice = substream->private_data; struct dice *dice = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
__be32 clock_sel, number_audio, number_midi; __be32 clock_sel, number_audio, number_midi;
unsigned int rate; unsigned int rate_index, rate;
int err; int err;
err = dice_try_lock(dice); err = dice_try_lock(dice);
...@@ -387,12 +387,13 @@ static int dice_open(struct snd_pcm_substream *substream) ...@@ -387,12 +387,13 @@ static int dice_open(struct snd_pcm_substream *substream)
&clock_sel, 4); &clock_sel, 4);
if (err < 0) if (err < 0)
goto err_lock; goto err_lock;
rate = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT; rate_index = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK)
if (rate >= ARRAY_SIZE(dice_rates)) { >> CLOCK_RATE_SHIFT;
if (rate_index >= ARRAY_SIZE(dice_rates)) {
err = -ENXIO; err = -ENXIO;
goto err_lock; goto err_lock;
} }
rate = dice_rates[rate]; rate = dice_rates[rate_index];
err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
rx_address(dice, RX_NUMBER_AUDIO), rx_address(dice, RX_NUMBER_AUDIO),
...@@ -413,9 +414,20 @@ static int dice_open(struct snd_pcm_substream *substream) ...@@ -413,9 +414,20 @@ static int dice_open(struct snd_pcm_substream *substream)
runtime->hw.channels_min = be32_to_cpu(number_audio); runtime->hw.channels_min = be32_to_cpu(number_audio);
runtime->hw.channels_max = be32_to_cpu(number_audio); runtime->hw.channels_max = be32_to_cpu(number_audio);
amdtp_out_stream_set_rate(&dice->stream, rate); amdtp_out_stream_set_parameters(&dice->stream, rate,
amdtp_out_stream_set_pcm(&dice->stream, be32_to_cpu(number_audio)); be32_to_cpu(number_audio),
amdtp_out_stream_set_midi(&dice->stream, be32_to_cpu(number_midi)); be32_to_cpu(number_midi));
err = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
amdtp_syt_intervals[rate_index]);
if (err < 0)
goto err_lock;
err = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
amdtp_syt_intervals[rate_index]);
if (err < 0)
goto err_lock;
err = snd_pcm_hw_constraint_minmax(runtime, err = snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_PERIOD_TIME, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
...@@ -993,7 +1005,8 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) ...@@ -993,7 +1005,8 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
goto err_notification_handler; goto err_notification_handler;
dice->resources.channels_mask = 0x00000000ffffffffuLL; dice->resources.channels_mask = 0x00000000ffffffffuLL;
err = amdtp_out_stream_init(&dice->stream, unit, CIP_BLOCKING); err = amdtp_out_stream_init(&dice->stream, unit,
CIP_BLOCKING | CIP_HI_DUALWIRE);
if (err < 0) if (err < 0)
goto err_resources; goto err_resources;
......
...@@ -245,8 +245,10 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream, ...@@ -245,8 +245,10 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream,
if (err < 0) if (err < 0)
goto error; goto error;
amdtp_out_stream_set_rate(&fwspk->stream, params_rate(hw_params)); amdtp_out_stream_set_parameters(&fwspk->stream,
amdtp_out_stream_set_pcm(&fwspk->stream, params_channels(hw_params)); params_rate(hw_params),
params_channels(hw_params),
0);
amdtp_out_stream_set_pcm_format(&fwspk->stream, amdtp_out_stream_set_pcm_format(&fwspk->stream,
params_format(hw_params)); params_format(hw_params));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment