summaryrefslogtreecommitdiffstats
path: root/sound/firewire/amdtp.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-11-12 15:29:53 +0900
committerLinus Torvalds <torvalds@linux-foundation.org>2013-11-12 15:29:53 +0900
commiteeab517b68beb9e044e869bee18e3bdfa60e5aca (patch)
tree48a47e3223786919f664824842a5a23d7a8d99cd /sound/firewire/amdtp.c
parentf095ca6b31cfd20e6e7e0338ed8548d8a4374287 (diff)
parenta6bc732b5a96b5403c2637e85c350b95ec6591f3 (diff)
Merge tag 'sound-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "There are no too intrusive changes in this update batch. The biggest LOC is found in the new DICE driver, and other small changes are scattered over the whole sound subtree (which is a common pattern). Below are highlights: - ALSA core: * Memory allocation support with genpool * Fix blocking in drain ioctl of compress_offload - HD-audio: * Improved AMD HDMI supports * Intel HDMI detection improvements * thinkpad_acpi mute-key integration * New PCI ID, New ALC255,285,293 codecs, CX20952 - USB-audio: * New buffer size management * Clean up endpoint handling codes - ASoC: * Further work on the dmaengine helpers, including support for configuring the parameters for DMA by reading the capabilities of the DMA controller which removes some guesswork and magic numbers from drivers. * A refresh of the documentation. * Conversions of many drivers to direct regmap API usage in order to allow the ASoC level register I/O code to be removed, this will hopefully be completed by v3.14. * Support for using async register I/O in DAPM, reducing the time taken to implement power transitions on systems that support it. - Firewire: DICE driver - Lots of small fixes for bugs reported by Coverity" * tag 'sound-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (382 commits) ALSA: hda/realtek - Add new codec ALC255/ALC3234 UAJ supported ALSA: hda - Apply MacBook fixups for CS4208 correctly ASoC: fsl: imx-wm8962: remove an unneeded check ASoC: fsl: imx-pcm-fiq: Remove unused 'runtime' variable ALSA: hda/realtek - Make fixup regs persist after resume ALSA: hda_intel: ratelimit "spurious response" message ASoC: generic-dmaengine-pcm: Use SNDRV_DMA_TYPE_DEV_IRAM as default ASoC: dapm: Use WARN_ON() instead of BUG_ON() ASoC: wm_adsp: Fix BUG_ON() and WARN_ON() usages ASoC: Replace BUG() with WARN() ASoC: wm_hubs: Replace BUG() with WARN() ASoC: wm8996: Replace BUG() with WARN() ASoC: wm8962: Replace BUG() with WARN() ASoC: wm8958: Replace BUG() with WARN() ASoC: wm8904: Replace BUG() with WARN() ASoC: wm8900: Replace BUG() with WARN() ASoC: wm8350: Replace BUG() with WARN() ASoC: txx9: Use WARN_ON() instead of BUG_ON() ASoC: sh: Use WARN_ON() instead of BUG_ON() ASoC: rcar: Use WARN_ON() instead of BUG_ON() ...
Diffstat (limited to 'sound/firewire/amdtp.c')
-rw-r--r--sound/firewire/amdtp.c213
1 files changed, 157 insertions, 56 deletions
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index ea995af6d04..d3226892ad6 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -42,9 +42,6 @@ static void pcm_period_tasklet(unsigned long data);
int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
enum cip_out_flags flags)
{
- if (flags != CIP_NONBLOCKING)
- return -EINVAL;
-
s->unit = fw_unit_get(unit);
s->flags = flags;
s->context = ERR_PTR(-1);
@@ -62,73 +59,91 @@ EXPORT_SYMBOL(amdtp_out_stream_init);
*/
void amdtp_out_stream_destroy(struct amdtp_out_stream *s)
{
- WARN_ON(!IS_ERR(s->context));
+ WARN_ON(amdtp_out_stream_running(s));
mutex_destroy(&s->mutex);
fw_unit_put(s->unit);
}
EXPORT_SYMBOL(amdtp_out_stream_destroy);
+const 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
* @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.
*/
-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 {
- unsigned int rate;
- unsigned int syt_interval;
- } rate_info[] = {
- [CIP_SFC_32000] = { 32000, 8, },
- [CIP_SFC_44100] = { 44100, 8, },
- [CIP_SFC_48000] = { 48000, 8, },
- [CIP_SFC_88200] = { 88200, 16, },
- [CIP_SFC_96000] = { 96000, 16, },
- [CIP_SFC_176400] = { 176400, 32, },
- [CIP_SFC_192000] = { 192000, 32, },
+ static const unsigned int rates[] = {
+ [CIP_SFC_32000] = 32000,
+ [CIP_SFC_44100] = 44100,
+ [CIP_SFC_48000] = 48000,
+ [CIP_SFC_88200] = 88200,
+ [CIP_SFC_96000] = 96000,
+ [CIP_SFC_176400] = 176400,
+ [CIP_SFC_192000] = 192000,
};
unsigned int sfc;
- if (WARN_ON(!IS_ERR(s->context)))
+ if (WARN_ON(amdtp_out_stream_running(s)))
return;
- for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc)
- if (rate_info[sfc].rate == rate) {
- s->sfc = sfc;
- s->syt_interval = rate_info[sfc].syt_interval;
- return;
- }
+ for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc)
+ if (rates[sfc] == rate)
+ goto sfc_found;
WARN_ON(1);
+ return;
+
+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->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 */
+ s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
+ if (s->flags & CIP_BLOCKING)
+ /* additional buffering needed to adjust for no-data packets */
+ 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
* @s: the AMDTP output stream
*
* This function must not be called before the stream has been configured
- * with amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and
- * amdtp_out_stream_set_midi().
+ * with amdtp_out_stream_set_parameters().
*/
unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s)
{
- static const unsigned int max_data_blocks[] = {
- [CIP_SFC_32000] = 4,
- [CIP_SFC_44100] = 6,
- [CIP_SFC_48000] = 6,
- [CIP_SFC_88200] = 12,
- [CIP_SFC_96000] = 12,
- [CIP_SFC_176400] = 23,
- [CIP_SFC_192000] = 24,
- };
-
- s->data_block_quadlets = s->pcm_channels;
- s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8);
-
- return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets;
+ return 8 + s->syt_interval * s->data_block_quadlets * 4;
}
EXPORT_SYMBOL(amdtp_out_stream_get_max_payload);
@@ -138,19 +153,26 @@ static void amdtp_write_s16(struct amdtp_out_stream *s,
static void amdtp_write_s32(struct amdtp_out_stream *s,
struct snd_pcm_substream *pcm,
__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
* @s: the AMDTP output stream to configure
* @format: the format of the ALSA PCM device
*
- * The sample format must be set before the stream is started, and must not be
- * changed while the stream is running.
+ * The sample format must be set after the other paramters (rate/PCM channels/
+ * 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,
snd_pcm_format_t format)
{
- if (WARN_ON(!IS_ERR(s->context)))
+ if (WARN_ON(amdtp_out_stream_running(s)))
return;
switch (format) {
@@ -158,10 +180,16 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
WARN_ON(1);
/* fall through */
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;
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;
}
}
@@ -248,7 +276,7 @@ static unsigned int calculate_syt(struct amdtp_out_stream *s,
s->last_syt_offset = syt_offset;
if (syt_offset < TICKS_PER_CYCLE) {
- syt_offset += TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
+ syt_offset += s->transfer_delay;
syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
syt += syt_offset % TICKS_PER_CYCLE;
@@ -268,7 +296,7 @@ static void amdtp_write_s32(struct amdtp_out_stream *s,
channels = s->pcm_channels;
src = (void *)runtime->dma_area +
- s->pcm_buffer_pointer * (runtime->frame_bits / 8);
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frame_step = s->data_block_quadlets - channels;
@@ -294,7 +322,7 @@ static void amdtp_write_s16(struct amdtp_out_stream *s,
channels = s->pcm_channels;
src = (void *)runtime->dma_area +
- s->pcm_buffer_pointer * (runtime->frame_bits / 8);
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
frame_step = s->data_block_quadlets - channels;
@@ -310,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,
__be32 *buffer, unsigned int frames)
{
@@ -344,8 +434,17 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
return;
index = s->packet_index;
- data_blocks = calculate_data_blocks(s);
syt = calculate_syt(s, cycle);
+ if (!(s->flags & CIP_BLOCKING)) {
+ data_blocks = calculate_data_blocks(s);
+ } else {
+ if (syt != 0xffff) {
+ data_blocks = s->syt_interval;
+ } else {
+ data_blocks = 0;
+ syt = 0xffffff;
+ }
+ }
buffer = s->buffer.packets[index].buffer;
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)
s->packet_index = index;
if (pcm) {
+ if (s->dual_wire)
+ data_blocks *= 2;
+
ptr = s->pcm_buffer_pointer + data_blocks;
if (ptr >= pcm->runtime->buffer_size)
ptr -= pcm->runtime->buffer_size;
@@ -455,9 +557,8 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s)
* @speed: firewire speed code
*
* The stream cannot be started until it has been configured with
- * amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and
- * amdtp_out_stream_set_midi(); and it must be started before any
- * PCM or MIDI device can be started.
+ * amdtp_out_stream_set_parameters() and amdtp_out_stream_set_pcm_format(),
+ * 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)
{
@@ -477,7 +578,7 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
mutex_lock(&s->mutex);
- if (WARN_ON(!IS_ERR(s->context) ||
+ if (WARN_ON(amdtp_out_stream_running(s) ||
(!s->pcm_channels && !s->midi_ports))) {
err = -EBADFD;
goto err_unlock;
@@ -573,7 +674,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s)
{
mutex_lock(&s->mutex);
- if (IS_ERR(s->context)) {
+ if (!amdtp_out_stream_running(s)) {
mutex_unlock(&s->mutex);
return;
}