You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'for-next' into for-linus
This commit is contained in:
+11
-2
@@ -15,6 +15,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma/pxa-dma.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
@@ -43,7 +44,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
|
||||
.reset = pxa2xx_ac97_reset,
|
||||
};
|
||||
|
||||
static unsigned long pxa2xx_ac97_pcm_out_req = 12;
|
||||
static struct pxad_param pxa2xx_ac97_pcm_out_req = {
|
||||
.prio = PXAD_PRIO_LOWEST,
|
||||
.drcmr = 12,
|
||||
};
|
||||
|
||||
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = {
|
||||
.addr = __PREG(PCDR),
|
||||
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
|
||||
@@ -51,7 +56,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = {
|
||||
.filter_data = &pxa2xx_ac97_pcm_out_req,
|
||||
};
|
||||
|
||||
static unsigned long pxa2xx_ac97_pcm_in_req = 11;
|
||||
static struct pxad_param pxa2xx_ac97_pcm_in_req = {
|
||||
.prio = PXAD_PRIO_LOWEST,
|
||||
.drcmr = 11,
|
||||
};
|
||||
|
||||
static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = {
|
||||
.addr = __PREG(PCDR),
|
||||
.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
|
||||
|
||||
+36
-165
@@ -8,6 +8,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma/pxa-dma.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
@@ -15,8 +16,6 @@
|
||||
#include <sound/pxa2xx-lib.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
#include <mach/dma.h>
|
||||
|
||||
#include "pxa2xx-pcm.h"
|
||||
|
||||
static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
|
||||
@@ -31,7 +30,7 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
|
||||
.period_bytes_min = 32,
|
||||
.period_bytes_max = 8192 - 32,
|
||||
.periods_min = 1,
|
||||
.periods_max = PAGE_SIZE/sizeof(pxa_dma_desc),
|
||||
.periods_max = 256,
|
||||
.buffer_bytes_max = 128 * 1024,
|
||||
.fifo_size = 32,
|
||||
};
|
||||
@@ -39,65 +38,29 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
|
||||
int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pxa2xx_runtime_data *rtd = runtime->private_data;
|
||||
size_t totsize = params_buffer_bytes(params);
|
||||
size_t period = params_period_bytes(params);
|
||||
pxa_dma_desc *dma_desc;
|
||||
dma_addr_t dma_buff_phys, next_desc_phys;
|
||||
u32 dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG;
|
||||
struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_dmaengine_dai_dma_data *dma_params;
|
||||
struct dma_slave_config config;
|
||||
int ret;
|
||||
|
||||
/* temporary transition hack */
|
||||
switch (rtd->params->addr_width) {
|
||||
case DMA_SLAVE_BUSWIDTH_1_BYTE:
|
||||
dcmd |= DCMD_WIDTH1;
|
||||
break;
|
||||
case DMA_SLAVE_BUSWIDTH_2_BYTES:
|
||||
dcmd |= DCMD_WIDTH2;
|
||||
break;
|
||||
case DMA_SLAVE_BUSWIDTH_4_BYTES:
|
||||
dcmd |= DCMD_WIDTH4;
|
||||
break;
|
||||
default:
|
||||
/* can't happen */
|
||||
break;
|
||||
}
|
||||
dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
||||
if (!dma_params)
|
||||
return 0;
|
||||
|
||||
switch (rtd->params->maxburst) {
|
||||
case 8:
|
||||
dcmd |= DCMD_BURST8;
|
||||
break;
|
||||
case 16:
|
||||
dcmd |= DCMD_BURST16;
|
||||
break;
|
||||
case 32:
|
||||
dcmd |= DCMD_BURST32;
|
||||
break;
|
||||
}
|
||||
ret = snd_hwparams_to_dma_slave_config(substream, params, &config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
snd_dmaengine_pcm_set_config_from_dai_data(substream,
|
||||
snd_soc_dai_get_dma_data(rtd->cpu_dai, substream),
|
||||
&config);
|
||||
|
||||
ret = dmaengine_slave_config(chan, &config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
runtime->dma_bytes = totsize;
|
||||
|
||||
dma_desc = rtd->dma_desc_array;
|
||||
next_desc_phys = rtd->dma_desc_array_phys;
|
||||
dma_buff_phys = runtime->dma_addr;
|
||||
do {
|
||||
next_desc_phys += sizeof(pxa_dma_desc);
|
||||
dma_desc->ddadr = next_desc_phys;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
dma_desc->dsadr = dma_buff_phys;
|
||||
dma_desc->dtadr = rtd->params->addr;
|
||||
} else {
|
||||
dma_desc->dsadr = rtd->params->addr;
|
||||
dma_desc->dtadr = dma_buff_phys;
|
||||
}
|
||||
if (period > totsize)
|
||||
period = totsize;
|
||||
dma_desc->dcmd = dcmd | period | DCMD_ENDIRQEN;
|
||||
dma_desc++;
|
||||
dma_buff_phys += period;
|
||||
} while (totsize -= period);
|
||||
dma_desc[-1].ddadr = rtd->dma_desc_array_phys;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -105,13 +68,6 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_params);
|
||||
|
||||
int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
|
||||
|
||||
if (rtd && rtd->params && rtd->params->filter_data) {
|
||||
unsigned long req = *(unsigned long *) rtd->params->filter_data;
|
||||
DRCMR(req) = 0;
|
||||
}
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, NULL);
|
||||
return 0;
|
||||
}
|
||||
@@ -119,100 +75,36 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_free);
|
||||
|
||||
int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
|
||||
DCSR(prtd->dma_ch) = DCSR_RUN;
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
DCSR(prtd->dma_ch) &= ~DCSR_RUN;
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
DCSR(prtd->dma_ch) |= DCSR_RUN;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
|
||||
DCSR(prtd->dma_ch) |= DCSR_RUN;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return snd_dmaengine_pcm_trigger(substream, cmd);
|
||||
}
|
||||
EXPORT_SYMBOL(pxa2xx_pcm_trigger);
|
||||
|
||||
snd_pcm_uframes_t
|
||||
pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pxa2xx_runtime_data *prtd = runtime->private_data;
|
||||
|
||||
dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
|
||||
DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch);
|
||||
snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
|
||||
|
||||
if (x == runtime->buffer_size)
|
||||
x = 0;
|
||||
return x;
|
||||
return snd_dmaengine_pcm_pointer(substream);
|
||||
}
|
||||
EXPORT_SYMBOL(pxa2xx_pcm_pointer);
|
||||
|
||||
int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
|
||||
unsigned long req;
|
||||
|
||||
if (!prtd || !prtd->params)
|
||||
return 0;
|
||||
|
||||
if (prtd->dma_ch == -1)
|
||||
return -EINVAL;
|
||||
|
||||
DCSR(prtd->dma_ch) &= ~DCSR_RUN;
|
||||
DCSR(prtd->dma_ch) = 0;
|
||||
DCMD(prtd->dma_ch) = 0;
|
||||
req = *(unsigned long *) prtd->params->filter_data;
|
||||
DRCMR(req) = prtd->dma_ch | DRCMR_MAPVLD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(__pxa2xx_pcm_prepare);
|
||||
|
||||
void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
|
||||
{
|
||||
struct snd_pcm_substream *substream = dev_id;
|
||||
int dcsr;
|
||||
|
||||
dcsr = DCSR(dma_ch);
|
||||
DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
|
||||
|
||||
if (dcsr & DCSR_ENDINTR) {
|
||||
snd_pcm_period_elapsed(substream);
|
||||
} else {
|
||||
printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n",
|
||||
dma_ch, dcsr);
|
||||
snd_pcm_stop_xrun(substream);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(pxa2xx_pcm_dma_irq);
|
||||
|
||||
int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pxa2xx_runtime_data *rtd;
|
||||
struct snd_dmaengine_dai_dma_data *dma_params;
|
||||
int ret;
|
||||
|
||||
runtime->hw = pxa2xx_pcm_hardware;
|
||||
|
||||
dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
||||
if (!dma_params)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* For mysterious reasons (and despite what the manual says)
|
||||
* playback samples are lost if the DMA count is not a multiple
|
||||
@@ -221,48 +113,27 @@ int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
|
||||
ret = snd_pcm_hw_constraint_step(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
ret = snd_pcm_hw_constraint_step(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
ret = -ENOMEM;
|
||||
rtd = kzalloc(sizeof(*rtd), GFP_KERNEL);
|
||||
if (!rtd)
|
||||
goto out;
|
||||
rtd->dma_desc_array =
|
||||
dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
|
||||
&rtd->dma_desc_array_phys, GFP_KERNEL);
|
||||
if (!rtd->dma_desc_array)
|
||||
goto err1;
|
||||
|
||||
rtd->dma_ch = -1;
|
||||
runtime->private_data = rtd;
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
kfree(rtd);
|
||||
out:
|
||||
return ret;
|
||||
return snd_dmaengine_pcm_open_request_chan(substream,
|
||||
pxad_filter_fn,
|
||||
dma_params->filter_data);
|
||||
}
|
||||
EXPORT_SYMBOL(__pxa2xx_pcm_open);
|
||||
|
||||
int __pxa2xx_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pxa2xx_runtime_data *rtd = runtime->private_data;
|
||||
|
||||
dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
|
||||
rtd->dma_desc_array, rtd->dma_desc_array_phys);
|
||||
kfree(rtd);
|
||||
return 0;
|
||||
return snd_dmaengine_pcm_close_release_chan(substream);
|
||||
}
|
||||
EXPORT_SYMBOL(__pxa2xx_pcm_close);
|
||||
|
||||
|
||||
@@ -46,17 +46,13 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
|
||||
|
||||
rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
|
||||
client->playback_params : client->capture_params;
|
||||
ret = pxa_request_dma("dma", DMA_PRIO_LOW,
|
||||
pxa2xx_pcm_dma_irq, substream);
|
||||
if (ret < 0)
|
||||
goto err2;
|
||||
rtd->dma_ch = ret;
|
||||
|
||||
ret = client->startup(substream);
|
||||
if (!ret)
|
||||
goto out;
|
||||
goto err2;
|
||||
|
||||
return 0;
|
||||
|
||||
pxa_free_dma(rtd->dma_ch);
|
||||
err2:
|
||||
__pxa2xx_pcm_close(substream);
|
||||
out:
|
||||
@@ -66,9 +62,7 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
|
||||
static int pxa2xx_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct pxa2xx_pcm_client *client = substream->private_data;
|
||||
struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
|
||||
|
||||
pxa_free_dma(rtd->dma_ch);
|
||||
client->shutdown(substream);
|
||||
|
||||
return __pxa2xx_pcm_close(substream);
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
struct pxa2xx_runtime_data {
|
||||
int dma_ch;
|
||||
struct snd_dmaengine_dai_dma_data *params;
|
||||
struct pxa_dma_desc *dma_desc_array;
|
||||
dma_addr_t dma_desc_array_phys;
|
||||
};
|
||||
|
||||
struct pxa2xx_pcm_client {
|
||||
|
||||
+12
-1
@@ -4,7 +4,7 @@ config SND_TIMER
|
||||
|
||||
config SND_PCM
|
||||
tristate
|
||||
select SND_TIMER
|
||||
select SND_TIMER if SND_PCM_TIMER
|
||||
|
||||
config SND_PCM_ELD
|
||||
bool
|
||||
@@ -93,6 +93,17 @@ config SND_PCM_OSS_PLUGINS
|
||||
support conversion of channels, formats and rates. It will
|
||||
behave like most of new OSS/Free drivers in 2.4/2.6 kernels.
|
||||
|
||||
config SND_PCM_TIMER
|
||||
bool "PCM timer interface" if EXPERT
|
||||
default y
|
||||
help
|
||||
If you disable this option, pcm timer will be inavailable, so
|
||||
those stubs used pcm timer (e.g. dmix, dsnoop & co) may work
|
||||
incorrectlly.
|
||||
|
||||
For some embedded device, we may disable it to reduce memory
|
||||
footprint, about 20KB on x86_64 platform.
|
||||
|
||||
config SND_SEQUENCER_OSS
|
||||
bool "OSS Sequencer API"
|
||||
depends on SND_SEQUENCER
|
||||
|
||||
+2
-1
@@ -13,8 +13,9 @@ snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o
|
||||
snd-$(CONFIG_SND_VMASTER) += vmaster.o
|
||||
snd-$(CONFIG_SND_JACK) += ctljack.o jack.o
|
||||
|
||||
snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
|
||||
snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_misc.o \
|
||||
pcm_memory.o memalloc.o
|
||||
snd-pcm-$(CONFIG_SND_PCM_TIMER) += pcm_timer.o
|
||||
snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
|
||||
snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o
|
||||
snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
|
||||
|
||||
@@ -1177,7 +1177,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
|
||||
struct snd_mixer_oss *mixer = entry->private_data;
|
||||
char line[128], str[32], idxstr[16];
|
||||
const char *cptr;
|
||||
int ch, idx;
|
||||
unsigned int idx;
|
||||
int ch;
|
||||
struct snd_mixer_oss_assign_table *tbl;
|
||||
struct slot *slot;
|
||||
|
||||
|
||||
@@ -1014,9 +1014,6 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
|
||||
snd_free_pages((void*)runtime->control,
|
||||
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)));
|
||||
kfree(runtime->hw_constraints.rules);
|
||||
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
|
||||
kfree(runtime->hwptr_log);
|
||||
#endif
|
||||
kfree(runtime);
|
||||
substream->runtime = NULL;
|
||||
put_pid(substream->pid);
|
||||
|
||||
+11
-13
@@ -801,7 +801,7 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
|
||||
* negative error code.
|
||||
*/
|
||||
int snd_interval_ratnum(struct snd_interval *i,
|
||||
unsigned int rats_count, struct snd_ratnum *rats,
|
||||
unsigned int rats_count, const struct snd_ratnum *rats,
|
||||
unsigned int *nump, unsigned int *denp)
|
||||
{
|
||||
unsigned int best_num, best_den;
|
||||
@@ -920,7 +920,8 @@ EXPORT_SYMBOL(snd_interval_ratnum);
|
||||
* negative error code.
|
||||
*/
|
||||
static int snd_interval_ratden(struct snd_interval *i,
|
||||
unsigned int rats_count, struct snd_ratden *rats,
|
||||
unsigned int rats_count,
|
||||
const struct snd_ratden *rats,
|
||||
unsigned int *nump, unsigned int *denp)
|
||||
{
|
||||
unsigned int best_num, best_diff, best_den;
|
||||
@@ -1339,7 +1340,7 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_ranges);
|
||||
static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct snd_pcm_hw_constraint_ratnums *r = rule->private;
|
||||
const struct snd_pcm_hw_constraint_ratnums *r = rule->private;
|
||||
unsigned int num = 0, den = 0;
|
||||
int err;
|
||||
err = snd_interval_ratnum(hw_param_interval(params, rule->var),
|
||||
@@ -1363,10 +1364,10 @@ static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
|
||||
int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime,
|
||||
unsigned int cond,
|
||||
snd_pcm_hw_param_t var,
|
||||
struct snd_pcm_hw_constraint_ratnums *r)
|
||||
const struct snd_pcm_hw_constraint_ratnums *r)
|
||||
{
|
||||
return snd_pcm_hw_rule_add(runtime, cond, var,
|
||||
snd_pcm_hw_rule_ratnums, r,
|
||||
snd_pcm_hw_rule_ratnums, (void *)r,
|
||||
var, -1);
|
||||
}
|
||||
|
||||
@@ -1375,7 +1376,7 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums);
|
||||
static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct snd_pcm_hw_constraint_ratdens *r = rule->private;
|
||||
const struct snd_pcm_hw_constraint_ratdens *r = rule->private;
|
||||
unsigned int num = 0, den = 0;
|
||||
int err = snd_interval_ratden(hw_param_interval(params, rule->var),
|
||||
r->nrats, r->rats, &num, &den);
|
||||
@@ -1398,10 +1399,10 @@ static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
|
||||
int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime,
|
||||
unsigned int cond,
|
||||
snd_pcm_hw_param_t var,
|
||||
struct snd_pcm_hw_constraint_ratdens *r)
|
||||
const struct snd_pcm_hw_constraint_ratdens *r)
|
||||
{
|
||||
return snd_pcm_hw_rule_add(runtime, cond, var,
|
||||
snd_pcm_hw_rule_ratdens, r,
|
||||
snd_pcm_hw_rule_ratdens, (void *)r,
|
||||
var, -1);
|
||||
}
|
||||
|
||||
@@ -1875,20 +1876,17 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
|
||||
return;
|
||||
runtime = substream->runtime;
|
||||
|
||||
if (runtime->transfer_ack_begin)
|
||||
runtime->transfer_ack_begin(substream);
|
||||
|
||||
snd_pcm_stream_lock_irqsave(substream, flags);
|
||||
if (!snd_pcm_running(substream) ||
|
||||
snd_pcm_update_hw_ptr0(substream, 1) < 0)
|
||||
goto _end;
|
||||
|
||||
#ifdef CONFIG_SND_PCM_TIMER
|
||||
if (substream->timer_running)
|
||||
snd_timer_interrupt(substream->timer, 1);
|
||||
#endif
|
||||
_end:
|
||||
snd_pcm_stream_unlock_irqrestore(substream, flags);
|
||||
if (runtime->transfer_ack_end)
|
||||
runtime->transfer_ack_end(substream);
|
||||
kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
|
||||
}
|
||||
|
||||
|
||||
+20
-22
@@ -486,6 +486,16 @@ static void snd_pcm_set_state(struct snd_pcm_substream *substream, int state)
|
||||
snd_pcm_stream_unlock_irq(substream);
|
||||
}
|
||||
|
||||
static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
|
||||
int event)
|
||||
{
|
||||
#ifdef CONFIG_SND_PCM_TIMER
|
||||
if (substream->timer)
|
||||
snd_timer_notify(substream->timer, event,
|
||||
&substream->runtime->trigger_tstamp);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
@@ -650,7 +660,8 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
|
||||
}
|
||||
snd_pcm_stream_unlock_irq(substream);
|
||||
|
||||
if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
|
||||
if (params->tstamp_mode < 0 ||
|
||||
params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
|
||||
return -EINVAL;
|
||||
if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12) &&
|
||||
params->tstamp_type > SNDRV_PCM_TSTAMP_TYPE_LAST)
|
||||
@@ -1042,9 +1053,7 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
|
||||
runtime->silence_size > 0)
|
||||
snd_pcm_playback_silence(substream, ULONG_MAX);
|
||||
if (substream->timer)
|
||||
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART,
|
||||
&runtime->trigger_tstamp);
|
||||
snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART);
|
||||
}
|
||||
|
||||
static struct action_ops snd_pcm_action_start = {
|
||||
@@ -1092,9 +1101,7 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
|
||||
if (runtime->status->state != state) {
|
||||
snd_pcm_trigger_tstamp(substream);
|
||||
runtime->status->state = state;
|
||||
if (substream->timer)
|
||||
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP,
|
||||
&runtime->trigger_tstamp);
|
||||
snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTOP);
|
||||
}
|
||||
wake_up(&runtime->sleep);
|
||||
wake_up(&runtime->tsleep);
|
||||
@@ -1208,18 +1215,12 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push)
|
||||
snd_pcm_trigger_tstamp(substream);
|
||||
if (push) {
|
||||
runtime->status->state = SNDRV_PCM_STATE_PAUSED;
|
||||
if (substream->timer)
|
||||
snd_timer_notify(substream->timer,
|
||||
SNDRV_TIMER_EVENT_MPAUSE,
|
||||
&runtime->trigger_tstamp);
|
||||
snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MPAUSE);
|
||||
wake_up(&runtime->sleep);
|
||||
wake_up(&runtime->tsleep);
|
||||
} else {
|
||||
runtime->status->state = SNDRV_PCM_STATE_RUNNING;
|
||||
if (substream->timer)
|
||||
snd_timer_notify(substream->timer,
|
||||
SNDRV_TIMER_EVENT_MCONTINUE,
|
||||
&runtime->trigger_tstamp);
|
||||
snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MCONTINUE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1267,9 +1268,7 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
|
||||
snd_pcm_trigger_tstamp(substream);
|
||||
runtime->status->suspended_state = runtime->status->state;
|
||||
runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
|
||||
if (substream->timer)
|
||||
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND,
|
||||
&runtime->trigger_tstamp);
|
||||
snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSUSPEND);
|
||||
wake_up(&runtime->sleep);
|
||||
wake_up(&runtime->tsleep);
|
||||
}
|
||||
@@ -1373,9 +1372,7 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
snd_pcm_trigger_tstamp(substream);
|
||||
runtime->status->state = runtime->status->suspended_state;
|
||||
if (substream->timer)
|
||||
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME,
|
||||
&runtime->trigger_tstamp);
|
||||
snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MRESUME);
|
||||
}
|
||||
|
||||
static struct action_ops snd_pcm_action_resume = {
|
||||
@@ -2226,7 +2223,8 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream)
|
||||
|
||||
snd_pcm_drop(substream);
|
||||
if (substream->hw_opened) {
|
||||
if (substream->ops->hw_free != NULL)
|
||||
if (substream->ops->hw_free &&
|
||||
substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
substream->ops->hw_free(substream);
|
||||
substream->ops->close(substream);
|
||||
substream->hw_opened = 0;
|
||||
|
||||
@@ -91,8 +91,7 @@ snd_seq_oss_readq_clear(struct seq_oss_readq *q)
|
||||
q->head = q->tail = 0;
|
||||
}
|
||||
/* if someone sleeping, wake'em up */
|
||||
if (waitqueue_active(&q->midi_sleep))
|
||||
wake_up(&q->midi_sleep);
|
||||
wake_up(&q->midi_sleep);
|
||||
q->input_time = (unsigned long)-1;
|
||||
}
|
||||
|
||||
@@ -138,8 +137,7 @@ snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev)
|
||||
q->qlen++;
|
||||
|
||||
/* wake up sleeper */
|
||||
if (waitqueue_active(&q->midi_sleep))
|
||||
wake_up(&q->midi_sleep);
|
||||
wake_up(&q->midi_sleep);
|
||||
|
||||
spin_unlock_irqrestore(&q->lock, flags);
|
||||
|
||||
|
||||
@@ -138,9 +138,7 @@ snd_seq_oss_writeq_wakeup(struct seq_oss_writeq *q, abstime_t time)
|
||||
spin_lock_irqsave(&q->sync_lock, flags);
|
||||
q->sync_time = time;
|
||||
q->sync_event_put = 0;
|
||||
if (waitqueue_active(&q->sync_sleep)) {
|
||||
wake_up(&q->sync_sleep);
|
||||
}
|
||||
wake_up(&q->sync_sleep);
|
||||
spin_unlock_irqrestore(&q->sync_lock, flags);
|
||||
}
|
||||
|
||||
|
||||
@@ -120,4 +120,31 @@ config SND_BEBOB
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-bebob.
|
||||
|
||||
config SND_FIREWIRE_DIGI00X
|
||||
tristate "Digidesign Digi 002/003 family support"
|
||||
select SND_FIREWIRE_LIB
|
||||
select SND_HWDEP
|
||||
help
|
||||
Say Y here to include support for Digidesign Digi 002/003 family.
|
||||
* Digi 002 Console
|
||||
* Digi 002 Rack
|
||||
* Digi 003 Console
|
||||
* Digi 003 Rack
|
||||
* Digi 003 Rack+
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-firewire-digi00x.
|
||||
|
||||
config SND_FIREWIRE_TASCAM
|
||||
tristate "TASCAM FireWire series support"
|
||||
select SND_FIREWIRE_LIB
|
||||
select SND_HWDEP
|
||||
help
|
||||
Say Y here to include support for TASCAM.
|
||||
* FW-1884
|
||||
* FW-1082
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-firewire-tascam.
|
||||
|
||||
endif # SND_FIREWIRE
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
|
||||
fcp.o cmp.o amdtp.o
|
||||
snd-oxfw-objs := oxfw.o
|
||||
fcp.o cmp.o amdtp-stream.o amdtp-am824.o
|
||||
snd-isight-objs := isight.o
|
||||
snd-scs1x-objs := scs1x.o
|
||||
|
||||
@@ -11,3 +10,5 @@ obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
|
||||
obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
|
||||
obj-$(CONFIG_SND_FIREWORKS) += fireworks/
|
||||
obj-$(CONFIG_SND_BEBOB) += bebob/
|
||||
obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += digi00x/
|
||||
obj-$(CONFIG_SND_FIREWIRE_TASCAM) += tascam/
|
||||
|
||||
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
* AM824 format in Audio and Music Data Transmission Protocol (IEC 61883-6)
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
* Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp>
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "amdtp-am824.h"
|
||||
|
||||
#define CIP_FMT_AM 0x10
|
||||
|
||||
/* "Clock-based rate control mode" is just supported. */
|
||||
#define AMDTP_FDF_AM824 0x00
|
||||
|
||||
/*
|
||||
* Nominally 3125 bytes/second, but the MIDI port's clock might be
|
||||
* 1% too slow, and the bus clock 100 ppm too fast.
|
||||
*/
|
||||
#define MIDI_BYTES_PER_SECOND 3093
|
||||
|
||||
/*
|
||||
* Several devices look only at the first eight data blocks.
|
||||
* In any case, this is more than enough for the MIDI data rate.
|
||||
*/
|
||||
#define MAX_MIDI_RX_BLOCKS 8
|
||||
|
||||
struct amdtp_am824 {
|
||||
struct snd_rawmidi_substream *midi[AM824_MAX_CHANNELS_FOR_MIDI * 8];
|
||||
int midi_fifo_limit;
|
||||
int midi_fifo_used[AM824_MAX_CHANNELS_FOR_MIDI * 8];
|
||||
unsigned int pcm_channels;
|
||||
unsigned int midi_ports;
|
||||
|
||||
u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM];
|
||||
u8 midi_position;
|
||||
|
||||
void (*transfer_samples)(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames);
|
||||
|
||||
unsigned int frame_multiplier;
|
||||
};
|
||||
|
||||
/**
|
||||
* amdtp_am824_set_parameters - set stream parameters
|
||||
* @s: the AMDTP 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)
|
||||
* @double_pcm_frames: one data block transfers two PCM frames
|
||||
*
|
||||
* The parameters must be set before the stream is started, and must not be
|
||||
* changed while the stream is running.
|
||||
*/
|
||||
int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
||||
unsigned int pcm_channels,
|
||||
unsigned int midi_ports,
|
||||
bool double_pcm_frames)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
unsigned int midi_channels;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
if (amdtp_stream_running(s))
|
||||
return -EINVAL;
|
||||
|
||||
if (pcm_channels > AM824_MAX_CHANNELS_FOR_PCM)
|
||||
return -EINVAL;
|
||||
|
||||
midi_channels = DIV_ROUND_UP(midi_ports, 8);
|
||||
if (midi_channels > AM824_MAX_CHANNELS_FOR_MIDI)
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(amdtp_stream_running(s)) ||
|
||||
WARN_ON(pcm_channels > AM824_MAX_CHANNELS_FOR_PCM) ||
|
||||
WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI))
|
||||
return -EINVAL;
|
||||
|
||||
err = amdtp_stream_set_parameters(s, rate,
|
||||
pcm_channels + midi_channels);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
s->fdf = AMDTP_FDF_AM824 | s->sfc;
|
||||
|
||||
p->pcm_channels = pcm_channels;
|
||||
p->midi_ports = midi_ports;
|
||||
|
||||
/*
|
||||
* In IEC 61883-6, one data block represents one event. In ALSA, one
|
||||
* event equals to one PCM frame. But Dice has a quirk at higher
|
||||
* sampling rate to transfer two PCM frames in one data block.
|
||||
*/
|
||||
if (double_pcm_frames)
|
||||
p->frame_multiplier = 2;
|
||||
else
|
||||
p->frame_multiplier = 1;
|
||||
|
||||
/* init the position map for PCM and MIDI channels */
|
||||
for (i = 0; i < pcm_channels; i++)
|
||||
p->pcm_positions[i] = i;
|
||||
p->midi_position = p->pcm_channels;
|
||||
|
||||
/*
|
||||
* We do not know the actual MIDI FIFO size of most devices. Just
|
||||
* assume two bytes, i.e., one byte can be received over the bus while
|
||||
* the previous one is transmitted over MIDI.
|
||||
* (The value here is adjusted for midi_ratelimit_per_packet().)
|
||||
*/
|
||||
p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_am824_set_parameters);
|
||||
|
||||
/**
|
||||
* amdtp_am824_set_pcm_position - set an index of data channel for a channel
|
||||
* of PCM frame
|
||||
* @s: the AMDTP stream
|
||||
* @index: the index of data channel in an data block
|
||||
* @position: the channel of PCM frame
|
||||
*/
|
||||
void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index,
|
||||
unsigned int position)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
|
||||
if (index < p->pcm_channels)
|
||||
p->pcm_positions[index] = position;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_position);
|
||||
|
||||
/**
|
||||
* amdtp_am824_set_midi_position - set a index of data channel for MIDI
|
||||
* conformant data channel
|
||||
* @s: the AMDTP stream
|
||||
* @position: the index of data channel in an data block
|
||||
*/
|
||||
void amdtp_am824_set_midi_position(struct amdtp_stream *s,
|
||||
unsigned int position)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
|
||||
p->midi_position = position;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position);
|
||||
|
||||
static void write_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
const u32 *src;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
src = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
buffer[p->pcm_positions[c]] =
|
||||
cpu_to_be32((*src >> 8) | 0x40000000);
|
||||
src++;
|
||||
}
|
||||
buffer += s->data_block_quadlets;
|
||||
if (--remaining_frames == 0)
|
||||
src = (void *)runtime->dma_area;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_pcm_s16(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
const u16 *src;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
src = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
buffer[p->pcm_positions[c]] =
|
||||
cpu_to_be32((*src << 8) | 0x42000000);
|
||||
src++;
|
||||
}
|
||||
buffer += s->data_block_quadlets;
|
||||
if (--remaining_frames == 0)
|
||||
src = (void *)runtime->dma_area;
|
||||
}
|
||||
}
|
||||
|
||||
static void read_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
u32 *dst;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
dst = (void *)runtime->dma_area +
|
||||
frames_to_bytes(runtime, s->pcm_buffer_pointer);
|
||||
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
*dst = be32_to_cpu(buffer[p->pcm_positions[c]]) << 8;
|
||||
dst++;
|
||||
}
|
||||
buffer += s->data_block_quadlets;
|
||||
if (--remaining_frames == 0)
|
||||
dst = (void *)runtime->dma_area;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_pcm_silence(struct amdtp_stream *s,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
unsigned int i, c, channels = p->pcm_channels;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c)
|
||||
buffer[p->pcm_positions[c]] = cpu_to_be32(0x40000000);
|
||||
buffer += s->data_block_quadlets;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* amdtp_am824_set_pcm_format - set the PCM format
|
||||
* @s: the AMDTP stream to configure
|
||||
* @format: the format of the ALSA PCM device
|
||||
*
|
||||
* The sample format must be set after the other parameters (rate/PCM channels/
|
||||
* MIDI) and before the stream is started, and must not be changed while the
|
||||
* stream is running.
|
||||
*/
|
||||
void amdtp_am824_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
|
||||
if (WARN_ON(amdtp_stream_pcm_running(s)))
|
||||
return;
|
||||
|
||||
switch (format) {
|
||||
default:
|
||||
WARN_ON(1);
|
||||
/* fall through */
|
||||
case SNDRV_PCM_FORMAT_S16:
|
||||
if (s->direction == AMDTP_OUT_STREAM) {
|
||||
p->transfer_samples = write_pcm_s16;
|
||||
break;
|
||||
}
|
||||
WARN_ON(1);
|
||||
/* fall through */
|
||||
case SNDRV_PCM_FORMAT_S32:
|
||||
if (s->direction == AMDTP_OUT_STREAM)
|
||||
p->transfer_samples = write_pcm_s32;
|
||||
else
|
||||
p->transfer_samples = read_pcm_s32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_format);
|
||||
|
||||
/**
|
||||
* amdtp_am824_add_pcm_hw_constraints - add hw constraints for PCM substream
|
||||
* @s: the AMDTP stream for AM824 data block, must be initialized.
|
||||
* @runtime: the PCM substream runtime
|
||||
*
|
||||
*/
|
||||
int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s,
|
||||
struct snd_pcm_runtime *runtime)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* AM824 in IEC 61883-6 can deliver 24bit data. */
|
||||
return snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_am824_add_pcm_hw_constraints);
|
||||
|
||||
/**
|
||||
* amdtp_am824_midi_trigger - start/stop playback/capture with a MIDI device
|
||||
* @s: the AMDTP stream
|
||||
* @port: index of MIDI port
|
||||
* @midi: the MIDI device to be started, or %NULL to stop the current device
|
||||
*
|
||||
* Call this function on a running isochronous stream to enable the actual
|
||||
* transmission of MIDI data. This function should be called from the MIDI
|
||||
* device's .trigger callback.
|
||||
*/
|
||||
void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
|
||||
struct snd_rawmidi_substream *midi)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
|
||||
if (port < p->midi_ports)
|
||||
ACCESS_ONCE(p->midi[port]) = midi;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_am824_midi_trigger);
|
||||
|
||||
/*
|
||||
* To avoid sending MIDI bytes at too high a rate, assume that the receiving
|
||||
* device has a FIFO, and track how much it is filled. This values increases
|
||||
* by one whenever we send one byte in a packet, but the FIFO empties at
|
||||
* a constant rate independent of our packet rate. One packet has syt_interval
|
||||
* samples, so the number of bytes that empty out of the FIFO, per packet(!),
|
||||
* is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing
|
||||
* fractional values, the values in midi_fifo_used[] are measured in bytes
|
||||
* multiplied by the sample rate.
|
||||
*/
|
||||
static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
int used;
|
||||
|
||||
used = p->midi_fifo_used[port];
|
||||
if (used == 0) /* common shortcut */
|
||||
return true;
|
||||
|
||||
used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
|
||||
used = max(used, 0);
|
||||
p->midi_fifo_used[port] = used;
|
||||
|
||||
return used < p->midi_fifo_limit;
|
||||
}
|
||||
|
||||
static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
|
||||
p->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
|
||||
}
|
||||
|
||||
static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
|
||||
unsigned int frames)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
unsigned int f, port;
|
||||
u8 *b;
|
||||
|
||||
for (f = 0; f < frames; f++) {
|
||||
b = (u8 *)&buffer[p->midi_position];
|
||||
|
||||
port = (s->data_block_counter + f) % 8;
|
||||
if (f < MAX_MIDI_RX_BLOCKS &&
|
||||
midi_ratelimit_per_packet(s, port) &&
|
||||
p->midi[port] != NULL &&
|
||||
snd_rawmidi_transmit(p->midi[port], &b[1], 1) == 1) {
|
||||
midi_rate_use_one_byte(s, port);
|
||||
b[0] = 0x81;
|
||||
} else {
|
||||
b[0] = 0x80;
|
||||
b[1] = 0;
|
||||
}
|
||||
b[2] = 0;
|
||||
b[3] = 0;
|
||||
|
||||
buffer += s->data_block_quadlets;
|
||||
}
|
||||
}
|
||||
|
||||
static void read_midi_messages(struct amdtp_stream *s,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
unsigned int f, port;
|
||||
int len;
|
||||
u8 *b;
|
||||
|
||||
for (f = 0; f < frames; f++) {
|
||||
port = (s->data_block_counter + f) % 8;
|
||||
b = (u8 *)&buffer[p->midi_position];
|
||||
|
||||
len = b[0] - 0x80;
|
||||
if ((1 <= len) && (len <= 3) && (p->midi[port]))
|
||||
snd_rawmidi_receive(p->midi[port], b + 1, len);
|
||||
|
||||
buffer += s->data_block_quadlets;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
|
||||
unsigned int data_blocks, unsigned int *syt)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
|
||||
unsigned int pcm_frames;
|
||||
|
||||
if (pcm) {
|
||||
p->transfer_samples(s, pcm, buffer, data_blocks);
|
||||
pcm_frames = data_blocks * p->frame_multiplier;
|
||||
} else {
|
||||
write_pcm_silence(s, buffer, data_blocks);
|
||||
pcm_frames = 0;
|
||||
}
|
||||
|
||||
if (p->midi_ports)
|
||||
write_midi_messages(s, buffer, data_blocks);
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
|
||||
unsigned int data_blocks, unsigned int *syt)
|
||||
{
|
||||
struct amdtp_am824 *p = s->protocol;
|
||||
struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
|
||||
unsigned int pcm_frames;
|
||||
|
||||
if (pcm) {
|
||||
p->transfer_samples(s, pcm, buffer, data_blocks);
|
||||
pcm_frames = data_blocks * p->frame_multiplier;
|
||||
} else {
|
||||
pcm_frames = 0;
|
||||
}
|
||||
|
||||
if (p->midi_ports)
|
||||
read_midi_messages(s, buffer, data_blocks);
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdtp_am824_init - initialize an AMDTP stream structure to handle AM824
|
||||
* data block
|
||||
* @s: the AMDTP stream to initialize
|
||||
* @unit: the target of the stream
|
||||
* @dir: the direction of stream
|
||||
* @flags: the packet transmission method to use
|
||||
*/
|
||||
int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir, enum cip_flags flags)
|
||||
{
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM)
|
||||
process_data_blocks = process_tx_data_blocks;
|
||||
else
|
||||
process_data_blocks = process_rx_data_blocks;
|
||||
|
||||
return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
|
||||
process_data_blocks,
|
||||
sizeof(struct amdtp_am824));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amdtp_am824_init);
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED
|
||||
#define SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED
|
||||
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/rawmidi.h>
|
||||
|
||||
#include "amdtp-stream.h"
|
||||
|
||||
#define AM824_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32
|
||||
|
||||
#define AM824_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \
|
||||
SNDRV_PCM_FMTBIT_S32)
|
||||
|
||||
/*
|
||||
* This module supports maximum 64 PCM channels for one PCM stream
|
||||
* This is for our convenience.
|
||||
*/
|
||||
#define AM824_MAX_CHANNELS_FOR_PCM 64
|
||||
|
||||
/*
|
||||
* AMDTP packet can include channels for MIDI conformant data.
|
||||
* Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
|
||||
* Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
|
||||
*
|
||||
* This module supports maximum 1 MIDI conformant data channels.
|
||||
* Then this AMDTP packets can transfer maximum 8 MIDI data streams.
|
||||
*/
|
||||
#define AM824_MAX_CHANNELS_FOR_MIDI 1
|
||||
|
||||
int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
||||
unsigned int pcm_channels,
|
||||
unsigned int midi_ports,
|
||||
bool double_pcm_frames);
|
||||
|
||||
void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index,
|
||||
unsigned int position);
|
||||
|
||||
void amdtp_am824_set_midi_position(struct amdtp_stream *s,
|
||||
unsigned int position);
|
||||
|
||||
int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s,
|
||||
struct snd_pcm_runtime *runtime);
|
||||
|
||||
void amdtp_am824_set_pcm_format(struct amdtp_stream *s,
|
||||
snd_pcm_format_t format);
|
||||
|
||||
void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
|
||||
struct snd_rawmidi_substream *midi);
|
||||
|
||||
int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir, enum cip_flags flags);
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sched.h>
|
||||
#include <sound/asound.h>
|
||||
#include "packets-buffer.h"
|
||||
|
||||
@@ -80,100 +81,78 @@ enum cip_sfc {
|
||||
CIP_SFC_COUNT
|
||||
};
|
||||
|
||||
#define AMDTP_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32
|
||||
|
||||
#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \
|
||||
SNDRV_PCM_FMTBIT_S32)
|
||||
|
||||
|
||||
/*
|
||||
* This module supports maximum 64 PCM channels for one PCM stream
|
||||
* This is for our convenience.
|
||||
*/
|
||||
#define AMDTP_MAX_CHANNELS_FOR_PCM 64
|
||||
|
||||
/*
|
||||
* AMDTP packet can include channels for MIDI conformant data.
|
||||
* Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
|
||||
* Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
|
||||
*
|
||||
* This module supports maximum 1 MIDI conformant data channels.
|
||||
* Then this AMDTP packets can transfer maximum 8 MIDI data streams.
|
||||
*/
|
||||
#define AMDTP_MAX_CHANNELS_FOR_MIDI 1
|
||||
|
||||
struct fw_unit;
|
||||
struct fw_iso_context;
|
||||
struct snd_pcm_substream;
|
||||
struct snd_pcm_runtime;
|
||||
struct snd_rawmidi_substream;
|
||||
|
||||
enum amdtp_stream_direction {
|
||||
AMDTP_OUT_STREAM = 0,
|
||||
AMDTP_IN_STREAM
|
||||
};
|
||||
|
||||
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);
|
||||
struct amdtp_stream {
|
||||
struct fw_unit *unit;
|
||||
enum cip_flags flags;
|
||||
enum amdtp_stream_direction direction;
|
||||
struct fw_iso_context *context;
|
||||
struct mutex mutex;
|
||||
|
||||
enum cip_sfc sfc;
|
||||
unsigned int data_block_quadlets;
|
||||
unsigned int pcm_channels;
|
||||
unsigned int midi_ports;
|
||||
void (*transfer_samples)(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames);
|
||||
u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM];
|
||||
u8 midi_position;
|
||||
|
||||
unsigned int syt_interval;
|
||||
unsigned int transfer_delay;
|
||||
unsigned int source_node_id_field;
|
||||
/* For packet processing. */
|
||||
struct fw_iso_context *context;
|
||||
struct iso_packets_buffer buffer;
|
||||
|
||||
struct snd_pcm_substream *pcm;
|
||||
struct tasklet_struct period_tasklet;
|
||||
|
||||
int packet_index;
|
||||
|
||||
/* For CIP headers. */
|
||||
unsigned int source_node_id_field;
|
||||
unsigned int data_block_quadlets;
|
||||
unsigned int data_block_counter;
|
||||
|
||||
unsigned int data_block_state;
|
||||
|
||||
unsigned int last_syt_offset;
|
||||
unsigned int syt_offset_state;
|
||||
|
||||
unsigned int pcm_buffer_pointer;
|
||||
unsigned int pcm_period_pointer;
|
||||
bool pointer_flush;
|
||||
bool double_pcm_frames;
|
||||
|
||||
struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
|
||||
int midi_fifo_limit;
|
||||
int midi_fifo_used[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
|
||||
|
||||
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. */
|
||||
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;
|
||||
unsigned int pcm_buffer_pointer;
|
||||
unsigned int pcm_period_pointer;
|
||||
bool pointer_flush;
|
||||
|
||||
/* To wait for first packet. */
|
||||
bool callbacked;
|
||||
wait_queue_head_t callback_wait;
|
||||
struct amdtp_stream *sync_slave;
|
||||
|
||||
/* For backends to process data blocks. */
|
||||
void *protocol;
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks;
|
||||
};
|
||||
|
||||
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, enum cip_flags flags,
|
||||
unsigned int fmt,
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks,
|
||||
unsigned int protocol_size);
|
||||
void amdtp_stream_destroy(struct amdtp_stream *s);
|
||||
|
||||
void amdtp_stream_set_parameters(struct amdtp_stream *s,
|
||||
unsigned int rate,
|
||||
unsigned int pcm_channels,
|
||||
unsigned int midi_ports);
|
||||
int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
||||
unsigned int data_block_quadlets);
|
||||
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
|
||||
|
||||
int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
|
||||
@@ -182,8 +161,7 @@ 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_set_pcm_format(struct amdtp_stream *s,
|
||||
snd_pcm_format_t format);
|
||||
|
||||
void amdtp_stream_pcm_prepare(struct amdtp_stream *s);
|
||||
unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s);
|
||||
void amdtp_stream_pcm_abort(struct amdtp_stream *s);
|
||||
@@ -240,24 +218,6 @@ static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
|
||||
ACCESS_ONCE(s->pcm) = pcm;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdtp_stream_midi_trigger - start/stop playback/capture with a MIDI device
|
||||
* @s: the AMDTP stream
|
||||
* @port: index of MIDI port
|
||||
* @midi: the MIDI device to be started, or %NULL to stop the current device
|
||||
*
|
||||
* Call this function on a running isochronous stream to enable the actual
|
||||
* transmission of MIDI data. This function should be called from the MIDI
|
||||
* device's .trigger callback.
|
||||
*/
|
||||
static inline void amdtp_stream_midi_trigger(struct amdtp_stream *s,
|
||||
unsigned int port,
|
||||
struct snd_rawmidi_substream *midi)
|
||||
{
|
||||
if (port < s->midi_ports)
|
||||
ACCESS_ONCE(s->midi[port]) = midi;
|
||||
}
|
||||
|
||||
static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
|
||||
{
|
||||
return sfc & 1;
|
||||
@@ -1,4 +1,4 @@
|
||||
snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
|
||||
bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob_yamaha.o \
|
||||
bebob_focusrite.o bebob_maudio.o bebob.o
|
||||
obj-m += snd-bebob.o
|
||||
obj-$(CONFIG_SND_BEBOB) += snd-bebob.o
|
||||
|
||||
@@ -41,7 +41,8 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
|
||||
#define VEN_EDIROL 0x000040ab
|
||||
#define VEN_PRESONUS 0x00000a92
|
||||
#define VEN_BRIDGECO 0x000007f5
|
||||
#define VEN_MACKIE 0x0000000f
|
||||
#define VEN_MACKIE1 0x0000000f
|
||||
#define VEN_MACKIE2 0x00000ff2
|
||||
#define VEN_STANTON 0x00001260
|
||||
#define VEN_TASCAM 0x0000022e
|
||||
#define VEN_BEHRINGER 0x00001564
|
||||
@@ -334,7 +335,7 @@ static void bebob_remove(struct fw_unit *unit)
|
||||
snd_card_free_when_closed(bebob->card);
|
||||
}
|
||||
|
||||
static struct snd_bebob_rate_spec normal_rate_spec = {
|
||||
static const struct snd_bebob_rate_spec normal_rate_spec = {
|
||||
.get = &snd_bebob_stream_get_rate,
|
||||
.set = &snd_bebob_stream_set_rate
|
||||
};
|
||||
@@ -360,9 +361,9 @@ static const struct ieee1394_device_id bebob_id_table[] = {
|
||||
/* BridgeCo, Audio5 */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049, &spec_normal),
|
||||
/* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065, &spec_normal),
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MACKIE2, 0x00010065, &spec_normal),
|
||||
/* Mackie, d.2 (Firewire Option) */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067, &spec_normal),
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MACKIE1, 0x00010067, &spec_normal),
|
||||
/* Stanton, ScratchAmp */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001, &spec_normal),
|
||||
/* Tascam, IF-FW DM */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user