Merge tag 'sound-4.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"Here is the first batch of updates for sound system on 4.4-rc1.
Again at this time, the update looks fairly calm; no big changes in
either ALSA core or ASoC infrastructures, rather all small cleanups,
in addition to the new stuff as usual.
The biggest changes are about Firewire sound devices. It gained lots
of new device support, and MIDI functionality. Also there are updates
for a few still working-in-progress stuff (topology API and ASoC
skylake), too. But overall, this update should give no big surprise.
Some highlights are below:
Core:
- A few more Kconfig items for tinification; it's marked as EXPERT,
so normal user should't be bothered :)
- Refactoring with a new PCM hw_constraint helper
- Removal of unused transfer_ack_{begin,end} PCM callbacks
Firewire:
- Restructuring of code subtree, lots of refactoring
- Support AMDTP variants
- New driver for Digidesign 002/003 family
- Adds support for TASCAM FireOne to ALSA OXFW driver
- Add MIDI support to TASCAM and Digi00x devices
HD-Audio:
- Automated modalias generation for codec drivers, finally
- Improvement on heuristics for setting mixer name
- A few fixes for longstanding bugs on Creative CA0132 cards
- Addition of audio rate callback with i915 communication
- Fix suspend issue on recent Dell XPS
- Intel Lewisburg controller support
ASoC:
- Updates to the topology userspace interface
- Big updates to the Renesas support (rcar)
- More updates for supporting Intel Sky Lake systems
- New drivers for Asahi Kasei Microdevices AK4613, Allwinnner A10,
Cirrus Logic WM8998, Dialog DA7219, Nuvoton NAU8825, Rockchip
S/PDIF, and Atmel class D amplifier
USB-Audio:
- A fix for newer Roland MIDI devices
- Quirks and workarounds for Zoom R16/24 device
Misc:
- A few fixes for some old Cirrus CS46xx PCI sound boards
- Yet another fixes for some old ESS Maestro3 PCI sound boards"
* tag 'sound-4.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (330 commits)
ALSA: hda - Add Intel Lewisburg device IDs Audio
ALSA: hda - Apply pin fixup for HP ProBook 6550b
ALSA: hda - Fix lost 4k BDL boundary workaround
ALSA: maestro3: Fix Allegro mute until master volume/mute is touched
ALSA: maestro3: Enable docking support for Dell Latitude C810
ALSA: firewire-digi00x: add another rawmidi character device for MIDI control ports
ALSA: firewire-digi00x: add MIDI operations for MIDI control port
ALSA: firewire-digi00x: rename identifiers of MIDI operation for physical ports
ALSA: cs46xx: Fix suspend for all channels
ALSA: cs46xx: Fix Duplicate front for CS4294 and CS4298 codecs
ALSA: DocBook: Add soc-ops.c and soc-compress.c
ALSA: hda - Add / fix kernel doc comments
ALSA: Constify ratden/ratnum constraints
ALSA: hda - Disable 64bit address for Creative HDA controllers
ALSA: hda/realtek - Dell XPS one ALC3260 speaker no sound after resume back
ALSA: hda/ca0132 - Convert leftover pr_info() and pr_err()
ASoC: fsl: Use #ifdef instead of #if for CONFIG_PM_SLEEP
ASoC: rt5645: Sort the order for register bit defines
ASoC: dwc: add check for master/slave format
ASoC: rt5645: Add the HWEQ for the speaker output
...
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
|
||||
@@ -11,28 +11,14 @@
|
||||
#include <linux/firewire.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/rawmidi.h>
|
||||
#include "amdtp.h"
|
||||
#include "amdtp-stream.h"
|
||||
|
||||
#define TICKS_PER_CYCLE 3072
|
||||
#define CYCLES_PER_SECOND 8000
|
||||
#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */
|
||||
|
||||
/* isochronous header parameters */
|
||||
@@ -55,12 +41,8 @@
|
||||
#define CIP_SYT_MASK 0x0000ffff
|
||||
#define CIP_SYT_NO_INFO 0xffff
|
||||
|
||||
/*
|
||||
* Audio and Music transfer protocol specific parameters
|
||||
* only "Clock-based rate control mode" is supported
|
||||
*/
|
||||
#define CIP_FMT_AM (0x10 << CIP_FMT_SHIFT)
|
||||
#define AMDTP_FDF_AM824 (0 << (CIP_FDF_SHIFT + 3))
|
||||
/* Audio and Music transfer protocol specific parameters */
|
||||
#define CIP_FMT_AM 0x10
|
||||
#define AMDTP_FDF_NO_DATA 0xff
|
||||
|
||||
/* TODO: make these configurable */
|
||||
@@ -78,10 +60,23 @@ static void pcm_period_tasklet(unsigned long data);
|
||||
* @unit: the target of the stream
|
||||
* @dir: the direction of stream
|
||||
* @flags: the packet transmission method to use
|
||||
* @fmt: the value of fmt field in CIP header
|
||||
* @process_data_blocks: callback handler to process data blocks
|
||||
* @protocol_size: the size to allocate newly for protocol
|
||||
*/
|
||||
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)
|
||||
{
|
||||
if (process_data_blocks == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
s->protocol = kzalloc(protocol_size, GFP_KERNEL);
|
||||
if (!s->protocol)
|
||||
return -ENOMEM;
|
||||
|
||||
s->unit = unit;
|
||||
s->direction = dir;
|
||||
s->flags = flags;
|
||||
@@ -94,6 +89,9 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
s->callbacked = false;
|
||||
s->sync_slave = NULL;
|
||||
|
||||
s->fmt = fmt;
|
||||
s->process_data_blocks = process_data_blocks;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(amdtp_stream_init);
|
||||
@@ -105,6 +103,7 @@ EXPORT_SYMBOL(amdtp_stream_init);
|
||||
void amdtp_stream_destroy(struct amdtp_stream *s)
|
||||
{
|
||||
WARN_ON(amdtp_stream_running(s));
|
||||
kfree(s->protocol);
|
||||
mutex_destroy(&s->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(amdtp_stream_destroy);
|
||||
@@ -141,11 +140,6 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
|
||||
{
|
||||
int err;
|
||||
|
||||
/* AM824 in IEC 61883-6 can deliver 24bit data */
|
||||
err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
/*
|
||||
* Currently firewire-lib processes 16 packets in one software
|
||||
* interrupt callback. This equals to 2msec but actually the
|
||||
@@ -190,39 +184,25 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints);
|
||||
* amdtp_stream_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)
|
||||
* @data_block_quadlets: the size of a data block in quadlet unit
|
||||
*
|
||||
* The parameters must be set before the stream is started, and must not be
|
||||
* changed while the stream is running.
|
||||
*/
|
||||
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 i, sfc, midi_channels;
|
||||
unsigned int sfc;
|
||||
|
||||
midi_channels = DIV_ROUND_UP(midi_ports, 8);
|
||||
|
||||
if (WARN_ON(amdtp_stream_running(s)) |
|
||||
WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) |
|
||||
WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI))
|
||||
return;
|
||||
|
||||
for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc)
|
||||
for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) {
|
||||
if (amdtp_rate_table[sfc] == rate)
|
||||
goto sfc_found;
|
||||
WARN_ON(1);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
if (sfc == ARRAY_SIZE(amdtp_rate_table))
|
||||
return -EINVAL;
|
||||
|
||||
sfc_found:
|
||||
s->pcm_channels = pcm_channels;
|
||||
s->sfc = sfc;
|
||||
s->data_block_quadlets = s->pcm_channels + midi_channels;
|
||||
s->midi_ports = midi_ports;
|
||||
|
||||
s->data_block_quadlets = data_block_quadlets;
|
||||
s->syt_interval = amdtp_syt_intervals[sfc];
|
||||
|
||||
/* default buffering in the device */
|
||||
@@ -231,18 +211,7 @@ sfc_found:
|
||||
/* additional buffering needed to adjust for no-data packets */
|
||||
s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
|
||||
|
||||
/* init the position map for PCM and MIDI channels */
|
||||
for (i = 0; i < pcm_channels; i++)
|
||||
s->pcm_positions[i] = i;
|
||||
s->midi_position = s->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().)
|
||||
*/
|
||||
s->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(amdtp_stream_set_parameters);
|
||||
|
||||
@@ -264,52 +233,6 @@ unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
|
||||
}
|
||||
EXPORT_SYMBOL(amdtp_stream_get_max_payload);
|
||||
|
||||
static void write_pcm_s16(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames);
|
||||
static void write_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames);
|
||||
static void read_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames);
|
||||
|
||||
/**
|
||||
* amdtp_stream_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_stream_set_pcm_format(struct amdtp_stream *s,
|
||||
snd_pcm_format_t format)
|
||||
{
|
||||
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) {
|
||||
s->transfer_samples = write_pcm_s16;
|
||||
break;
|
||||
}
|
||||
WARN_ON(1);
|
||||
/* fall through */
|
||||
case SNDRV_PCM_FORMAT_S32:
|
||||
if (s->direction == AMDTP_OUT_STREAM)
|
||||
s->transfer_samples = write_pcm_s32;
|
||||
else
|
||||
s->transfer_samples = read_pcm_s32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(amdtp_stream_set_pcm_format);
|
||||
|
||||
/**
|
||||
* amdtp_stream_pcm_prepare - prepare PCM device for running
|
||||
* @s: the AMDTP stream
|
||||
@@ -412,182 +335,12 @@ static unsigned int calculate_syt(struct amdtp_stream *s,
|
||||
}
|
||||
}
|
||||
|
||||
static void write_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
const u32 *src;
|
||||
|
||||
channels = s->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[s->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 snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
const u16 *src;
|
||||
|
||||
channels = s->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[s->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 snd_pcm_runtime *runtime = pcm->runtime;
|
||||
unsigned int channels, remaining_frames, i, c;
|
||||
u32 *dst;
|
||||
|
||||
channels = s->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[s->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)
|
||||
{
|
||||
unsigned int i, c;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < s->pcm_channels; ++c)
|
||||
buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000);
|
||||
buffer += s->data_block_quadlets;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
int used;
|
||||
|
||||
used = s->midi_fifo_used[port];
|
||||
if (used == 0) /* common shortcut */
|
||||
return true;
|
||||
|
||||
used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
|
||||
used = max(used, 0);
|
||||
s->midi_fifo_used[port] = used;
|
||||
|
||||
return used < s->midi_fifo_limit;
|
||||
}
|
||||
|
||||
static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
|
||||
{
|
||||
s->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
|
||||
}
|
||||
|
||||
static void write_midi_messages(struct amdtp_stream *s,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
{
|
||||
unsigned int f, port;
|
||||
u8 *b;
|
||||
|
||||
for (f = 0; f < frames; f++) {
|
||||
b = (u8 *)&buffer[s->midi_position];
|
||||
|
||||
port = (s->data_block_counter + f) % 8;
|
||||
if (f < MAX_MIDI_RX_BLOCKS &&
|
||||
midi_ratelimit_per_packet(s, port) &&
|
||||
s->midi[port] != NULL &&
|
||||
snd_rawmidi_transmit(s->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)
|
||||
{
|
||||
unsigned int f, port;
|
||||
int len;
|
||||
u8 *b;
|
||||
|
||||
for (f = 0; f < frames; f++) {
|
||||
port = (s->data_block_counter + f) % 8;
|
||||
b = (u8 *)&buffer[s->midi_position];
|
||||
|
||||
len = b[0] - 0x80;
|
||||
if ((1 <= len) && (len <= 3) && (s->midi[port]))
|
||||
snd_rawmidi_receive(s->midi[port], b + 1, len);
|
||||
|
||||
buffer += s->data_block_quadlets;
|
||||
}
|
||||
}
|
||||
|
||||
static void update_pcm_pointers(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
unsigned int frames)
|
||||
{
|
||||
unsigned int ptr;
|
||||
|
||||
/*
|
||||
* In IEC 61883-6, one data block represents one event. In ALSA, one
|
||||
* event equals to one PCM frame. But Dice has a quirk to transfer
|
||||
* two PCM frames in one data block.
|
||||
*/
|
||||
if (s->double_pcm_frames)
|
||||
frames *= 2;
|
||||
|
||||
ptr = s->pcm_buffer_pointer + frames;
|
||||
if (ptr >= pcm->runtime->buffer_size)
|
||||
ptr -= pcm->runtime->buffer_size;
|
||||
@@ -656,23 +409,19 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
|
||||
{
|
||||
__be32 *buffer;
|
||||
unsigned int payload_length;
|
||||
unsigned int pcm_frames;
|
||||
struct snd_pcm_substream *pcm;
|
||||
|
||||
buffer = s->buffer.packets[s->packet_index].buffer;
|
||||
pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
|
||||
|
||||
buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
|
||||
(s->data_block_quadlets << CIP_DBS_SHIFT) |
|
||||
s->data_block_counter);
|
||||
buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 |
|
||||
(s->sfc << CIP_FDF_SHIFT) | syt);
|
||||
buffer += 2;
|
||||
|
||||
pcm = ACCESS_ONCE(s->pcm);
|
||||
if (pcm)
|
||||
s->transfer_samples(s, pcm, buffer, data_blocks);
|
||||
else
|
||||
write_pcm_silence(s, buffer, data_blocks);
|
||||
if (s->midi_ports)
|
||||
write_midi_messages(s, buffer, data_blocks);
|
||||
buffer[1] = cpu_to_be32(CIP_EOH |
|
||||
((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
|
||||
((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
|
||||
(syt & CIP_SYT_MASK));
|
||||
|
||||
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
|
||||
|
||||
@@ -680,8 +429,9 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
|
||||
if (queue_out_packet(s, payload_length, false) < 0)
|
||||
return -EIO;
|
||||
|
||||
if (pcm)
|
||||
update_pcm_pointers(s, pcm, data_blocks);
|
||||
pcm = ACCESS_ONCE(s->pcm);
|
||||
if (pcm && pcm_frames > 0)
|
||||
update_pcm_pointers(s, pcm, pcm_frames);
|
||||
|
||||
/* No need to return the number of handled data blocks. */
|
||||
return 0;
|
||||
@@ -689,11 +439,13 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
|
||||
|
||||
static int handle_in_packet(struct amdtp_stream *s,
|
||||
unsigned int payload_quadlets, __be32 *buffer,
|
||||
unsigned int *data_blocks)
|
||||
unsigned int *data_blocks, unsigned int syt)
|
||||
{
|
||||
u32 cip_header[2];
|
||||
unsigned int fmt, fdf;
|
||||
unsigned int data_block_quadlets, data_block_counter, dbc_interval;
|
||||
struct snd_pcm_substream *pcm = NULL;
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames;
|
||||
bool lost;
|
||||
|
||||
cip_header[0] = be32_to_cpu(buffer[0]);
|
||||
@@ -704,19 +456,30 @@ static int handle_in_packet(struct amdtp_stream *s,
|
||||
* For convenience, also check FMT field is AM824 or not.
|
||||
*/
|
||||
if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
|
||||
((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) ||
|
||||
((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) {
|
||||
((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) {
|
||||
dev_info_ratelimited(&s->unit->device,
|
||||
"Invalid CIP header for AMDTP: %08X:%08X\n",
|
||||
cip_header[0], cip_header[1]);
|
||||
*data_blocks = 0;
|
||||
pcm_frames = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Check valid protocol or not. */
|
||||
fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT;
|
||||
if (fmt != s->fmt) {
|
||||
dev_info_ratelimited(&s->unit->device,
|
||||
"Detect unexpected protocol: %08x %08x\n",
|
||||
cip_header[0], cip_header[1]);
|
||||
*data_blocks = 0;
|
||||
pcm_frames = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Calculate data blocks */
|
||||
fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
|
||||
if (payload_quadlets < 3 ||
|
||||
((cip_header[1] & CIP_FDF_MASK) ==
|
||||
(AMDTP_FDF_NO_DATA << CIP_FDF_SHIFT))) {
|
||||
(fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
|
||||
*data_blocks = 0;
|
||||
} else {
|
||||
data_block_quadlets =
|
||||
@@ -763,16 +526,7 @@ static int handle_in_packet(struct amdtp_stream *s,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (*data_blocks > 0) {
|
||||
buffer += 2;
|
||||
|
||||
pcm = ACCESS_ONCE(s->pcm);
|
||||
if (pcm)
|
||||
s->transfer_samples(s, pcm, buffer, *data_blocks);
|
||||
|
||||
if (s->midi_ports)
|
||||
read_midi_messages(s, buffer, *data_blocks);
|
||||
}
|
||||
pcm_frames = s->process_data_blocks(s, buffer + 2, *data_blocks, &syt);
|
||||
|
||||
if (s->flags & CIP_DBC_IS_END_EVENT)
|
||||
s->data_block_counter = data_block_counter;
|
||||
@@ -783,8 +537,9 @@ end:
|
||||
if (queue_in_packet(s) < 0)
|
||||
return -EIO;
|
||||
|
||||
if (pcm)
|
||||
update_pcm_pointers(s, pcm, *data_blocks);
|
||||
pcm = ACCESS_ONCE(s->pcm);
|
||||
if (pcm && pcm_frames > 0)
|
||||
update_pcm_pointers(s, pcm, pcm_frames);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -854,15 +609,15 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
|
||||
break;
|
||||
}
|
||||
|
||||
syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
|
||||
if (handle_in_packet(s, payload_quadlets, buffer,
|
||||
&data_blocks) < 0) {
|
||||
&data_blocks, syt) < 0) {
|
||||
s->packet_index = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Process sync slave stream */
|
||||
if (s->sync_slave && s->sync_slave->callbacked) {
|
||||
syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
|
||||
if (handle_out_packet(s->sync_slave,
|
||||
data_blocks, syt) < 0) {
|
||||
s->packet_index = -1;
|
||||
@@ -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 */
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#include "../fcp.h"
|
||||
#include "../packets-buffer.h"
|
||||
#include "../iso-resources.h"
|
||||
#include "../amdtp.h"
|
||||
#include "../amdtp-am824.h"
|
||||
#include "../cmp.h"
|
||||
|
||||
/* basic register addresses on DM1000/DM1100/DM1500 */
|
||||
@@ -70,9 +70,9 @@ struct snd_bebob_meter_spec {
|
||||
int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size);
|
||||
};
|
||||
struct snd_bebob_spec {
|
||||
struct snd_bebob_clock_spec *clock;
|
||||
struct snd_bebob_rate_spec *rate;
|
||||
struct snd_bebob_meter_spec *meter;
|
||||
const struct snd_bebob_clock_spec *clock;
|
||||
const struct snd_bebob_rate_spec *rate;
|
||||
const struct snd_bebob_meter_spec *meter;
|
||||
};
|
||||
|
||||
struct snd_bebob {
|
||||
@@ -235,19 +235,19 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob);
|
||||
int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
|
||||
|
||||
/* model specific operations */
|
||||
extern struct snd_bebob_spec phase88_rack_spec;
|
||||
extern struct snd_bebob_spec phase24_series_spec;
|
||||
extern struct snd_bebob_spec yamaha_go_spec;
|
||||
extern struct snd_bebob_spec saffirepro_26_spec;
|
||||
extern struct snd_bebob_spec saffirepro_10_spec;
|
||||
extern struct snd_bebob_spec saffire_le_spec;
|
||||
extern struct snd_bebob_spec saffire_spec;
|
||||
extern struct snd_bebob_spec maudio_fw410_spec;
|
||||
extern struct snd_bebob_spec maudio_audiophile_spec;
|
||||
extern struct snd_bebob_spec maudio_solo_spec;
|
||||
extern struct snd_bebob_spec maudio_ozonic_spec;
|
||||
extern struct snd_bebob_spec maudio_nrv10_spec;
|
||||
extern struct snd_bebob_spec maudio_special_spec;
|
||||
extern const struct snd_bebob_spec phase88_rack_spec;
|
||||
extern const struct snd_bebob_spec phase24_series_spec;
|
||||
extern const struct snd_bebob_spec yamaha_go_spec;
|
||||
extern const struct snd_bebob_spec saffirepro_26_spec;
|
||||
extern const struct snd_bebob_spec saffirepro_10_spec;
|
||||
extern const struct snd_bebob_spec saffire_le_spec;
|
||||
extern const struct snd_bebob_spec saffire_spec;
|
||||
extern const struct snd_bebob_spec maudio_fw410_spec;
|
||||
extern const struct snd_bebob_spec maudio_audiophile_spec;
|
||||
extern const struct snd_bebob_spec maudio_solo_spec;
|
||||
extern const struct snd_bebob_spec maudio_ozonic_spec;
|
||||
extern const struct snd_bebob_spec maudio_nrv10_spec;
|
||||
extern const struct snd_bebob_spec maudio_special_spec;
|
||||
int snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814);
|
||||
int snd_bebob_maudio_load_firmware(struct fw_unit *unit);
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ end:
|
||||
return err;
|
||||
}
|
||||
|
||||
struct snd_bebob_spec saffire_le_spec;
|
||||
const struct snd_bebob_spec saffire_le_spec;
|
||||
static enum snd_bebob_clock_type saffire_both_clk_src_types[] = {
|
||||
SND_BEBOB_CLOCK_TYPE_INTERNAL,
|
||||
SND_BEBOB_CLOCK_TYPE_EXTERNAL,
|
||||
@@ -229,7 +229,7 @@ static const char *const saffire_meter_labels[] = {
|
||||
static int
|
||||
saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
|
||||
{
|
||||
struct snd_bebob_meter_spec *spec = bebob->spec->meter;
|
||||
const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
|
||||
unsigned int channels;
|
||||
u64 offset;
|
||||
int err;
|
||||
@@ -260,60 +260,60 @@ saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
|
||||
static const struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
|
||||
.get = &saffirepro_both_clk_freq_get,
|
||||
.set = &saffirepro_both_clk_freq_set,
|
||||
};
|
||||
/* Saffire Pro 26 I/O */
|
||||
static struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
|
||||
static const struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
|
||||
.num = ARRAY_SIZE(saffirepro_26_clk_src_types),
|
||||
.types = saffirepro_26_clk_src_types,
|
||||
.get = &saffirepro_both_clk_src_get,
|
||||
};
|
||||
struct snd_bebob_spec saffirepro_26_spec = {
|
||||
const struct snd_bebob_spec saffirepro_26_spec = {
|
||||
.clock = &saffirepro_26_clk_spec,
|
||||
.rate = &saffirepro_both_rate_spec,
|
||||
.meter = NULL
|
||||
};
|
||||
/* Saffire Pro 10 I/O */
|
||||
static struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
|
||||
static const struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
|
||||
.num = ARRAY_SIZE(saffirepro_10_clk_src_types),
|
||||
.types = saffirepro_10_clk_src_types,
|
||||
.get = &saffirepro_both_clk_src_get,
|
||||
};
|
||||
struct snd_bebob_spec saffirepro_10_spec = {
|
||||
const struct snd_bebob_spec saffirepro_10_spec = {
|
||||
.clock = &saffirepro_10_clk_spec,
|
||||
.rate = &saffirepro_both_rate_spec,
|
||||
.meter = NULL
|
||||
};
|
||||
|
||||
static struct snd_bebob_rate_spec saffire_both_rate_spec = {
|
||||
static const struct snd_bebob_rate_spec saffire_both_rate_spec = {
|
||||
.get = &snd_bebob_stream_get_rate,
|
||||
.set = &snd_bebob_stream_set_rate,
|
||||
};
|
||||
static struct snd_bebob_clock_spec saffire_both_clk_spec = {
|
||||
static const struct snd_bebob_clock_spec saffire_both_clk_spec = {
|
||||
.num = ARRAY_SIZE(saffire_both_clk_src_types),
|
||||
.types = saffire_both_clk_src_types,
|
||||
.get = &saffire_both_clk_src_get,
|
||||
};
|
||||
/* Saffire LE */
|
||||
static struct snd_bebob_meter_spec saffire_le_meter_spec = {
|
||||
static const struct snd_bebob_meter_spec saffire_le_meter_spec = {
|
||||
.num = ARRAY_SIZE(saffire_le_meter_labels),
|
||||
.labels = saffire_le_meter_labels,
|
||||
.get = &saffire_meter_get,
|
||||
};
|
||||
struct snd_bebob_spec saffire_le_spec = {
|
||||
const struct snd_bebob_spec saffire_le_spec = {
|
||||
.clock = &saffire_both_clk_spec,
|
||||
.rate = &saffire_both_rate_spec,
|
||||
.meter = &saffire_le_meter_spec
|
||||
};
|
||||
/* Saffire */
|
||||
static struct snd_bebob_meter_spec saffire_meter_spec = {
|
||||
static const struct snd_bebob_meter_spec saffire_meter_spec = {
|
||||
.num = ARRAY_SIZE(saffire_meter_labels),
|
||||
.labels = saffire_meter_labels,
|
||||
.get = &saffire_meter_get,
|
||||
};
|
||||
struct snd_bebob_spec saffire_spec = {
|
||||
const struct snd_bebob_spec saffire_spec = {
|
||||
.clock = &saffire_both_clk_spec,
|
||||
.rate = &saffire_both_rate_spec,
|
||||
.meter = &saffire_meter_spec
|
||||
|
||||
@@ -628,7 +628,7 @@ static const char *const special_meter_labels[] = {
|
||||
static int
|
||||
special_meter_get(struct snd_bebob *bebob, u32 *target, unsigned int size)
|
||||
{
|
||||
u16 *buf;
|
||||
__be16 *buf;
|
||||
unsigned int i, c, channels;
|
||||
int err;
|
||||
|
||||
@@ -687,7 +687,7 @@ static const char *const nrv10_meter_labels[] = {
|
||||
static int
|
||||
normal_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
|
||||
{
|
||||
struct snd_bebob_meter_spec *spec = bebob->spec->meter;
|
||||
const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
|
||||
unsigned int c, channels;
|
||||
int err;
|
||||
|
||||
@@ -712,85 +712,85 @@ end:
|
||||
}
|
||||
|
||||
/* for special customized devices */
|
||||
static struct snd_bebob_rate_spec special_rate_spec = {
|
||||
static const struct snd_bebob_rate_spec special_rate_spec = {
|
||||
.get = &special_get_rate,
|
||||
.set = &special_set_rate,
|
||||
};
|
||||
static struct snd_bebob_clock_spec special_clk_spec = {
|
||||
static const struct snd_bebob_clock_spec special_clk_spec = {
|
||||
.num = ARRAY_SIZE(special_clk_types),
|
||||
.types = special_clk_types,
|
||||
.get = &special_clk_get,
|
||||
};
|
||||
static struct snd_bebob_meter_spec special_meter_spec = {
|
||||
static const struct snd_bebob_meter_spec special_meter_spec = {
|
||||
.num = ARRAY_SIZE(special_meter_labels),
|
||||
.labels = special_meter_labels,
|
||||
.get = &special_meter_get
|
||||
};
|
||||
struct snd_bebob_spec maudio_special_spec = {
|
||||
const struct snd_bebob_spec maudio_special_spec = {
|
||||
.clock = &special_clk_spec,
|
||||
.rate = &special_rate_spec,
|
||||
.meter = &special_meter_spec
|
||||
};
|
||||
|
||||
/* Firewire 410 specification */
|
||||
static struct snd_bebob_rate_spec usual_rate_spec = {
|
||||
static const struct snd_bebob_rate_spec usual_rate_spec = {
|
||||
.get = &snd_bebob_stream_get_rate,
|
||||
.set = &snd_bebob_stream_set_rate,
|
||||
};
|
||||
static struct snd_bebob_meter_spec fw410_meter_spec = {
|
||||
static const struct snd_bebob_meter_spec fw410_meter_spec = {
|
||||
.num = ARRAY_SIZE(fw410_meter_labels),
|
||||
.labels = fw410_meter_labels,
|
||||
.get = &normal_meter_get
|
||||
};
|
||||
struct snd_bebob_spec maudio_fw410_spec = {
|
||||
const struct snd_bebob_spec maudio_fw410_spec = {
|
||||
.clock = NULL,
|
||||
.rate = &usual_rate_spec,
|
||||
.meter = &fw410_meter_spec
|
||||
};
|
||||
|
||||
/* Firewire Audiophile specification */
|
||||
static struct snd_bebob_meter_spec audiophile_meter_spec = {
|
||||
static const struct snd_bebob_meter_spec audiophile_meter_spec = {
|
||||
.num = ARRAY_SIZE(audiophile_meter_labels),
|
||||
.labels = audiophile_meter_labels,
|
||||
.get = &normal_meter_get
|
||||
};
|
||||
struct snd_bebob_spec maudio_audiophile_spec = {
|
||||
const struct snd_bebob_spec maudio_audiophile_spec = {
|
||||
.clock = NULL,
|
||||
.rate = &usual_rate_spec,
|
||||
.meter = &audiophile_meter_spec
|
||||
};
|
||||
|
||||
/* Firewire Solo specification */
|
||||
static struct snd_bebob_meter_spec solo_meter_spec = {
|
||||
static const struct snd_bebob_meter_spec solo_meter_spec = {
|
||||
.num = ARRAY_SIZE(solo_meter_labels),
|
||||
.labels = solo_meter_labels,
|
||||
.get = &normal_meter_get
|
||||
};
|
||||
struct snd_bebob_spec maudio_solo_spec = {
|
||||
const struct snd_bebob_spec maudio_solo_spec = {
|
||||
.clock = NULL,
|
||||
.rate = &usual_rate_spec,
|
||||
.meter = &solo_meter_spec
|
||||
};
|
||||
|
||||
/* Ozonic specification */
|
||||
static struct snd_bebob_meter_spec ozonic_meter_spec = {
|
||||
static const struct snd_bebob_meter_spec ozonic_meter_spec = {
|
||||
.num = ARRAY_SIZE(ozonic_meter_labels),
|
||||
.labels = ozonic_meter_labels,
|
||||
.get = &normal_meter_get
|
||||
};
|
||||
struct snd_bebob_spec maudio_ozonic_spec = {
|
||||
const struct snd_bebob_spec maudio_ozonic_spec = {
|
||||
.clock = NULL,
|
||||
.rate = &usual_rate_spec,
|
||||
.meter = &ozonic_meter_spec
|
||||
};
|
||||
|
||||
/* NRV10 specification */
|
||||
static struct snd_bebob_meter_spec nrv10_meter_spec = {
|
||||
static const struct snd_bebob_meter_spec nrv10_meter_spec = {
|
||||
.num = ARRAY_SIZE(nrv10_meter_labels),
|
||||
.labels = nrv10_meter_labels,
|
||||
.get = &normal_meter_get
|
||||
};
|
||||
struct snd_bebob_spec maudio_nrv10_spec = {
|
||||
const struct snd_bebob_spec maudio_nrv10_spec = {
|
||||
.clock = NULL,
|
||||
.rate = &usual_rate_spec,
|
||||
.meter = &nrv10_meter_spec
|
||||
|
||||
@@ -72,11 +72,11 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
|
||||
spin_lock_irqsave(&bebob->lock, flags);
|
||||
|
||||
if (up)
|
||||
amdtp_stream_midi_trigger(&bebob->tx_stream,
|
||||
substrm->number, substrm);
|
||||
amdtp_am824_midi_trigger(&bebob->tx_stream,
|
||||
substrm->number, substrm);
|
||||
else
|
||||
amdtp_stream_midi_trigger(&bebob->tx_stream,
|
||||
substrm->number, NULL);
|
||||
amdtp_am824_midi_trigger(&bebob->tx_stream,
|
||||
substrm->number, NULL);
|
||||
|
||||
spin_unlock_irqrestore(&bebob->lock, flags);
|
||||
}
|
||||
@@ -89,11 +89,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
|
||||
spin_lock_irqsave(&bebob->lock, flags);
|
||||
|
||||
if (up)
|
||||
amdtp_stream_midi_trigger(&bebob->rx_stream,
|
||||
substrm->number, substrm);
|
||||
amdtp_am824_midi_trigger(&bebob->rx_stream,
|
||||
substrm->number, substrm);
|
||||
else
|
||||
amdtp_stream_midi_trigger(&bebob->rx_stream,
|
||||
substrm->number, NULL);
|
||||
amdtp_am824_midi_trigger(&bebob->rx_stream,
|
||||
substrm->number, NULL);
|
||||
|
||||
spin_unlock_irqrestore(&bebob->lock, flags);
|
||||
}
|
||||
|
||||
@@ -122,11 +122,11 @@ pcm_init_hw_params(struct snd_bebob *bebob,
|
||||
SNDRV_PCM_INFO_MMAP_VALID;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
|
||||
runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
|
||||
s = &bebob->tx_stream;
|
||||
formations = bebob->tx_stream_formations;
|
||||
} else {
|
||||
runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
|
||||
runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
|
||||
s = &bebob->rx_stream;
|
||||
formations = bebob->rx_stream_formations;
|
||||
}
|
||||
@@ -146,7 +146,7 @@ pcm_init_hw_params(struct snd_bebob *bebob,
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
|
||||
err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
@@ -155,7 +155,7 @@ static int
|
||||
pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_bebob *bebob = substream->private_data;
|
||||
struct snd_bebob_rate_spec *spec = bebob->spec->rate;
|
||||
const struct snd_bebob_rate_spec *spec = bebob->spec->rate;
|
||||
unsigned int sampling_rate;
|
||||
enum snd_bebob_clock_type src;
|
||||
int err;
|
||||
@@ -220,8 +220,8 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
|
||||
atomic_inc(&bebob->substreams_counter);
|
||||
amdtp_stream_set_pcm_format(&bebob->tx_stream,
|
||||
params_format(hw_params));
|
||||
|
||||
amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -239,8 +239,8 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
|
||||
atomic_inc(&bebob->substreams_counter);
|
||||
amdtp_stream_set_pcm_format(&bebob->rx_stream,
|
||||
params_format(hw_params));
|
||||
|
||||
amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ proc_read_meters(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct snd_bebob *bebob = entry->private_data;
|
||||
struct snd_bebob_meter_spec *spec = bebob->spec->meter;
|
||||
const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
|
||||
u32 *buf;
|
||||
unsigned int i, c, channels, size;
|
||||
|
||||
@@ -138,8 +138,8 @@ proc_read_clock(struct snd_info_entry *entry,
|
||||
"SYT-Match",
|
||||
};
|
||||
struct snd_bebob *bebob = entry->private_data;
|
||||
struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
|
||||
struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
|
||||
const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
|
||||
const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
|
||||
enum snd_bebob_clock_type src;
|
||||
unsigned int rate;
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ end:
|
||||
int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
|
||||
enum snd_bebob_clock_type *src)
|
||||
{
|
||||
struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
|
||||
const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
|
||||
u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7];
|
||||
unsigned int id;
|
||||
enum avc_bridgeco_plug_type type;
|
||||
@@ -338,7 +338,7 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
|
||||
err = -ENOSYS;
|
||||
goto end;
|
||||
}
|
||||
s->midi_position = stm_pos;
|
||||
amdtp_am824_set_midi_position(s, stm_pos);
|
||||
midi = stm_pos;
|
||||
break;
|
||||
/* for PCM data channel */
|
||||
@@ -354,11 +354,12 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
|
||||
case 0x09: /* Digital */
|
||||
default:
|
||||
location = pcm + sec_loc;
|
||||
if (location >= AMDTP_MAX_CHANNELS_FOR_PCM) {
|
||||
if (location >= AM824_MAX_CHANNELS_FOR_PCM) {
|
||||
err = -ENOSYS;
|
||||
goto end;
|
||||
}
|
||||
s->pcm_positions[location] = stm_pos;
|
||||
amdtp_am824_set_pcm_position(s, location,
|
||||
stm_pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -427,12 +428,19 @@ make_both_connections(struct snd_bebob *bebob, unsigned int rate)
|
||||
index = get_formation_index(rate);
|
||||
pcm_channels = bebob->tx_stream_formations[index].pcm;
|
||||
midi_channels = bebob->tx_stream_formations[index].midi;
|
||||
amdtp_stream_set_parameters(&bebob->tx_stream,
|
||||
rate, pcm_channels, midi_channels * 8);
|
||||
err = amdtp_am824_set_parameters(&bebob->tx_stream, rate,
|
||||
pcm_channels, midi_channels * 8,
|
||||
false);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
pcm_channels = bebob->rx_stream_formations[index].pcm;
|
||||
midi_channels = bebob->rx_stream_formations[index].midi;
|
||||
amdtp_stream_set_parameters(&bebob->rx_stream,
|
||||
rate, pcm_channels, midi_channels * 8);
|
||||
err = amdtp_am824_set_parameters(&bebob->rx_stream, rate,
|
||||
pcm_channels, midi_channels * 8,
|
||||
false);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
/* establish connections for both streams */
|
||||
err = cmp_connection_establish(&bebob->out_conn,
|
||||
@@ -530,8 +538,8 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = amdtp_stream_init(&bebob->tx_stream, bebob->unit,
|
||||
AMDTP_IN_STREAM, CIP_BLOCKING);
|
||||
err = amdtp_am824_init(&bebob->tx_stream, bebob->unit,
|
||||
AMDTP_IN_STREAM, CIP_BLOCKING);
|
||||
if (err < 0) {
|
||||
amdtp_stream_destroy(&bebob->tx_stream);
|
||||
destroy_both_connections(bebob);
|
||||
@@ -559,8 +567,8 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
|
||||
if (bebob->maudio_special_quirk)
|
||||
bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC;
|
||||
|
||||
err = amdtp_stream_init(&bebob->rx_stream, bebob->unit,
|
||||
AMDTP_OUT_STREAM, CIP_BLOCKING);
|
||||
err = amdtp_am824_init(&bebob->rx_stream, bebob->unit,
|
||||
AMDTP_OUT_STREAM, CIP_BLOCKING);
|
||||
if (err < 0) {
|
||||
amdtp_stream_destroy(&bebob->tx_stream);
|
||||
amdtp_stream_destroy(&bebob->rx_stream);
|
||||
@@ -572,7 +580,7 @@ end:
|
||||
|
||||
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
|
||||
{
|
||||
struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
|
||||
const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
|
||||
struct amdtp_stream *master, *slave;
|
||||
enum cip_flags sync_mode;
|
||||
unsigned int curr_rate;
|
||||
@@ -864,8 +872,8 @@ parse_stream_formation(u8 *buf, unsigned int len,
|
||||
}
|
||||
}
|
||||
|
||||
if (formation[i].pcm > AMDTP_MAX_CHANNELS_FOR_PCM ||
|
||||
formation[i].midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
|
||||
if (formation[i].pcm > AM824_MAX_CHANNELS_FOR_PCM ||
|
||||
formation[i].midi > AM824_MAX_CHANNELS_FOR_MIDI)
|
||||
return -ENOSYS;
|
||||
|
||||
return 0;
|
||||
@@ -959,7 +967,7 @@ end:
|
||||
|
||||
int snd_bebob_stream_discover(struct snd_bebob *bebob)
|
||||
{
|
||||
struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
|
||||
const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
|
||||
u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES];
|
||||
enum avc_bridgeco_plug_type type;
|
||||
unsigned int i;
|
||||
|
||||
@@ -55,30 +55,30 @@ phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_bebob_rate_spec phase_series_rate_spec = {
|
||||
static const struct snd_bebob_rate_spec phase_series_rate_spec = {
|
||||
.get = &snd_bebob_stream_get_rate,
|
||||
.set = &snd_bebob_stream_set_rate,
|
||||
};
|
||||
|
||||
/* PHASE 88 Rack FW */
|
||||
static struct snd_bebob_clock_spec phase88_rack_clk = {
|
||||
static const struct snd_bebob_clock_spec phase88_rack_clk = {
|
||||
.num = ARRAY_SIZE(phase88_rack_clk_src_types),
|
||||
.types = phase88_rack_clk_src_types,
|
||||
.get = &phase88_rack_clk_src_get,
|
||||
};
|
||||
struct snd_bebob_spec phase88_rack_spec = {
|
||||
const struct snd_bebob_spec phase88_rack_spec = {
|
||||
.clock = &phase88_rack_clk,
|
||||
.rate = &phase_series_rate_spec,
|
||||
.meter = NULL
|
||||
};
|
||||
|
||||
/* 'PHASE 24 FW' and 'PHASE X24 FW' */
|
||||
static struct snd_bebob_clock_spec phase24_series_clk = {
|
||||
static const struct snd_bebob_clock_spec phase24_series_clk = {
|
||||
.num = ARRAY_SIZE(phase24_series_clk_src_types),
|
||||
.types = phase24_series_clk_src_types,
|
||||
.get = &phase24_series_clk_src_get,
|
||||
};
|
||||
struct snd_bebob_spec phase24_series_spec = {
|
||||
const struct snd_bebob_spec phase24_series_spec = {
|
||||
.clock = &phase24_series_clk,
|
||||
.rate = &phase_series_rate_spec,
|
||||
.meter = NULL
|
||||
|
||||
@@ -46,16 +46,16 @@ clk_src_get(struct snd_bebob *bebob, unsigned int *id)
|
||||
|
||||
return 0;
|
||||
}
|
||||
static struct snd_bebob_clock_spec clock_spec = {
|
||||
static const struct snd_bebob_clock_spec clock_spec = {
|
||||
.num = ARRAY_SIZE(clk_src_types),
|
||||
.types = clk_src_types,
|
||||
.get = &clk_src_get,
|
||||
};
|
||||
static struct snd_bebob_rate_spec rate_spec = {
|
||||
static const struct snd_bebob_rate_spec rate_spec = {
|
||||
.get = &snd_bebob_stream_get_rate,
|
||||
.set = &snd_bebob_stream_set_rate,
|
||||
};
|
||||
struct snd_bebob_spec yamaha_go_spec = {
|
||||
const struct snd_bebob_spec yamaha_go_spec = {
|
||||
.clock = &clock_spec,
|
||||
.rate = &rate_spec,
|
||||
.meter = NULL
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
|
||||
dice-pcm.o dice-hwdep.o dice.o
|
||||
obj-m += snd-dice.o
|
||||
obj-$(CONFIG_SND_DICE) += snd-dice.o
|
||||
|
||||
@@ -52,10 +52,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
|
||||
spin_lock_irqsave(&dice->lock, flags);
|
||||
|
||||
if (up)
|
||||
amdtp_stream_midi_trigger(&dice->tx_stream,
|
||||
amdtp_am824_midi_trigger(&dice->tx_stream,
|
||||
substrm->number, substrm);
|
||||
else
|
||||
amdtp_stream_midi_trigger(&dice->tx_stream,
|
||||
amdtp_am824_midi_trigger(&dice->tx_stream,
|
||||
substrm->number, NULL);
|
||||
|
||||
spin_unlock_irqrestore(&dice->lock, flags);
|
||||
@@ -69,11 +69,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
|
||||
spin_lock_irqsave(&dice->lock, flags);
|
||||
|
||||
if (up)
|
||||
amdtp_stream_midi_trigger(&dice->rx_stream,
|
||||
substrm->number, substrm);
|
||||
amdtp_am824_midi_trigger(&dice->rx_stream,
|
||||
substrm->number, substrm);
|
||||
else
|
||||
amdtp_stream_midi_trigger(&dice->rx_stream,
|
||||
substrm->number, NULL);
|
||||
amdtp_am824_midi_trigger(&dice->rx_stream,
|
||||
substrm->number, NULL);
|
||||
|
||||
spin_unlock_irqrestore(&dice->lock, flags);
|
||||
}
|
||||
|
||||
@@ -133,11 +133,11 @@ static int init_hw_info(struct snd_dice *dice,
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
hw->formats = AMDTP_IN_PCM_FORMAT_BITS;
|
||||
hw->formats = AM824_IN_PCM_FORMAT_BITS;
|
||||
stream = &dice->tx_stream;
|
||||
pcm_channels = dice->tx_channels;
|
||||
} else {
|
||||
hw->formats = AMDTP_OUT_PCM_FORMAT_BITS;
|
||||
hw->formats = AM824_OUT_PCM_FORMAT_BITS;
|
||||
stream = &dice->rx_stream;
|
||||
pcm_channels = dice->rx_channels;
|
||||
}
|
||||
@@ -156,7 +156,7 @@ static int init_hw_info(struct snd_dice *dice,
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
|
||||
err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
@@ -243,8 +243,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
|
||||
mutex_unlock(&dice->mutex);
|
||||
}
|
||||
|
||||
amdtp_stream_set_pcm_format(&dice->tx_stream,
|
||||
params_format(hw_params));
|
||||
amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -265,8 +264,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
|
||||
mutex_unlock(&dice->mutex);
|
||||
}
|
||||
|
||||
amdtp_stream_set_pcm_format(&dice->rx_stream,
|
||||
params_format(hw_params));
|
||||
amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -44,16 +44,16 @@ int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
|
||||
static void release_resources(struct snd_dice *dice,
|
||||
struct fw_iso_resources *resources)
|
||||
{
|
||||
unsigned int channel;
|
||||
__be32 channel;
|
||||
|
||||
/* Reset channel number */
|
||||
channel = cpu_to_be32((u32)-1);
|
||||
if (resources == &dice->tx_resources)
|
||||
snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
|
||||
&channel, 4);
|
||||
&channel, sizeof(channel));
|
||||
else
|
||||
snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
|
||||
&channel, 4);
|
||||
&channel, sizeof(channel));
|
||||
|
||||
fw_iso_resources_free(resources);
|
||||
}
|
||||
@@ -62,7 +62,7 @@ static int keep_resources(struct snd_dice *dice,
|
||||
struct fw_iso_resources *resources,
|
||||
unsigned int max_payload_bytes)
|
||||
{
|
||||
unsigned int channel;
|
||||
__be32 channel;
|
||||
int err;
|
||||
|
||||
err = fw_iso_resources_allocate(resources, max_payload_bytes,
|
||||
@@ -74,10 +74,10 @@ static int keep_resources(struct snd_dice *dice,
|
||||
channel = cpu_to_be32(resources->channel);
|
||||
if (resources == &dice->tx_resources)
|
||||
err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
|
||||
&channel, 4);
|
||||
&channel, sizeof(channel));
|
||||
else
|
||||
err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
|
||||
&channel, 4);
|
||||
&channel, sizeof(channel));
|
||||
if (err < 0)
|
||||
release_resources(dice, resources);
|
||||
end:
|
||||
@@ -100,6 +100,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
|
||||
{
|
||||
struct fw_iso_resources *resources;
|
||||
unsigned int i, mode, pcm_chs, midi_ports;
|
||||
bool double_pcm_frames;
|
||||
int err;
|
||||
|
||||
err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
|
||||
@@ -125,21 +126,24 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
|
||||
* For this quirk, blocking mode is required and PCM buffer size should
|
||||
* be aligned to SYT_INTERVAL.
|
||||
*/
|
||||
if (mode > 1) {
|
||||
double_pcm_frames = mode > 1;
|
||||
if (double_pcm_frames) {
|
||||
rate /= 2;
|
||||
pcm_chs *= 2;
|
||||
stream->double_pcm_frames = true;
|
||||
} else {
|
||||
stream->double_pcm_frames = false;
|
||||
}
|
||||
|
||||
amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports);
|
||||
if (mode > 1) {
|
||||
err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports,
|
||||
double_pcm_frames);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
if (double_pcm_frames) {
|
||||
pcm_chs /= 2;
|
||||
|
||||
for (i = 0; i < pcm_chs; i++) {
|
||||
stream->pcm_positions[i] = i * 2;
|
||||
stream->pcm_positions[i + pcm_chs] = i * 2 + 1;
|
||||
amdtp_am824_set_pcm_position(stream, i, i * 2);
|
||||
amdtp_am824_set_pcm_position(stream, i + pcm_chs,
|
||||
i * 2 + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +306,7 @@ static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream)
|
||||
goto end;
|
||||
resources->channels_mask = 0x00000000ffffffffuLL;
|
||||
|
||||
err = amdtp_stream_init(stream, dice->unit, dir, CIP_BLOCKING);
|
||||
err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING);
|
||||
if (err < 0) {
|
||||
amdtp_stream_destroy(stream);
|
||||
fw_iso_resources_destroy(resources);
|
||||
|
||||
@@ -29,7 +29,8 @@ static int dice_interface_check(struct fw_unit *unit)
|
||||
struct fw_csr_iterator it;
|
||||
int key, val, vendor = -1, model = -1, err;
|
||||
unsigned int category, i;
|
||||
__be32 *pointers, value;
|
||||
__be32 *pointers;
|
||||
u32 value;
|
||||
__be32 version;
|
||||
|
||||
pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/rawmidi.h>
|
||||
|
||||
#include "../amdtp.h"
|
||||
#include "../amdtp-am824.h"
|
||||
#include "../iso-resources.h"
|
||||
#include "../lib.h"
|
||||
#include "dice-interface.h"
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \
|
||||
digi00x-pcm.o digi00x-hwdep.o \
|
||||
digi00x-transaction.o digi00x-midi.o digi00x.o
|
||||
obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o
|
||||
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* amdtp-dot.c - a part of driver for Digidesign Digi 002/003 family
|
||||
*
|
||||
* Copyright (c) 2014-2015 Takashi Sakamoto
|
||||
* Copyright (C) 2012 Robin Gareus <robin@gareus.org>
|
||||
* Copyright (C) 2012 Damien Zammit <damien@zamaudio.com>
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include <sound/pcm.h>
|
||||
#include "digi00x.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
|
||||
|
||||
/*
|
||||
* The double-oh-three algorithm was discovered by Robin Gareus and Damien
|
||||
* Zammit in 2012, with reverse-engineering for Digi 003 Rack.
|
||||
*/
|
||||
struct dot_state {
|
||||
u8 carry;
|
||||
u8 idx;
|
||||
unsigned int off;
|
||||
};
|
||||
|
||||
struct amdtp_dot {
|
||||
unsigned int pcm_channels;
|
||||
struct dot_state state;
|
||||
|
||||
unsigned int midi_ports;
|
||||
/* 2 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) */
|
||||
struct snd_rawmidi_substream *midi[2];
|
||||
int midi_fifo_used[2];
|
||||
int midi_fifo_limit;
|
||||
|
||||
void (*transfer_samples)(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames);
|
||||
};
|
||||
|
||||
/*
|
||||
* double-oh-three look up table
|
||||
*
|
||||
* @param idx index byte (audio-sample data) 0x00..0xff
|
||||
* @param off channel offset shift
|
||||
* @return salt to XOR with given data
|
||||
*/
|
||||
#define BYTE_PER_SAMPLE (4)
|
||||
#define MAGIC_DOT_BYTE (2)
|
||||
#define MAGIC_BYTE_OFF(x) (((x) * BYTE_PER_SAMPLE) + MAGIC_DOT_BYTE)
|
||||
static const u8 dot_scrt(const u8 idx, const unsigned int off)
|
||||
{
|
||||
/*
|
||||
* the length of the added pattern only depends on the lower nibble
|
||||
* of the last non-zero data
|
||||
*/
|
||||
static const u8 len[16] = {0, 1, 3, 5, 7, 9, 11, 13, 14,
|
||||
12, 10, 8, 6, 4, 2, 0};
|
||||
|
||||
/*
|
||||
* the lower nibble of the salt. Interleaved sequence.
|
||||
* this is walked backwards according to len[]
|
||||
*/
|
||||
static const u8 nib[15] = {0x8, 0x7, 0x9, 0x6, 0xa, 0x5, 0xb, 0x4,
|
||||
0xc, 0x3, 0xd, 0x2, 0xe, 0x1, 0xf};
|
||||
|
||||
/* circular list for the salt's hi nibble. */
|
||||
static const u8 hir[15] = {0x0, 0x6, 0xf, 0x8, 0x7, 0x5, 0x3, 0x4,
|
||||
0xc, 0xd, 0xe, 0x1, 0x2, 0xb, 0xa};
|
||||
|
||||
/*
|
||||
* start offset for upper nibble mapping.
|
||||
* note: 9 is /special/. In the case where the high nibble == 0x9,
|
||||
* hir[] is not used and - coincidentally - the salt's hi nibble is
|
||||
* 0x09 regardless of the offset.
|
||||
*/
|
||||
static const u8 hio[16] = {0, 11, 12, 6, 7, 5, 1, 4,
|
||||
3, 0x00, 14, 13, 8, 9, 10, 2};
|
||||
|
||||
const u8 ln = idx & 0xf;
|
||||
const u8 hn = (idx >> 4) & 0xf;
|
||||
const u8 hr = (hn == 0x9) ? 0x9 : hir[(hio[hn] + off) % 15];
|
||||
|
||||
if (len[ln] < off)
|
||||
return 0x00;
|
||||
|
||||
return ((nib[14 + off - len[ln]]) | (hr << 4));
|
||||
}
|
||||
|
||||
static void dot_encode_step(struct dot_state *state, __be32 *const buffer)
|
||||
{
|
||||
u8 * const data = (u8 *) buffer;
|
||||
|
||||
if (data[MAGIC_DOT_BYTE] != 0x00) {
|
||||
state->off = 0;
|
||||
state->idx = data[MAGIC_DOT_BYTE] ^ state->carry;
|
||||
}
|
||||
data[MAGIC_DOT_BYTE] ^= state->carry;
|
||||
state->carry = dot_scrt(state->idx, ++(state->off));
|
||||
}
|
||||
|
||||
int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
||||
unsigned int pcm_channels)
|
||||
{
|
||||
struct amdtp_dot *p = s->protocol;
|
||||
int err;
|
||||
|
||||
if (amdtp_stream_running(s))
|
||||
return -EBUSY;
|
||||
|
||||
/*
|
||||
* A first data channel is for MIDI conformant data channel, the rest is
|
||||
* Multi Bit Linear Audio data channel.
|
||||
*/
|
||||
err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
s->fdf = AMDTP_FDF_AM824 | s->sfc;
|
||||
|
||||
p->pcm_channels = pcm_channels;
|
||||
|
||||
if (s->direction == AMDTP_IN_STREAM)
|
||||
p->midi_ports = DOT_MIDI_IN_PORTS;
|
||||
else
|
||||
p->midi_ports = DOT_MIDI_OUT_PORTS;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
{
|
||||
struct amdtp_dot *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;
|
||||
|
||||
buffer++;
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
buffer[c] = cpu_to_be32((*src >> 8) | 0x40000000);
|
||||
dot_encode_step(&p->state, &buffer[c]);
|
||||
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_dot *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;
|
||||
|
||||
buffer++;
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
buffer[c] = cpu_to_be32((*src << 8) | 0x40000000);
|
||||
dot_encode_step(&p->state, &buffer[c]);
|
||||
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_dot *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;
|
||||
|
||||
buffer++;
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
*dst = be32_to_cpu(buffer[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 data_blocks)
|
||||
{
|
||||
struct amdtp_dot *p = s->protocol;
|
||||
unsigned int channels, i, c;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
|
||||
buffer++;
|
||||
for (i = 0; i < data_blocks; ++i) {
|
||||
for (c = 0; c < channels; ++c)
|
||||
buffer[c] = cpu_to_be32(0x40000000);
|
||||
buffer += s->data_block_quadlets;
|
||||
}
|
||||
}
|
||||
|
||||
static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
|
||||
{
|
||||
struct amdtp_dot *p = s->protocol;
|
||||
int used;
|
||||
|
||||
used = p->midi_fifo_used[port];
|
||||
if (used == 0)
|
||||
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 inline void midi_use_bytes(struct amdtp_stream *s,
|
||||
unsigned int port, unsigned int count)
|
||||
{
|
||||
struct amdtp_dot *p = s->protocol;
|
||||
|
||||
p->midi_fifo_used[port] += amdtp_rate_table[s->sfc] * count;
|
||||
}
|
||||
|
||||
static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
|
||||
unsigned int data_blocks)
|
||||
{
|
||||
struct amdtp_dot *p = s->protocol;
|
||||
unsigned int f, port;
|
||||
int len;
|
||||
u8 *b;
|
||||
|
||||
for (f = 0; f < data_blocks; f++) {
|
||||
port = (s->data_block_counter + f) % 8;
|
||||
b = (u8 *)&buffer[0];
|
||||
|
||||
len = 0;
|
||||
if (port < p->midi_ports &&
|
||||
midi_ratelimit_per_packet(s, port) &&
|
||||
p->midi[port] != NULL)
|
||||
len = snd_rawmidi_transmit(p->midi[port], b + 1, 2);
|
||||
|
||||
if (len > 0) {
|
||||
b[3] = (0x10 << port) | len;
|
||||
midi_use_bytes(s, port, len);
|
||||
} else {
|
||||
b[1] = 0;
|
||||
b[2] = 0;
|
||||
b[3] = 0;
|
||||
}
|
||||
b[0] = 0x80;
|
||||
|
||||
buffer += s->data_block_quadlets;
|
||||
}
|
||||
}
|
||||
|
||||
static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
|
||||
unsigned int data_blocks)
|
||||
{
|
||||
struct amdtp_dot *p = s->protocol;
|
||||
unsigned int f, port, len;
|
||||
u8 *b;
|
||||
|
||||
for (f = 0; f < data_blocks; f++) {
|
||||
b = (u8 *)&buffer[0];
|
||||
port = b[3] >> 4;
|
||||
len = b[3] & 0x0f;
|
||||
|
||||
if (port < p->midi_ports && p->midi[port] && len > 0)
|
||||
snd_rawmidi_receive(p->midi[port], b + 1, len);
|
||||
|
||||
buffer += s->data_block_quadlets;
|
||||
}
|
||||
}
|
||||
|
||||
int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
|
||||
struct snd_pcm_runtime *runtime)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* This protocol delivers 24 bit data in 32bit data channel. */
|
||||
err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return amdtp_stream_add_pcm_hw_constraints(s, runtime);
|
||||
}
|
||||
|
||||
void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
|
||||
{
|
||||
struct amdtp_dot *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;
|
||||
}
|
||||
}
|
||||
|
||||
void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
|
||||
struct snd_rawmidi_substream *midi)
|
||||
{
|
||||
struct amdtp_dot *p = s->protocol;
|
||||
|
||||
if (port < p->midi_ports)
|
||||
ACCESS_ONCE(p->midi[port]) = midi;
|
||||
}
|
||||
|
||||
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
{
|
||||
struct amdtp_dot *p = (struct amdtp_dot *)s->protocol;
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames;
|
||||
|
||||
pcm = ACCESS_ONCE(s->pcm);
|
||||
if (pcm) {
|
||||
p->transfer_samples(s, pcm, buffer, data_blocks);
|
||||
pcm_frames = data_blocks;
|
||||
} else {
|
||||
pcm_frames = 0;
|
||||
}
|
||||
|
||||
read_midi_messages(s, buffer, data_blocks);
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
{
|
||||
struct amdtp_dot *p = (struct amdtp_dot *)s->protocol;
|
||||
struct snd_pcm_substream *pcm;
|
||||
unsigned int pcm_frames;
|
||||
|
||||
pcm = ACCESS_ONCE(s->pcm);
|
||||
if (pcm) {
|
||||
p->transfer_samples(s, pcm, buffer, data_blocks);
|
||||
pcm_frames = data_blocks;
|
||||
} else {
|
||||
write_pcm_silence(s, buffer, data_blocks);
|
||||
pcm_frames = 0;
|
||||
}
|
||||
|
||||
write_midi_messages(s, buffer, data_blocks);
|
||||
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir)
|
||||
{
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks;
|
||||
enum cip_flags flags;
|
||||
|
||||
/* Use different mode between incoming/outgoing. */
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
flags = CIP_NONBLOCKING | CIP_SKIP_INIT_DBC_CHECK;
|
||||
process_data_blocks = process_tx_data_blocks;
|
||||
} else {
|
||||
flags = CIP_BLOCKING;
|
||||
process_data_blocks = process_rx_data_blocks;
|
||||
}
|
||||
|
||||
return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
|
||||
process_data_blocks, sizeof(struct amdtp_dot));
|
||||
}
|
||||
|
||||
void amdtp_dot_reset(struct amdtp_stream *s)
|
||||
{
|
||||
struct amdtp_dot *p = s->protocol;
|
||||
|
||||
p->state.carry = 0x00;
|
||||
p->state.idx = 0x00;
|
||||
p->state.off = 0;
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* digi00x-hwdep.c - a part of driver for Digidesign Digi 002/003 family
|
||||
*
|
||||
* Copyright (c) 2014-2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This codes give three functionality.
|
||||
*
|
||||
* 1.get firewire node information
|
||||
* 2.get notification about starting/stopping stream
|
||||
* 3.lock/unlock stream
|
||||
* 4.get asynchronous messaging
|
||||
*/
|
||||
|
||||
#include "digi00x.h"
|
||||
|
||||
static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
|
||||
loff_t *offset)
|
||||
{
|
||||
struct snd_dg00x *dg00x = hwdep->private_data;
|
||||
DEFINE_WAIT(wait);
|
||||
union snd_firewire_event event;
|
||||
|
||||
spin_lock_irq(&dg00x->lock);
|
||||
|
||||
while (!dg00x->dev_lock_changed && dg00x->msg == 0) {
|
||||
prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
|
||||
spin_unlock_irq(&dg00x->lock);
|
||||
schedule();
|
||||
finish_wait(&dg00x->hwdep_wait, &wait);
|
||||
if (signal_pending(current))
|
||||
return -ERESTARTSYS;
|
||||
spin_lock_irq(&dg00x->lock);
|
||||
}
|
||||
|
||||
memset(&event, 0, sizeof(event));
|
||||
if (dg00x->dev_lock_changed) {
|
||||
event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
|
||||
event.lock_status.status = (dg00x->dev_lock_count > 0);
|
||||
dg00x->dev_lock_changed = false;
|
||||
|
||||
count = min_t(long, count, sizeof(event.lock_status));
|
||||
} else {
|
||||
event.digi00x_message.type =
|
||||
SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE;
|
||||
event.digi00x_message.message = dg00x->msg;
|
||||
dg00x->msg = 0;
|
||||
|
||||
count = min_t(long, count, sizeof(event.digi00x_message));
|
||||
}
|
||||
|
||||
spin_unlock_irq(&dg00x->lock);
|
||||
|
||||
if (copy_to_user(buf, &event, count))
|
||||
return -EFAULT;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
|
||||
poll_table *wait)
|
||||
{
|
||||
struct snd_dg00x *dg00x = hwdep->private_data;
|
||||
unsigned int events;
|
||||
|
||||
poll_wait(file, &dg00x->hwdep_wait, wait);
|
||||
|
||||
spin_lock_irq(&dg00x->lock);
|
||||
if (dg00x->dev_lock_changed || dg00x->msg)
|
||||
events = POLLIN | POLLRDNORM;
|
||||
else
|
||||
events = 0;
|
||||
spin_unlock_irq(&dg00x->lock);
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg)
|
||||
{
|
||||
struct fw_device *dev = fw_parent_device(dg00x->unit);
|
||||
struct snd_firewire_get_info info;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.type = SNDRV_FIREWIRE_TYPE_DIGI00X;
|
||||
info.card = dev->card->index;
|
||||
*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
|
||||
*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
|
||||
strlcpy(info.device_name, dev_name(&dev->device),
|
||||
sizeof(info.device_name));
|
||||
|
||||
if (copy_to_user(arg, &info, sizeof(info)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hwdep_lock(struct snd_dg00x *dg00x)
|
||||
{
|
||||
int err;
|
||||
|
||||
spin_lock_irq(&dg00x->lock);
|
||||
|
||||
if (dg00x->dev_lock_count == 0) {
|
||||
dg00x->dev_lock_count = -1;
|
||||
err = 0;
|
||||
} else {
|
||||
err = -EBUSY;
|
||||
}
|
||||
|
||||
spin_unlock_irq(&dg00x->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hwdep_unlock(struct snd_dg00x *dg00x)
|
||||
{
|
||||
int err;
|
||||
|
||||
spin_lock_irq(&dg00x->lock);
|
||||
|
||||
if (dg00x->dev_lock_count == -1) {
|
||||
dg00x->dev_lock_count = 0;
|
||||
err = 0;
|
||||
} else {
|
||||
err = -EBADFD;
|
||||
}
|
||||
|
||||
spin_unlock_irq(&dg00x->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
|
||||
{
|
||||
struct snd_dg00x *dg00x = hwdep->private_data;
|
||||
|
||||
spin_lock_irq(&dg00x->lock);
|
||||
if (dg00x->dev_lock_count == -1)
|
||||
dg00x->dev_lock_count = 0;
|
||||
spin_unlock_irq(&dg00x->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct snd_dg00x *dg00x = hwdep->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_FIREWIRE_IOCTL_GET_INFO:
|
||||
return hwdep_get_info(dg00x, (void __user *)arg);
|
||||
case SNDRV_FIREWIRE_IOCTL_LOCK:
|
||||
return hwdep_lock(dg00x);
|
||||
case SNDRV_FIREWIRE_IOCTL_UNLOCK:
|
||||
return hwdep_unlock(dg00x);
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
return hwdep_ioctl(hwdep, file, cmd,
|
||||
(unsigned long)compat_ptr(arg));
|
||||
}
|
||||
#else
|
||||
#define hwdep_compat_ioctl NULL
|
||||
#endif
|
||||
|
||||
static const struct snd_hwdep_ops hwdep_ops = {
|
||||
.read = hwdep_read,
|
||||
.release = hwdep_release,
|
||||
.poll = hwdep_poll,
|
||||
.ioctl = hwdep_ioctl,
|
||||
.ioctl_compat = hwdep_compat_ioctl,
|
||||
};
|
||||
|
||||
int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x)
|
||||
{
|
||||
struct snd_hwdep *hwdep;
|
||||
int err;
|
||||
|
||||
err = snd_hwdep_new(dg00x->card, "Digi00x", 0, &hwdep);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
strcpy(hwdep->name, "Digi00x");
|
||||
hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X;
|
||||
hwdep->ops = hwdep_ops;
|
||||
hwdep->private_data = dg00x;
|
||||
hwdep->exclusive = true;
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* digi00x-midi.h - a part of driver for Digidesign Digi 002/003 family
|
||||
*
|
||||
* Copyright (c) 2014-2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include "digi00x.h"
|
||||
|
||||
static int midi_phys_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->rmidi->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_dg00x_stream_lock_try(dg00x);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mutex_lock(&dg00x->mutex);
|
||||
dg00x->substreams_counter++;
|
||||
err = snd_dg00x_stream_start_duplex(dg00x, 0);
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
if (err < 0)
|
||||
snd_dg00x_stream_lock_release(dg00x);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int midi_phys_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->rmidi->private_data;
|
||||
|
||||
mutex_lock(&dg00x->mutex);
|
||||
dg00x->substreams_counter--;
|
||||
snd_dg00x_stream_stop_duplex(dg00x);
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
|
||||
snd_dg00x_stream_lock_release(dg00x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void midi_phys_capture_trigger(struct snd_rawmidi_substream *substream,
|
||||
int up)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->rmidi->private_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dg00x->lock, flags);
|
||||
|
||||
if (up)
|
||||
amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number,
|
||||
substream);
|
||||
else
|
||||
amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number,
|
||||
NULL);
|
||||
|
||||
spin_unlock_irqrestore(&dg00x->lock, flags);
|
||||
}
|
||||
|
||||
static void midi_phys_playback_trigger(struct snd_rawmidi_substream *substream,
|
||||
int up)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->rmidi->private_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dg00x->lock, flags);
|
||||
|
||||
if (up)
|
||||
amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number,
|
||||
substream);
|
||||
else
|
||||
amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number,
|
||||
NULL);
|
||||
|
||||
spin_unlock_irqrestore(&dg00x->lock, flags);
|
||||
}
|
||||
|
||||
static struct snd_rawmidi_ops midi_phys_capture_ops = {
|
||||
.open = midi_phys_open,
|
||||
.close = midi_phys_close,
|
||||
.trigger = midi_phys_capture_trigger,
|
||||
};
|
||||
|
||||
static struct snd_rawmidi_ops midi_phys_playback_ops = {
|
||||
.open = midi_phys_open,
|
||||
.close = midi_phys_close,
|
||||
.trigger = midi_phys_playback_trigger,
|
||||
};
|
||||
|
||||
static int midi_ctl_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
/* Do nothing. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int midi_ctl_capture_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
/* Do nothing. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int midi_ctl_playback_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->rmidi->private_data;
|
||||
|
||||
snd_fw_async_midi_port_finish(&dg00x->out_control);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void midi_ctl_capture_trigger(struct snd_rawmidi_substream *substream,
|
||||
int up)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->rmidi->private_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dg00x->lock, flags);
|
||||
|
||||
if (up)
|
||||
dg00x->in_control = substream;
|
||||
else
|
||||
dg00x->in_control = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&dg00x->lock, flags);
|
||||
}
|
||||
|
||||
static void midi_ctl_playback_trigger(struct snd_rawmidi_substream *substream,
|
||||
int up)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->rmidi->private_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dg00x->lock, flags);
|
||||
|
||||
if (up)
|
||||
snd_fw_async_midi_port_run(&dg00x->out_control, substream);
|
||||
|
||||
spin_unlock_irqrestore(&dg00x->lock, flags);
|
||||
}
|
||||
|
||||
static struct snd_rawmidi_ops midi_ctl_capture_ops = {
|
||||
.open = midi_ctl_open,
|
||||
.close = midi_ctl_capture_close,
|
||||
.trigger = midi_ctl_capture_trigger,
|
||||
};
|
||||
|
||||
static struct snd_rawmidi_ops midi_ctl_playback_ops = {
|
||||
.open = midi_ctl_open,
|
||||
.close = midi_ctl_playback_close,
|
||||
.trigger = midi_ctl_playback_trigger,
|
||||
};
|
||||
|
||||
static void set_midi_substream_names(struct snd_dg00x *dg00x,
|
||||
struct snd_rawmidi_str *str,
|
||||
bool is_ctl)
|
||||
{
|
||||
struct snd_rawmidi_substream *subs;
|
||||
|
||||
list_for_each_entry(subs, &str->substreams, list) {
|
||||
if (!is_ctl)
|
||||
snprintf(subs->name, sizeof(subs->name),
|
||||
"%s MIDI %d",
|
||||
dg00x->card->shortname, subs->number + 1);
|
||||
else
|
||||
/* This port is for asynchronous transaction. */
|
||||
snprintf(subs->name, sizeof(subs->name),
|
||||
"%s control",
|
||||
dg00x->card->shortname);
|
||||
}
|
||||
}
|
||||
|
||||
int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x)
|
||||
{
|
||||
struct snd_rawmidi *rmidi[2];
|
||||
struct snd_rawmidi_str *str;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
/* Add physical midi ports. */
|
||||
err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0,
|
||||
DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS, &rmidi[0]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
snprintf(rmidi[0]->name, sizeof(rmidi[0]->name),
|
||||
"%s MIDI", dg00x->card->shortname);
|
||||
|
||||
snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_INPUT,
|
||||
&midi_phys_capture_ops);
|
||||
snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_OUTPUT,
|
||||
&midi_phys_playback_ops);
|
||||
|
||||
/* Add a pair of control midi ports. */
|
||||
err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 1,
|
||||
1, 1, &rmidi[1]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
snprintf(rmidi[1]->name, sizeof(rmidi[1]->name),
|
||||
"%s control", dg00x->card->shortname);
|
||||
|
||||
snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_INPUT,
|
||||
&midi_ctl_capture_ops);
|
||||
snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_OUTPUT,
|
||||
&midi_ctl_playback_ops);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rmidi); i++) {
|
||||
rmidi[i]->private_data = dg00x;
|
||||
|
||||
rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
|
||||
str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_INPUT];
|
||||
set_midi_substream_names(dg00x, str, i);
|
||||
|
||||
rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
|
||||
str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
|
||||
set_midi_substream_names(dg00x, str, i);
|
||||
|
||||
rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* digi00x-pcm.c - a part of driver for Digidesign Digi 002/003 family
|
||||
*
|
||||
* Copyright (c) 2014-2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include "digi00x.h"
|
||||
|
||||
static int hw_rule_rate(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct snd_interval *r =
|
||||
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
|
||||
const struct snd_interval *c =
|
||||
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
struct snd_interval t = {
|
||||
.min = UINT_MAX, .max = 0, .integer = 1,
|
||||
};
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
|
||||
if (!snd_interval_test(c,
|
||||
snd_dg00x_stream_pcm_channels[i]))
|
||||
continue;
|
||||
|
||||
t.min = min(t.min, snd_dg00x_stream_rates[i]);
|
||||
t.max = max(t.max, snd_dg00x_stream_rates[i]);
|
||||
}
|
||||
|
||||
return snd_interval_refine(r, &t);
|
||||
}
|
||||
|
||||
static int hw_rule_channels(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct snd_interval *c =
|
||||
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
const struct snd_interval *r =
|
||||
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
|
||||
struct snd_interval t = {
|
||||
.min = UINT_MAX, .max = 0, .integer = 1,
|
||||
};
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
|
||||
if (!snd_interval_test(r, snd_dg00x_stream_rates[i]))
|
||||
continue;
|
||||
|
||||
t.min = min(t.min, snd_dg00x_stream_pcm_channels[i]);
|
||||
t.max = max(t.max, snd_dg00x_stream_pcm_channels[i]);
|
||||
}
|
||||
|
||||
return snd_interval_refine(c, &t);
|
||||
}
|
||||
|
||||
static int pcm_init_hw_params(struct snd_dg00x *dg00x,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
static const struct snd_pcm_hardware hardware = {
|
||||
.info = SNDRV_PCM_INFO_BATCH |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_JOINT_DUPLEX |
|
||||
SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID,
|
||||
.rates = SNDRV_PCM_RATE_44100 |
|
||||
SNDRV_PCM_RATE_48000 |
|
||||
SNDRV_PCM_RATE_88200 |
|
||||
SNDRV_PCM_RATE_96000,
|
||||
.rate_min = 44100,
|
||||
.rate_max = 96000,
|
||||
.channels_min = 10,
|
||||
.channels_max = 18,
|
||||
.period_bytes_min = 4 * 18,
|
||||
.period_bytes_max = 4 * 18 * 2048,
|
||||
.buffer_bytes_max = 4 * 18 * 2048 * 2,
|
||||
.periods_min = 2,
|
||||
.periods_max = UINT_MAX,
|
||||
};
|
||||
struct amdtp_stream *s;
|
||||
int err;
|
||||
|
||||
substream->runtime->hw = hardware;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
|
||||
s = &dg00x->tx_stream;
|
||||
} else {
|
||||
substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S16 |
|
||||
SNDRV_PCM_FMTBIT_S32;
|
||||
s = &dg00x->rx_stream;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_rule_add(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
hw_rule_channels, NULL,
|
||||
SNDRV_PCM_HW_PARAM_RATE, -1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = snd_pcm_hw_rule_add(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
hw_rule_rate, NULL,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return amdtp_dot_add_pcm_hw_constraints(s, substream->runtime);
|
||||
}
|
||||
|
||||
static int pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->private_data;
|
||||
enum snd_dg00x_clock clock;
|
||||
bool detect;
|
||||
unsigned int rate;
|
||||
int err;
|
||||
|
||||
err = snd_dg00x_stream_lock_try(dg00x);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = pcm_init_hw_params(dg00x, substream);
|
||||
if (err < 0)
|
||||
goto err_locked;
|
||||
|
||||
/* Check current clock source. */
|
||||
err = snd_dg00x_stream_get_clock(dg00x, &clock);
|
||||
if (err < 0)
|
||||
goto err_locked;
|
||||
if (clock != SND_DG00X_CLOCK_INTERNAL) {
|
||||
err = snd_dg00x_stream_check_external_clock(dg00x, &detect);
|
||||
if (err < 0)
|
||||
goto err_locked;
|
||||
if (!detect) {
|
||||
err = -EBUSY;
|
||||
goto err_locked;
|
||||
}
|
||||
}
|
||||
|
||||
if ((clock != SND_DG00X_CLOCK_INTERNAL) ||
|
||||
amdtp_stream_pcm_running(&dg00x->rx_stream) ||
|
||||
amdtp_stream_pcm_running(&dg00x->tx_stream)) {
|
||||
err = snd_dg00x_stream_get_external_rate(dg00x, &rate);
|
||||
if (err < 0)
|
||||
goto err_locked;
|
||||
substream->runtime->hw.rate_min = rate;
|
||||
substream->runtime->hw.rate_max = rate;
|
||||
}
|
||||
|
||||
snd_pcm_set_sync(substream);
|
||||
end:
|
||||
return err;
|
||||
err_locked:
|
||||
snd_dg00x_stream_lock_release(dg00x);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->private_data;
|
||||
|
||||
snd_dg00x_stream_lock_release(dg00x);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&dg00x->mutex);
|
||||
dg00x->substreams_counter++;
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
}
|
||||
|
||||
amdtp_dot_set_pcm_format(&dg00x->tx_stream, params_format(hw_params));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&dg00x->mutex);
|
||||
dg00x->substreams_counter++;
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
}
|
||||
|
||||
amdtp_dot_set_pcm_format(&dg00x->rx_stream, params_format(hw_params));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->private_data;
|
||||
|
||||
mutex_lock(&dg00x->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
dg00x->substreams_counter--;
|
||||
|
||||
snd_dg00x_stream_stop_duplex(dg00x);
|
||||
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
|
||||
return snd_pcm_lib_free_vmalloc_buffer(substream);
|
||||
}
|
||||
|
||||
static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->private_data;
|
||||
|
||||
mutex_lock(&dg00x->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
dg00x->substreams_counter--;
|
||||
|
||||
snd_dg00x_stream_stop_duplex(dg00x);
|
||||
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
|
||||
return snd_pcm_lib_free_vmalloc_buffer(substream);
|
||||
}
|
||||
|
||||
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
|
||||
mutex_lock(&dg00x->mutex);
|
||||
|
||||
err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
|
||||
if (err >= 0)
|
||||
amdtp_stream_pcm_prepare(&dg00x->tx_stream);
|
||||
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
|
||||
mutex_lock(&dg00x->mutex);
|
||||
|
||||
err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
|
||||
if (err >= 0) {
|
||||
amdtp_stream_pcm_prepare(&dg00x->rx_stream);
|
||||
amdtp_dot_reset(&dg00x->rx_stream);
|
||||
}
|
||||
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
amdtp_stream_pcm_trigger(&dg00x->tx_stream, substream);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
amdtp_stream_pcm_trigger(&dg00x->tx_stream, NULL);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_dg00x *dg00x = substream->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
amdtp_stream_pcm_trigger(&dg00x->rx_stream, substream);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
amdtp_stream_pcm_trigger(&dg00x->rx_stream, NULL);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
|
||||
{
|
||||
struct snd_dg00x *dg00x = sbstrm->private_data;
|
||||
|
||||
return amdtp_stream_pcm_pointer(&dg00x->tx_stream);
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
|
||||
{
|
||||
struct snd_dg00x *dg00x = sbstrm->private_data;
|
||||
|
||||
return amdtp_stream_pcm_pointer(&dg00x->rx_stream);
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops pcm_capture_ops = {
|
||||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = pcm_capture_hw_params,
|
||||
.hw_free = pcm_capture_hw_free,
|
||||
.prepare = pcm_capture_prepare,
|
||||
.trigger = pcm_capture_trigger,
|
||||
.pointer = pcm_capture_pointer,
|
||||
.page = snd_pcm_lib_get_vmalloc_page,
|
||||
};
|
||||
|
||||
static struct snd_pcm_ops pcm_playback_ops = {
|
||||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = pcm_playback_hw_params,
|
||||
.hw_free = pcm_playback_hw_free,
|
||||
.prepare = pcm_playback_prepare,
|
||||
.trigger = pcm_playback_trigger,
|
||||
.pointer = pcm_playback_pointer,
|
||||
.page = snd_pcm_lib_get_vmalloc_page,
|
||||
.mmap = snd_pcm_lib_mmap_vmalloc,
|
||||
};
|
||||
|
||||
int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
|
||||
{
|
||||
struct snd_pcm *pcm;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_new(dg00x->card, dg00x->card->driver, 0, 1, 1, &pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
pcm->private_data = dg00x;
|
||||
snprintf(pcm->name, sizeof(pcm->name),
|
||||
"%s PCM", dg00x->card->shortname);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* digi00x-proc.c - a part of driver for Digidesign Digi 002/003 family
|
||||
*
|
||||
* Copyright (c) 2014-2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include "digi00x.h"
|
||||
|
||||
static int get_optical_iface_mode(struct snd_dg00x *dg00x,
|
||||
enum snd_dg00x_optical_mode *mode)
|
||||
{
|
||||
__be32 data;
|
||||
int err;
|
||||
|
||||
err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_OPT_IFACE_MODE,
|
||||
&data, sizeof(data), 0);
|
||||
if (err >= 0)
|
||||
*mode = be32_to_cpu(data) & 0x01;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void proc_read_clock(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buf)
|
||||
{
|
||||
static const char *const source_name[] = {
|
||||
[SND_DG00X_CLOCK_INTERNAL] = "internal",
|
||||
[SND_DG00X_CLOCK_SPDIF] = "s/pdif",
|
||||
[SND_DG00X_CLOCK_ADAT] = "adat",
|
||||
[SND_DG00X_CLOCK_WORD] = "word clock",
|
||||
};
|
||||
static const char *const optical_name[] = {
|
||||
[SND_DG00X_OPT_IFACE_MODE_ADAT] = "adat",
|
||||
[SND_DG00X_OPT_IFACE_MODE_SPDIF] = "s/pdif",
|
||||
};
|
||||
struct snd_dg00x *dg00x = entry->private_data;
|
||||
enum snd_dg00x_optical_mode mode;
|
||||
unsigned int rate;
|
||||
enum snd_dg00x_clock clock;
|
||||
bool detect;
|
||||
|
||||
if (get_optical_iface_mode(dg00x, &mode) < 0)
|
||||
return;
|
||||
if (snd_dg00x_stream_get_local_rate(dg00x, &rate) < 0)
|
||||
return;
|
||||
if (snd_dg00x_stream_get_clock(dg00x, &clock) < 0)
|
||||
return;
|
||||
|
||||
snd_iprintf(buf, "Optical mode: %s\n", optical_name[mode]);
|
||||
snd_iprintf(buf, "Sampling Rate: %d\n", rate);
|
||||
snd_iprintf(buf, "Clock Source: %s\n", source_name[clock]);
|
||||
|
||||
if (clock == SND_DG00X_CLOCK_INTERNAL)
|
||||
return;
|
||||
|
||||
if (snd_dg00x_stream_check_external_clock(dg00x, &detect) < 0)
|
||||
return;
|
||||
snd_iprintf(buf, "External source: %s\n", detect ? "detected" : "not");
|
||||
if (!detect)
|
||||
return;
|
||||
|
||||
if (snd_dg00x_stream_get_external_rate(dg00x, &rate) >= 0)
|
||||
snd_iprintf(buf, "External sampling rate: %d\n", rate);
|
||||
}
|
||||
|
||||
void snd_dg00x_proc_init(struct snd_dg00x *dg00x)
|
||||
{
|
||||
struct snd_info_entry *root, *entry;
|
||||
|
||||
/*
|
||||
* All nodes are automatically removed at snd_card_disconnect(),
|
||||
* by following to link list.
|
||||
*/
|
||||
root = snd_info_create_card_entry(dg00x->card, "firewire",
|
||||
dg00x->card->proc_root);
|
||||
if (root == NULL)
|
||||
return;
|
||||
|
||||
root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
if (snd_info_register(root) < 0) {
|
||||
snd_info_free_entry(root);
|
||||
return;
|
||||
}
|
||||
|
||||
entry = snd_info_create_card_entry(dg00x->card, "clock", root);
|
||||
if (entry == NULL) {
|
||||
snd_info_free_entry(root);
|
||||
return;
|
||||
}
|
||||
|
||||
snd_info_set_text_ops(entry, dg00x, proc_read_clock);
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
snd_info_free_entry(root);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,422 @@
|
||||
/*
|
||||
* digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
|
||||
*
|
||||
* Copyright (c) 2014-2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include "digi00x.h"
|
||||
|
||||
#define CALLBACK_TIMEOUT 500
|
||||
|
||||
const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
|
||||
[SND_DG00X_RATE_44100] = 44100,
|
||||
[SND_DG00X_RATE_48000] = 48000,
|
||||
[SND_DG00X_RATE_88200] = 88200,
|
||||
[SND_DG00X_RATE_96000] = 96000,
|
||||
};
|
||||
|
||||
/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */
|
||||
const unsigned int
|
||||
snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {
|
||||
/* Analog/ADAT/SPDIF */
|
||||
[SND_DG00X_RATE_44100] = (8 + 8 + 2),
|
||||
[SND_DG00X_RATE_48000] = (8 + 8 + 2),
|
||||
/* Analog/SPDIF */
|
||||
[SND_DG00X_RATE_88200] = (8 + 2),
|
||||
[SND_DG00X_RATE_96000] = (8 + 2),
|
||||
};
|
||||
|
||||
int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate)
|
||||
{
|
||||
u32 data;
|
||||
__be32 reg;
|
||||
int err;
|
||||
|
||||
err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
data = be32_to_cpu(reg) & 0x0f;
|
||||
if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
|
||||
*rate = snd_dg00x_stream_rates[data];
|
||||
else
|
||||
err = -EIO;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate)
|
||||
{
|
||||
__be32 reg;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {
|
||||
if (rate == snd_dg00x_stream_rates[i])
|
||||
break;
|
||||
}
|
||||
if (i == ARRAY_SIZE(snd_dg00x_stream_rates))
|
||||
return -EINVAL;
|
||||
|
||||
reg = cpu_to_be32(i);
|
||||
return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
|
||||
®, sizeof(reg), 0);
|
||||
}
|
||||
|
||||
int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
|
||||
enum snd_dg00x_clock *clock)
|
||||
{
|
||||
__be32 reg;
|
||||
int err;
|
||||
|
||||
err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
*clock = be32_to_cpu(reg) & 0x0f;
|
||||
if (*clock >= SND_DG00X_CLOCK_COUNT)
|
||||
err = -EIO;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect)
|
||||
{
|
||||
__be32 reg;
|
||||
int err;
|
||||
|
||||
err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL,
|
||||
®, sizeof(reg), 0);
|
||||
if (err >= 0)
|
||||
*detect = be32_to_cpu(reg) > 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
|
||||
unsigned int *rate)
|
||||
{
|
||||
u32 data;
|
||||
__be32 reg;
|
||||
int err;
|
||||
|
||||
err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
data = be32_to_cpu(reg) & 0x0f;
|
||||
if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
|
||||
*rate = snd_dg00x_stream_rates[data];
|
||||
/* This means desync. */
|
||||
else
|
||||
err = -EBUSY;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void finish_session(struct snd_dg00x *dg00x)
|
||||
{
|
||||
__be32 data = cpu_to_be32(0x00000003);
|
||||
|
||||
snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
|
||||
&data, sizeof(data), 0);
|
||||
}
|
||||
|
||||
static int begin_session(struct snd_dg00x *dg00x)
|
||||
{
|
||||
__be32 data;
|
||||
u32 curr;
|
||||
int err;
|
||||
|
||||
err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
|
||||
&data, sizeof(data), 0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
curr = be32_to_cpu(data);
|
||||
|
||||
if (curr == 0)
|
||||
curr = 2;
|
||||
|
||||
curr--;
|
||||
while (curr > 0) {
|
||||
data = cpu_to_be32(curr);
|
||||
err = snd_fw_transaction(dg00x->unit,
|
||||
TCODE_WRITE_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE +
|
||||
DG00X_OFFSET_STREAMING_SET,
|
||||
&data, sizeof(data), 0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
msleep(20);
|
||||
curr--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
finish_session(dg00x);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void release_resources(struct snd_dg00x *dg00x)
|
||||
{
|
||||
__be32 data = 0;
|
||||
|
||||
/* Unregister isochronous channels for both direction. */
|
||||
snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
|
||||
&data, sizeof(data), 0);
|
||||
|
||||
/* Release isochronous resources. */
|
||||
fw_iso_resources_free(&dg00x->tx_resources);
|
||||
fw_iso_resources_free(&dg00x->rx_resources);
|
||||
}
|
||||
|
||||
static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
|
||||
{
|
||||
unsigned int i;
|
||||
__be32 data;
|
||||
int err;
|
||||
|
||||
/* Check sampling rate. */
|
||||
for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
|
||||
if (snd_dg00x_stream_rates[i] == rate)
|
||||
break;
|
||||
}
|
||||
if (i == SND_DG00X_RATE_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
/* Keep resources for out-stream. */
|
||||
err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate,
|
||||
snd_dg00x_stream_pcm_channels[i]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = fw_iso_resources_allocate(&dg00x->rx_resources,
|
||||
amdtp_stream_get_max_payload(&dg00x->rx_stream),
|
||||
fw_parent_device(dg00x->unit)->max_speed);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Keep resources for in-stream. */
|
||||
err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate,
|
||||
snd_dg00x_stream_pcm_channels[i]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = fw_iso_resources_allocate(&dg00x->tx_resources,
|
||||
amdtp_stream_get_max_payload(&dg00x->tx_stream),
|
||||
fw_parent_device(dg00x->unit)->max_speed);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
/* Register isochronous channels for both direction. */
|
||||
data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
|
||||
dg00x->rx_resources.channel);
|
||||
err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
|
||||
&data, sizeof(data), 0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
release_resources(dg00x);
|
||||
return err;
|
||||
}
|
||||
|
||||
int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* For out-stream. */
|
||||
err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
/* For in-stream. */
|
||||
err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
snd_dg00x_stream_destroy_duplex(dg00x);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function should be called before starting streams or after stopping
|
||||
* streams.
|
||||
*/
|
||||
void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
|
||||
{
|
||||
amdtp_stream_destroy(&dg00x->rx_stream);
|
||||
fw_iso_resources_destroy(&dg00x->rx_resources);
|
||||
|
||||
amdtp_stream_destroy(&dg00x->tx_stream);
|
||||
fw_iso_resources_destroy(&dg00x->tx_resources);
|
||||
}
|
||||
|
||||
int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
|
||||
{
|
||||
unsigned int curr_rate;
|
||||
int err = 0;
|
||||
|
||||
if (dg00x->substreams_counter == 0)
|
||||
goto end;
|
||||
|
||||
/* Check current sampling rate. */
|
||||
err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
if (curr_rate != rate ||
|
||||
amdtp_streaming_error(&dg00x->tx_stream) ||
|
||||
amdtp_streaming_error(&dg00x->rx_stream)) {
|
||||
finish_session(dg00x);
|
||||
|
||||
amdtp_stream_stop(&dg00x->tx_stream);
|
||||
amdtp_stream_stop(&dg00x->rx_stream);
|
||||
release_resources(dg00x);
|
||||
}
|
||||
|
||||
/*
|
||||
* No packets are transmitted without receiving packets, reagardless of
|
||||
* which source of clock is used.
|
||||
*/
|
||||
if (!amdtp_stream_running(&dg00x->rx_stream)) {
|
||||
err = snd_dg00x_stream_set_local_rate(dg00x, rate);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = keep_resources(dg00x, rate);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = begin_session(dg00x);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_stream_start(&dg00x->rx_stream,
|
||||
dg00x->rx_resources.channel,
|
||||
fw_parent_device(dg00x->unit)->max_speed);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The value of SYT field in transmitted packets is always 0x0000. Thus,
|
||||
* duplex streams with timestamp synchronization cannot be built.
|
||||
*/
|
||||
if (!amdtp_stream_running(&dg00x->tx_stream)) {
|
||||
err = amdtp_stream_start(&dg00x->tx_stream,
|
||||
dg00x->tx_resources.channel,
|
||||
fw_parent_device(dg00x->unit)->max_speed);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&dg00x->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
end:
|
||||
return err;
|
||||
error:
|
||||
finish_session(dg00x);
|
||||
|
||||
amdtp_stream_stop(&dg00x->tx_stream);
|
||||
amdtp_stream_stop(&dg00x->rx_stream);
|
||||
release_resources(dg00x);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
|
||||
{
|
||||
if (dg00x->substreams_counter > 0)
|
||||
return;
|
||||
|
||||
amdtp_stream_stop(&dg00x->tx_stream);
|
||||
amdtp_stream_stop(&dg00x->rx_stream);
|
||||
finish_session(dg00x);
|
||||
release_resources(dg00x);
|
||||
|
||||
/*
|
||||
* Just after finishing the session, the device may lost transmitting
|
||||
* functionality for a short time.
|
||||
*/
|
||||
msleep(50);
|
||||
}
|
||||
|
||||
void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
|
||||
{
|
||||
fw_iso_resources_update(&dg00x->tx_resources);
|
||||
fw_iso_resources_update(&dg00x->rx_resources);
|
||||
|
||||
amdtp_stream_update(&dg00x->tx_stream);
|
||||
amdtp_stream_update(&dg00x->rx_stream);
|
||||
}
|
||||
|
||||
void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
|
||||
{
|
||||
dg00x->dev_lock_changed = true;
|
||||
wake_up(&dg00x->hwdep_wait);
|
||||
}
|
||||
|
||||
int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
|
||||
{
|
||||
int err;
|
||||
|
||||
spin_lock_irq(&dg00x->lock);
|
||||
|
||||
/* user land lock this */
|
||||
if (dg00x->dev_lock_count < 0) {
|
||||
err = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* this is the first time */
|
||||
if (dg00x->dev_lock_count++ == 0)
|
||||
snd_dg00x_stream_lock_changed(dg00x);
|
||||
err = 0;
|
||||
end:
|
||||
spin_unlock_irq(&dg00x->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
|
||||
{
|
||||
spin_lock_irq(&dg00x->lock);
|
||||
|
||||
if (WARN_ON(dg00x->dev_lock_count <= 0))
|
||||
goto end;
|
||||
if (--dg00x->dev_lock_count == 0)
|
||||
snd_dg00x_stream_lock_changed(dg00x);
|
||||
end:
|
||||
spin_unlock_irq(&dg00x->lock);
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* digi00x-transaction.c - a part of driver for Digidesign Digi 002/003 family
|
||||
*
|
||||
* Copyright (c) 2014-2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include <sound/asound.h>
|
||||
#include "digi00x.h"
|
||||
|
||||
static int fill_midi_message(struct snd_rawmidi_substream *substream, u8 *buf)
|
||||
{
|
||||
int bytes;
|
||||
|
||||
buf[0] = 0x80;
|
||||
bytes = snd_rawmidi_transmit_peek(substream, buf + 1, 2);
|
||||
if (bytes >= 0)
|
||||
buf[3] = 0xc0 | bytes;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static void handle_midi_control(struct snd_dg00x *dg00x, __be32 *buf,
|
||||
unsigned int length)
|
||||
{
|
||||
struct snd_rawmidi_substream *substream;
|
||||
unsigned int i;
|
||||
unsigned int len;
|
||||
u8 *b;
|
||||
|
||||
substream = ACCESS_ONCE(dg00x->in_control);
|
||||
if (substream == NULL)
|
||||
return;
|
||||
|
||||
length /= 4;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
b = (u8 *)&buf[i];
|
||||
len = b[3] & 0xf;
|
||||
if (len > 0)
|
||||
snd_rawmidi_receive(dg00x->in_control, b + 1, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_unknown_message(struct snd_dg00x *dg00x,
|
||||
unsigned long long offset, __be32 *buf)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dg00x->lock, flags);
|
||||
dg00x->msg = be32_to_cpu(*buf);
|
||||
spin_unlock_irqrestore(&dg00x->lock, flags);
|
||||
|
||||
wake_up(&dg00x->hwdep_wait);
|
||||
}
|
||||
|
||||
static void handle_message(struct fw_card *card, struct fw_request *request,
|
||||
int tcode, int destination, int source,
|
||||
int generation, unsigned long long offset,
|
||||
void *data, size_t length, void *callback_data)
|
||||
{
|
||||
struct snd_dg00x *dg00x = callback_data;
|
||||
__be32 *buf = (__be32 *)data;
|
||||
|
||||
if (offset == dg00x->async_handler.offset)
|
||||
handle_unknown_message(dg00x, offset, buf);
|
||||
else if (offset == dg00x->async_handler.offset + 4)
|
||||
handle_midi_control(dg00x, buf, length);
|
||||
|
||||
fw_send_response(card, request, RCODE_COMPLETE);
|
||||
}
|
||||
|
||||
int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x)
|
||||
{
|
||||
struct fw_device *device = fw_parent_device(dg00x->unit);
|
||||
__be32 data[2];
|
||||
int err;
|
||||
|
||||
/* Unknown. 4bytes. */
|
||||
data[0] = cpu_to_be32((device->card->node_id << 16) |
|
||||
(dg00x->async_handler.offset >> 32));
|
||||
data[1] = cpu_to_be32(dg00x->async_handler.offset);
|
||||
err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR,
|
||||
&data, sizeof(data), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Asynchronous transactions for MIDI control message. */
|
||||
data[0] = cpu_to_be32((device->card->node_id << 16) |
|
||||
(dg00x->async_handler.offset >> 32));
|
||||
data[1] = cpu_to_be32(dg00x->async_handler.offset + 4);
|
||||
return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_MIDI_CTL_ADDR,
|
||||
&data, sizeof(data), 0);
|
||||
}
|
||||
|
||||
int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
|
||||
{
|
||||
static const struct fw_address_region resp_register_region = {
|
||||
.start = 0xffffe0000000ull,
|
||||
.end = 0xffffe000ffffull,
|
||||
};
|
||||
int err;
|
||||
|
||||
dg00x->async_handler.length = 12;
|
||||
dg00x->async_handler.address_callback = handle_message;
|
||||
dg00x->async_handler.callback_data = dg00x;
|
||||
|
||||
err = fw_core_add_address_handler(&dg00x->async_handler,
|
||||
&resp_register_region);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = snd_dg00x_transaction_reregister(dg00x);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_fw_async_midi_port_init(&dg00x->out_control, dg00x->unit,
|
||||
DG00X_ADDR_BASE + DG00X_OFFSET_MMC,
|
||||
4, fill_midi_message);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
return err;
|
||||
error:
|
||||
fw_core_remove_address_handler(&dg00x->async_handler);
|
||||
dg00x->async_handler.address_callback = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
|
||||
{
|
||||
snd_fw_async_midi_port_destroy(&dg00x->out_control);
|
||||
fw_core_remove_address_handler(&dg00x->async_handler);
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* digi00x.c - a part of driver for Digidesign Digi 002/003 family
|
||||
*
|
||||
* Copyright (c) 2014-2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include "digi00x.h"
|
||||
|
||||
MODULE_DESCRIPTION("Digidesign Digi 002/003 family Driver");
|
||||
MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
#define VENDOR_DIGIDESIGN 0x00a07e
|
||||
#define MODEL_DIGI00X 0x000002
|
||||
|
||||
static int name_card(struct snd_dg00x *dg00x)
|
||||
{
|
||||
struct fw_device *fw_dev = fw_parent_device(dg00x->unit);
|
||||
char name[32] = {0};
|
||||
char *model;
|
||||
int err;
|
||||
|
||||
err = fw_csr_string(dg00x->unit->directory, CSR_MODEL, name,
|
||||
sizeof(name));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
model = skip_spaces(name);
|
||||
|
||||
strcpy(dg00x->card->driver, "Digi00x");
|
||||
strcpy(dg00x->card->shortname, model);
|
||||
strcpy(dg00x->card->mixername, model);
|
||||
snprintf(dg00x->card->longname, sizeof(dg00x->card->longname),
|
||||
"Digidesign %s, GUID %08x%08x at %s, S%d", model,
|
||||
fw_dev->config_rom[3], fw_dev->config_rom[4],
|
||||
dev_name(&dg00x->unit->device), 100 << fw_dev->max_speed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dg00x_card_free(struct snd_card *card)
|
||||
{
|
||||
struct snd_dg00x *dg00x = card->private_data;
|
||||
|
||||
snd_dg00x_stream_destroy_duplex(dg00x);
|
||||
snd_dg00x_transaction_unregister(dg00x);
|
||||
|
||||
fw_unit_put(dg00x->unit);
|
||||
|
||||
mutex_destroy(&dg00x->mutex);
|
||||
}
|
||||
|
||||
static int snd_dg00x_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct snd_dg00x *dg00x;
|
||||
int err;
|
||||
|
||||
/* create card */
|
||||
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
|
||||
sizeof(struct snd_dg00x), &card);
|
||||
if (err < 0)
|
||||
return err;
|
||||
card->private_free = dg00x_card_free;
|
||||
|
||||
/* initialize myself */
|
||||
dg00x = card->private_data;
|
||||
dg00x->card = card;
|
||||
dg00x->unit = fw_unit_get(unit);
|
||||
|
||||
mutex_init(&dg00x->mutex);
|
||||
spin_lock_init(&dg00x->lock);
|
||||
init_waitqueue_head(&dg00x->hwdep_wait);
|
||||
|
||||
err = name_card(dg00x);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_dg00x_stream_init_duplex(dg00x);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
snd_dg00x_proc_init(dg00x);
|
||||
|
||||
err = snd_dg00x_create_pcm_devices(dg00x);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_dg00x_create_midi_devices(dg00x);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_dg00x_create_hwdep_device(dg00x);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_dg00x_transaction_register(dg00x);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
dev_set_drvdata(&unit->device, dg00x);
|
||||
|
||||
return err;
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void snd_dg00x_update(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
|
||||
|
||||
snd_dg00x_transaction_reregister(dg00x);
|
||||
|
||||
mutex_lock(&dg00x->mutex);
|
||||
snd_dg00x_stream_update_duplex(dg00x);
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
}
|
||||
|
||||
static void snd_dg00x_remove(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
|
||||
|
||||
/* No need to wait for releasing card object in this context. */
|
||||
snd_card_free_when_closed(dg00x->card);
|
||||
}
|
||||
|
||||
static const struct ieee1394_device_id snd_dg00x_id_table[] = {
|
||||
/* Both of 002/003 use the same ID. */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = VENDOR_DIGIDESIGN,
|
||||
.model_id = MODEL_DIGI00X,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ieee1394, snd_dg00x_id_table);
|
||||
|
||||
static struct fw_driver dg00x_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "snd-firewire-digi00x",
|
||||
.bus = &fw_bus_type,
|
||||
},
|
||||
.probe = snd_dg00x_probe,
|
||||
.update = snd_dg00x_update,
|
||||
.remove = snd_dg00x_remove,
|
||||
.id_table = snd_dg00x_id_table,
|
||||
};
|
||||
|
||||
static int __init snd_dg00x_init(void)
|
||||
{
|
||||
return driver_register(&dg00x_driver.driver);
|
||||
}
|
||||
|
||||
static void __exit snd_dg00x_exit(void)
|
||||
{
|
||||
driver_unregister(&dg00x_driver.driver);
|
||||
}
|
||||
|
||||
module_init(snd_dg00x_init);
|
||||
module_exit(snd_dg00x_exit);
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* digi00x.h - a part of driver for Digidesign Digi 002/003 family
|
||||
*
|
||||
* Copyright (c) 2014-2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#ifndef SOUND_DIGI00X_H_INCLUDED
|
||||
#define SOUND_DIGI00X_H_INCLUDED
|
||||
|
||||
#include <linux/compat.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/firewire.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/firewire.h>
|
||||
#include <sound/hwdep.h>
|
||||
#include <sound/rawmidi.h>
|
||||
|
||||
#include "../lib.h"
|
||||
#include "../iso-resources.h"
|
||||
#include "../amdtp-stream.h"
|
||||
|
||||
struct snd_dg00x {
|
||||
struct snd_card *card;
|
||||
struct fw_unit *unit;
|
||||
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
struct amdtp_stream tx_stream;
|
||||
struct fw_iso_resources tx_resources;
|
||||
|
||||
struct amdtp_stream rx_stream;
|
||||
struct fw_iso_resources rx_resources;
|
||||
|
||||
unsigned int substreams_counter;
|
||||
|
||||
/* for uapi */
|
||||
int dev_lock_count;
|
||||
bool dev_lock_changed;
|
||||
wait_queue_head_t hwdep_wait;
|
||||
|
||||
/* For asynchronous messages. */
|
||||
struct fw_address_handler async_handler;
|
||||
u32 msg;
|
||||
|
||||
/* For asynchronous MIDI controls. */
|
||||
struct snd_rawmidi_substream *in_control;
|
||||
struct snd_fw_async_midi_port out_control;
|
||||
};
|
||||
|
||||
#define DG00X_ADDR_BASE 0xffffe0000000ull
|
||||
|
||||
#define DG00X_OFFSET_STREAMING_STATE 0x0000
|
||||
#define DG00X_OFFSET_STREAMING_SET 0x0004
|
||||
#define DG00X_OFFSET_MIDI_CTL_ADDR 0x0008
|
||||
/* For LSB of the address 0x000c */
|
||||
/* unknown 0x0010 */
|
||||
#define DG00X_OFFSET_MESSAGE_ADDR 0x0014
|
||||
/* For LSB of the address 0x0018 */
|
||||
/* unknown 0x001c */
|
||||
/* unknown 0x0020 */
|
||||
/* not used 0x0024--0x00ff */
|
||||
#define DG00X_OFFSET_ISOC_CHANNELS 0x0100
|
||||
/* unknown 0x0104 */
|
||||
/* unknown 0x0108 */
|
||||
/* unknown 0x010c */
|
||||
#define DG00X_OFFSET_LOCAL_RATE 0x0110
|
||||
#define DG00X_OFFSET_EXTERNAL_RATE 0x0114
|
||||
#define DG00X_OFFSET_CLOCK_SOURCE 0x0118
|
||||
#define DG00X_OFFSET_OPT_IFACE_MODE 0x011c
|
||||
/* unknown 0x0120 */
|
||||
/* Mixer control on/off 0x0124 */
|
||||
/* unknown 0x0128 */
|
||||
#define DG00X_OFFSET_DETECT_EXTERNAL 0x012c
|
||||
/* unknown 0x0138 */
|
||||
#define DG00X_OFFSET_MMC 0x0400
|
||||
|
||||
enum snd_dg00x_rate {
|
||||
SND_DG00X_RATE_44100 = 0,
|
||||
SND_DG00X_RATE_48000,
|
||||
SND_DG00X_RATE_88200,
|
||||
SND_DG00X_RATE_96000,
|
||||
SND_DG00X_RATE_COUNT,
|
||||
};
|
||||
|
||||
enum snd_dg00x_clock {
|
||||
SND_DG00X_CLOCK_INTERNAL = 0,
|
||||
SND_DG00X_CLOCK_SPDIF,
|
||||
SND_DG00X_CLOCK_ADAT,
|
||||
SND_DG00X_CLOCK_WORD,
|
||||
SND_DG00X_CLOCK_COUNT,
|
||||
};
|
||||
|
||||
enum snd_dg00x_optical_mode {
|
||||
SND_DG00X_OPT_IFACE_MODE_ADAT = 0,
|
||||
SND_DG00X_OPT_IFACE_MODE_SPDIF,
|
||||
SND_DG00X_OPT_IFACE_MODE_COUNT,
|
||||
};
|
||||
|
||||
#define DOT_MIDI_IN_PORTS 1
|
||||
#define DOT_MIDI_OUT_PORTS 2
|
||||
|
||||
int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir);
|
||||
int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
||||
unsigned int pcm_channels);
|
||||
void amdtp_dot_reset(struct amdtp_stream *s);
|
||||
int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
|
||||
struct snd_pcm_runtime *runtime);
|
||||
void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format);
|
||||
void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
|
||||
struct snd_rawmidi_substream *midi);
|
||||
|
||||
int snd_dg00x_transaction_register(struct snd_dg00x *dg00x);
|
||||
int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x);
|
||||
void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x);
|
||||
|
||||
extern const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT];
|
||||
extern const unsigned int snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT];
|
||||
int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
|
||||
unsigned int *rate);
|
||||
int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x,
|
||||
unsigned int *rate);
|
||||
int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate);
|
||||
int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
|
||||
enum snd_dg00x_clock *clock);
|
||||
int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x,
|
||||
bool *detect);
|
||||
int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x);
|
||||
int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate);
|
||||
void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x);
|
||||
void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x);
|
||||
void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x);
|
||||
|
||||
void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x);
|
||||
int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x);
|
||||
void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x);
|
||||
|
||||
void snd_dg00x_proc_init(struct snd_dg00x *dg00x);
|
||||
|
||||
int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x);
|
||||
|
||||
int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x);
|
||||
|
||||
int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x);
|
||||
#endif
|
||||
@@ -17,7 +17,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include "fcp.h"
|
||||
#include "lib.h"
|
||||
#include "amdtp.h"
|
||||
#include "amdtp-stream.h"
|
||||
|
||||
#define CTS_AVC 0x00
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
|
||||
fireworks_stream.o fireworks_proc.o fireworks_midi.o \
|
||||
fireworks_pcm.o fireworks_hwdep.o fireworks.o
|
||||
obj-m += snd-fireworks.o
|
||||
obj-$(CONFIG_SND_FIREWORKS) += snd-fireworks.o
|
||||
|
||||
@@ -138,12 +138,12 @@ get_hardware_info(struct snd_efw *efw)
|
||||
efw->midi_out_ports = hwinfo->midi_out_ports;
|
||||
efw->midi_in_ports = hwinfo->midi_in_ports;
|
||||
|
||||
if (hwinfo->amdtp_tx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM ||
|
||||
hwinfo->amdtp_tx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
|
||||
hwinfo->amdtp_tx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM ||
|
||||
hwinfo->amdtp_rx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM ||
|
||||
hwinfo->amdtp_rx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
|
||||
hwinfo->amdtp_rx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM) {
|
||||
if (hwinfo->amdtp_tx_pcm_channels > AM824_MAX_CHANNELS_FOR_PCM ||
|
||||
hwinfo->amdtp_tx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM ||
|
||||
hwinfo->amdtp_tx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM ||
|
||||
hwinfo->amdtp_rx_pcm_channels > AM824_MAX_CHANNELS_FOR_PCM ||
|
||||
hwinfo->amdtp_rx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM ||
|
||||
hwinfo->amdtp_rx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM) {
|
||||
err = -ENOSYS;
|
||||
goto end;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
#include "../packets-buffer.h"
|
||||
#include "../iso-resources.h"
|
||||
#include "../amdtp.h"
|
||||
#include "../amdtp-am824.h"
|
||||
#include "../cmp.h"
|
||||
#include "../lib.h"
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ int snd_efw_command_get_phys_meters(struct snd_efw *efw,
|
||||
struct snd_efw_phys_meters *meters,
|
||||
unsigned int len)
|
||||
{
|
||||
__be32 *buf = (__be32 *)meters;
|
||||
u32 *buf = (u32 *)meters;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
|
||||
@@ -73,10 +73,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
|
||||
spin_lock_irqsave(&efw->lock, flags);
|
||||
|
||||
if (up)
|
||||
amdtp_stream_midi_trigger(&efw->tx_stream,
|
||||
amdtp_am824_midi_trigger(&efw->tx_stream,
|
||||
substrm->number, substrm);
|
||||
else
|
||||
amdtp_stream_midi_trigger(&efw->tx_stream,
|
||||
amdtp_am824_midi_trigger(&efw->tx_stream,
|
||||
substrm->number, NULL);
|
||||
|
||||
spin_unlock_irqrestore(&efw->lock, flags);
|
||||
@@ -90,11 +90,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
|
||||
spin_lock_irqsave(&efw->lock, flags);
|
||||
|
||||
if (up)
|
||||
amdtp_stream_midi_trigger(&efw->rx_stream,
|
||||
substrm->number, substrm);
|
||||
amdtp_am824_midi_trigger(&efw->rx_stream,
|
||||
substrm->number, substrm);
|
||||
else
|
||||
amdtp_stream_midi_trigger(&efw->rx_stream,
|
||||
substrm->number, NULL);
|
||||
amdtp_am824_midi_trigger(&efw->rx_stream,
|
||||
substrm->number, NULL);
|
||||
|
||||
spin_unlock_irqrestore(&efw->lock, flags);
|
||||
}
|
||||
|
||||
@@ -159,11 +159,11 @@ pcm_init_hw_params(struct snd_efw *efw,
|
||||
SNDRV_PCM_INFO_MMAP_VALID;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
|
||||
runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
|
||||
s = &efw->tx_stream;
|
||||
pcm_channels = efw->pcm_capture_channels;
|
||||
} else {
|
||||
runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
|
||||
runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
|
||||
s = &efw->rx_stream;
|
||||
pcm_channels = efw->pcm_playback_channels;
|
||||
}
|
||||
@@ -187,7 +187,7 @@ pcm_init_hw_params(struct snd_efw *efw,
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
|
||||
err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
@@ -253,7 +253,8 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
|
||||
atomic_inc(&efw->capture_substreams);
|
||||
amdtp_stream_set_pcm_format(&efw->tx_stream, params_format(hw_params));
|
||||
|
||||
amdtp_am824_set_pcm_format(&efw->tx_stream, params_format(hw_params));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -270,7 +271,8 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
|
||||
atomic_inc(&efw->playback_substreams);
|
||||
amdtp_stream_set_pcm_format(&efw->rx_stream, params_format(hw_params));
|
||||
|
||||
amdtp_am824_set_pcm_format(&efw->rx_stream, params_format(hw_params));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = amdtp_stream_init(stream, efw->unit, s_dir, CIP_BLOCKING);
|
||||
err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING);
|
||||
if (err < 0) {
|
||||
amdtp_stream_destroy(stream);
|
||||
cmp_connection_destroy(conn);
|
||||
@@ -73,8 +73,10 @@ start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
|
||||
midi_ports = efw->midi_in_ports;
|
||||
}
|
||||
|
||||
amdtp_stream_set_parameters(stream, sampling_rate,
|
||||
pcm_channels, midi_ports);
|
||||
err = amdtp_am824_set_parameters(stream, sampling_rate,
|
||||
pcm_channels, midi_ports, false);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
/* establish connection via CMP */
|
||||
err = cmp_connection_establish(conn,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/firewire.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include "lib.h"
|
||||
|
||||
#define ERROR_RETRY_DELAY_MS 20
|
||||
@@ -66,6 +67,147 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
|
||||
}
|
||||
EXPORT_SYMBOL(snd_fw_transaction);
|
||||
|
||||
static void async_midi_port_callback(struct fw_card *card, int rcode,
|
||||
void *data, size_t length,
|
||||
void *callback_data)
|
||||
{
|
||||
struct snd_fw_async_midi_port *port = callback_data;
|
||||
struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
|
||||
|
||||
/* This port is closed. */
|
||||
if (substream == NULL)
|
||||
return;
|
||||
|
||||
if (rcode == RCODE_COMPLETE)
|
||||
snd_rawmidi_transmit_ack(substream, port->consume_bytes);
|
||||
else if (!rcode_is_permanent_error(rcode))
|
||||
/* To start next transaction immediately for recovery. */
|
||||
port->next_ktime = ktime_set(0, 0);
|
||||
else
|
||||
/* Don't continue processing. */
|
||||
port->error = true;
|
||||
|
||||
port->idling = true;
|
||||
|
||||
if (!snd_rawmidi_transmit_empty(substream))
|
||||
schedule_work(&port->work);
|
||||
}
|
||||
|
||||
static void midi_port_work(struct work_struct *work)
|
||||
{
|
||||
struct snd_fw_async_midi_port *port =
|
||||
container_of(work, struct snd_fw_async_midi_port, work);
|
||||
struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
|
||||
int generation;
|
||||
int type;
|
||||
|
||||
/* Under transacting or error state. */
|
||||
if (!port->idling || port->error)
|
||||
return;
|
||||
|
||||
/* Nothing to do. */
|
||||
if (substream == NULL || snd_rawmidi_transmit_empty(substream))
|
||||
return;
|
||||
|
||||
/* Do it in next chance. */
|
||||
if (ktime_after(port->next_ktime, ktime_get())) {
|
||||
schedule_work(&port->work);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
|
||||
* Later, snd_rawmidi_transmit_ack() is called.
|
||||
*/
|
||||
memset(port->buf, 0, port->len);
|
||||
port->consume_bytes = port->fill(substream, port->buf);
|
||||
if (port->consume_bytes <= 0) {
|
||||
/* Do it in next chance, immediately. */
|
||||
if (port->consume_bytes == 0) {
|
||||
port->next_ktime = ktime_set(0, 0);
|
||||
schedule_work(&port->work);
|
||||
} else {
|
||||
/* Fatal error. */
|
||||
port->error = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Calculate type of transaction. */
|
||||
if (port->len == 4)
|
||||
type = TCODE_WRITE_QUADLET_REQUEST;
|
||||
else
|
||||
type = TCODE_WRITE_BLOCK_REQUEST;
|
||||
|
||||
/* Set interval to next transaction. */
|
||||
port->next_ktime = ktime_add_ns(ktime_get(),
|
||||
port->consume_bytes * 8 * NSEC_PER_SEC / 31250);
|
||||
|
||||
/* Start this transaction. */
|
||||
port->idling = false;
|
||||
|
||||
/*
|
||||
* In Linux FireWire core, when generation is updated with memory
|
||||
* barrier, node id has already been updated. In this module, After
|
||||
* this smp_rmb(), load/store instructions to memory are completed.
|
||||
* Thus, both of generation and node id are available with recent
|
||||
* values. This is a light-serialization solution to handle bus reset
|
||||
* events on IEEE 1394 bus.
|
||||
*/
|
||||
generation = port->parent->generation;
|
||||
smp_rmb();
|
||||
|
||||
fw_send_request(port->parent->card, &port->transaction, type,
|
||||
port->parent->node_id, generation,
|
||||
port->parent->max_speed, port->addr,
|
||||
port->buf, port->len, async_midi_port_callback,
|
||||
port);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_fw_async_midi_port_init - initialize asynchronous MIDI port structure
|
||||
* @port: the asynchronous MIDI port to initialize
|
||||
* @unit: the target of the asynchronous transaction
|
||||
* @addr: the address to which transactions are transferred
|
||||
* @len: the length of transaction
|
||||
* @fill: the callback function to fill given buffer, and returns the
|
||||
* number of consumed bytes for MIDI message.
|
||||
*
|
||||
*/
|
||||
int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
|
||||
struct fw_unit *unit, u64 addr, unsigned int len,
|
||||
snd_fw_async_midi_port_fill fill)
|
||||
{
|
||||
port->len = DIV_ROUND_UP(len, 4) * 4;
|
||||
port->buf = kzalloc(port->len, GFP_KERNEL);
|
||||
if (port->buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
port->parent = fw_parent_device(unit);
|
||||
port->addr = addr;
|
||||
port->fill = fill;
|
||||
port->idling = true;
|
||||
port->next_ktime = ktime_set(0, 0);
|
||||
port->error = false;
|
||||
|
||||
INIT_WORK(&port->work, midi_port_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_fw_async_midi_port_init);
|
||||
|
||||
/**
|
||||
* snd_fw_async_midi_port_destroy - free asynchronous MIDI port structure
|
||||
* @port: the asynchronous MIDI port structure
|
||||
*/
|
||||
void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port)
|
||||
{
|
||||
snd_fw_async_midi_port_finish(port);
|
||||
cancel_work_sync(&port->work);
|
||||
kfree(port->buf);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_fw_async_midi_port_destroy);
|
||||
|
||||
MODULE_DESCRIPTION("FireWire audio helper functions");
|
||||
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include <linux/firewire-constants.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <sound/rawmidi.h>
|
||||
|
||||
struct fw_unit;
|
||||
|
||||
@@ -20,4 +22,58 @@ static inline bool rcode_is_permanent_error(int rcode)
|
||||
return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR;
|
||||
}
|
||||
|
||||
struct snd_fw_async_midi_port;
|
||||
typedef int (*snd_fw_async_midi_port_fill)(
|
||||
struct snd_rawmidi_substream *substream,
|
||||
u8 *buf);
|
||||
|
||||
struct snd_fw_async_midi_port {
|
||||
struct fw_device *parent;
|
||||
struct work_struct work;
|
||||
bool idling;
|
||||
ktime_t next_ktime;
|
||||
bool error;
|
||||
|
||||
u64 addr;
|
||||
struct fw_transaction transaction;
|
||||
|
||||
u8 *buf;
|
||||
unsigned int len;
|
||||
|
||||
struct snd_rawmidi_substream *substream;
|
||||
snd_fw_async_midi_port_fill fill;
|
||||
unsigned int consume_bytes;
|
||||
};
|
||||
|
||||
int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
|
||||
struct fw_unit *unit, u64 addr, unsigned int len,
|
||||
snd_fw_async_midi_port_fill fill);
|
||||
void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port);
|
||||
|
||||
/**
|
||||
* snd_fw_async_midi_port_run - run transactions for the async MIDI port
|
||||
* @port: the asynchronous MIDI port
|
||||
* @substream: the MIDI substream
|
||||
*/
|
||||
static inline void
|
||||
snd_fw_async_midi_port_run(struct snd_fw_async_midi_port *port,
|
||||
struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
if (!port->error) {
|
||||
port->substream = substream;
|
||||
schedule_work(&port->work);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_fw_async_midi_port_finish - finish the asynchronous MIDI port
|
||||
* @port: the asynchronous MIDI port
|
||||
*/
|
||||
static inline void
|
||||
snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port)
|
||||
{
|
||||
port->substream = NULL;
|
||||
port->error = false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o \
|
||||
oxfw-proc.o oxfw-midi.o oxfw-hwdep.o oxfw.o
|
||||
obj-m += snd-oxfw.o
|
||||
obj-$(CONFIG_SND_OXFW) += snd-oxfw.o
|
||||
|
||||
@@ -90,11 +90,11 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
|
||||
spin_lock_irqsave(&oxfw->lock, flags);
|
||||
|
||||
if (up)
|
||||
amdtp_stream_midi_trigger(&oxfw->tx_stream,
|
||||
substrm->number, substrm);
|
||||
amdtp_am824_midi_trigger(&oxfw->tx_stream,
|
||||
substrm->number, substrm);
|
||||
else
|
||||
amdtp_stream_midi_trigger(&oxfw->tx_stream,
|
||||
substrm->number, NULL);
|
||||
amdtp_am824_midi_trigger(&oxfw->tx_stream,
|
||||
substrm->number, NULL);
|
||||
|
||||
spin_unlock_irqrestore(&oxfw->lock, flags);
|
||||
}
|
||||
@@ -107,11 +107,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
|
||||
spin_lock_irqsave(&oxfw->lock, flags);
|
||||
|
||||
if (up)
|
||||
amdtp_stream_midi_trigger(&oxfw->rx_stream,
|
||||
substrm->number, substrm);
|
||||
amdtp_am824_midi_trigger(&oxfw->rx_stream,
|
||||
substrm->number, substrm);
|
||||
else
|
||||
amdtp_stream_midi_trigger(&oxfw->rx_stream,
|
||||
substrm->number, NULL);
|
||||
amdtp_am824_midi_trigger(&oxfw->rx_stream,
|
||||
substrm->number, NULL);
|
||||
|
||||
spin_unlock_irqrestore(&oxfw->lock, flags);
|
||||
}
|
||||
@@ -142,29 +142,11 @@ static void set_midi_substream_names(struct snd_oxfw *oxfw,
|
||||
|
||||
int snd_oxfw_create_midi(struct snd_oxfw *oxfw)
|
||||
{
|
||||
struct snd_oxfw_stream_formation formation;
|
||||
struct snd_rawmidi *rmidi;
|
||||
struct snd_rawmidi_str *str;
|
||||
u8 *format;
|
||||
int i, err;
|
||||
int err;
|
||||
|
||||
/* If its stream has MIDI conformant data channel, add one MIDI port */
|
||||
for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
|
||||
format = oxfw->tx_stream_formats[i];
|
||||
if (format != NULL) {
|
||||
err = snd_oxfw_stream_parse_format(format, &formation);
|
||||
if (err >= 0 && formation.midi > 0)
|
||||
oxfw->midi_input_ports = 1;
|
||||
}
|
||||
|
||||
format = oxfw->rx_stream_formats[i];
|
||||
if (format != NULL) {
|
||||
err = snd_oxfw_stream_parse_format(format, &formation);
|
||||
if (err >= 0 && formation.midi > 0)
|
||||
oxfw->midi_output_ports = 1;
|
||||
}
|
||||
}
|
||||
if ((oxfw->midi_input_ports == 0) && (oxfw->midi_output_ports == 0))
|
||||
if (oxfw->midi_input_ports == 0 && oxfw->midi_output_ports == 0)
|
||||
return 0;
|
||||
|
||||
/* create midi ports */
|
||||
|
||||
@@ -134,11 +134,11 @@ static int init_hw_params(struct snd_oxfw *oxfw,
|
||||
SNDRV_PCM_INFO_MMAP_VALID;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
|
||||
runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
|
||||
stream = &oxfw->tx_stream;
|
||||
formats = oxfw->tx_stream_formats;
|
||||
} else {
|
||||
runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
|
||||
runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
|
||||
stream = &oxfw->rx_stream;
|
||||
formats = oxfw->rx_stream_formats;
|
||||
}
|
||||
@@ -158,7 +158,7 @@ static int init_hw_params(struct snd_oxfw *oxfw,
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
|
||||
err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
@@ -244,7 +244,7 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
}
|
||||
|
||||
amdtp_stream_set_pcm_format(&oxfw->tx_stream, params_format(hw_params));
|
||||
amdtp_am824_set_pcm_format(&oxfw->tx_stream, params_format(hw_params));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -265,7 +265,7 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
}
|
||||
|
||||
amdtp_stream_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
|
||||
amdtp_am824_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -148,14 +148,17 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream,
|
||||
}
|
||||
|
||||
pcm_channels = formation.pcm;
|
||||
midi_ports = DIV_ROUND_UP(formation.midi, 8);
|
||||
midi_ports = formation.midi * 8;
|
||||
|
||||
/* The stream should have one pcm channels at least */
|
||||
if (pcm_channels == 0) {
|
||||
err = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
amdtp_stream_set_parameters(stream, rate, pcm_channels, midi_ports);
|
||||
err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports,
|
||||
false);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = cmp_connection_establish(conn,
|
||||
amdtp_stream_get_max_payload(stream));
|
||||
@@ -225,7 +228,7 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = amdtp_stream_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
|
||||
err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
|
||||
if (err < 0) {
|
||||
amdtp_stream_destroy(stream);
|
||||
cmp_connection_destroy(conn);
|
||||
@@ -238,9 +241,12 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
|
||||
* packets. As a result, next isochronous packet includes more data
|
||||
* blocks than IEC 61883-6 defines.
|
||||
*/
|
||||
if (stream == &oxfw->tx_stream)
|
||||
if (stream == &oxfw->tx_stream) {
|
||||
oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK |
|
||||
CIP_JUMBO_PAYLOAD;
|
||||
if (oxfw->wrong_dbs)
|
||||
oxfw->tx_stream.flags |= CIP_WRONG_DBS;
|
||||
}
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
@@ -480,8 +486,8 @@ int snd_oxfw_stream_parse_format(u8 *format,
|
||||
}
|
||||
}
|
||||
|
||||
if (formation->pcm > AMDTP_MAX_CHANNELS_FOR_PCM ||
|
||||
formation->midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
|
||||
if (formation->pcm > AM824_MAX_CHANNELS_FOR_PCM ||
|
||||
formation->midi > AM824_MAX_CHANNELS_FOR_MIDI)
|
||||
return -ENOSYS;
|
||||
|
||||
return 0;
|
||||
@@ -623,6 +629,9 @@ end:
|
||||
int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
|
||||
{
|
||||
u8 plugs[AVC_PLUG_INFO_BUF_BYTES];
|
||||
struct snd_oxfw_stream_formation formation;
|
||||
u8 *format;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
/* the number of plugs for isoc in/out, ext in/out */
|
||||
@@ -642,12 +651,42 @@ int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
|
||||
err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
|
||||
format = oxfw->tx_stream_formats[i];
|
||||
if (format == NULL)
|
||||
continue;
|
||||
err = snd_oxfw_stream_parse_format(format, &formation);
|
||||
if (err < 0)
|
||||
continue;
|
||||
|
||||
/* Add one MIDI port. */
|
||||
if (formation.midi > 0)
|
||||
oxfw->midi_input_ports = 1;
|
||||
}
|
||||
|
||||
oxfw->has_output = true;
|
||||
}
|
||||
|
||||
/* use iPCR[0] if exists */
|
||||
if (plugs[0] > 0)
|
||||
if (plugs[0] > 0) {
|
||||
err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
|
||||
format = oxfw->rx_stream_formats[i];
|
||||
if (format == NULL)
|
||||
continue;
|
||||
err = snd_oxfw_stream_parse_format(format, &formation);
|
||||
if (err < 0)
|
||||
continue;
|
||||
|
||||
/* Add one MIDI port. */
|
||||
if (formation.midi > 0)
|
||||
oxfw->midi_output_ports = 1;
|
||||
}
|
||||
}
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
#define VENDOR_GRIFFIN 0x001292
|
||||
#define VENDOR_BEHRINGER 0x001564
|
||||
#define VENDOR_LACIE 0x00d04b
|
||||
#define VENDOR_TASCAM 0x00022e
|
||||
|
||||
#define MODEL_SATELLITE 0x00200f
|
||||
|
||||
#define SPECIFIER_1394TA 0x00a02d
|
||||
#define VERSION_AVC 0x010001
|
||||
@@ -129,6 +132,40 @@ static void oxfw_card_free(struct snd_card *card)
|
||||
mutex_destroy(&oxfw->mutex);
|
||||
}
|
||||
|
||||
static void detect_quirks(struct snd_oxfw *oxfw)
|
||||
{
|
||||
struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
|
||||
struct fw_csr_iterator it;
|
||||
int key, val;
|
||||
int vendor, model;
|
||||
|
||||
/* Seek from Root Directory of Config ROM. */
|
||||
vendor = model = 0;
|
||||
fw_csr_iterator_init(&it, fw_dev->config_rom + 5);
|
||||
while (fw_csr_iterator_next(&it, &key, &val)) {
|
||||
if (key == CSR_VENDOR)
|
||||
vendor = val;
|
||||
else if (key == CSR_MODEL)
|
||||
model = val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mackie Onyx Satellite with base station has a quirk to report a wrong
|
||||
* value in 'dbs' field of CIP header against its format information.
|
||||
*/
|
||||
if (vendor == VENDOR_LOUD && model == MODEL_SATELLITE)
|
||||
oxfw->wrong_dbs = true;
|
||||
|
||||
/*
|
||||
* TASCAM FireOne has physical control and requires a pair of additional
|
||||
* MIDI ports.
|
||||
*/
|
||||
if (vendor == VENDOR_TASCAM) {
|
||||
oxfw->midi_input_ports++;
|
||||
oxfw->midi_output_ports++;
|
||||
}
|
||||
}
|
||||
|
||||
static int oxfw_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *id)
|
||||
{
|
||||
@@ -157,6 +194,8 @@ static int oxfw_probe(struct fw_unit *unit,
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
detect_quirks(oxfw);
|
||||
|
||||
err = name_card(oxfw);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
@@ -294,6 +333,13 @@ static const struct ieee1394_device_id oxfw_id_table[] = {
|
||||
.specifier_id = SPECIFIER_1394TA,
|
||||
.version = VERSION_AVC,
|
||||
},
|
||||
/* TASCAM, FireOne */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = VENDOR_TASCAM,
|
||||
.model_id = 0x800007,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table);
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "../fcp.h"
|
||||
#include "../packets-buffer.h"
|
||||
#include "../iso-resources.h"
|
||||
#include "../amdtp.h"
|
||||
#include "../amdtp-am824.h"
|
||||
#include "../cmp.h"
|
||||
|
||||
struct device_info {
|
||||
@@ -49,6 +49,7 @@ struct snd_oxfw {
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
bool wrong_dbs;
|
||||
bool has_output;
|
||||
u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
|
||||
u8 *rx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \
|
||||
tascam-pcm.o tascam-hwdep.o tascam-transaction.o \
|
||||
tascam-midi.o tascam.o
|
||||
obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* amdtp-tascam.c - a part of driver for TASCAM FireWire series
|
||||
*
|
||||
* Copyright (c) 2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include <sound/pcm.h>
|
||||
#include "tascam.h"
|
||||
|
||||
#define AMDTP_FMT_TSCM_TX 0x1e
|
||||
#define AMDTP_FMT_TSCM_RX 0x3e
|
||||
|
||||
struct amdtp_tscm {
|
||||
unsigned int pcm_channels;
|
||||
|
||||
void (*transfer_samples)(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames);
|
||||
};
|
||||
|
||||
int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate)
|
||||
{
|
||||
struct amdtp_tscm *p = s->protocol;
|
||||
unsigned int data_channels;
|
||||
|
||||
if (amdtp_stream_running(s))
|
||||
return -EBUSY;
|
||||
|
||||
data_channels = p->pcm_channels;
|
||||
|
||||
/* Packets in in-stream have extra 2 data channels. */
|
||||
if (s->direction == AMDTP_IN_STREAM)
|
||||
data_channels += 2;
|
||||
|
||||
return amdtp_stream_set_parameters(s, rate, data_channels);
|
||||
}
|
||||
|
||||
static void write_pcm_s32(struct amdtp_stream *s,
|
||||
struct snd_pcm_substream *pcm,
|
||||
__be32 *buffer, unsigned int frames)
|
||||
{
|
||||
struct amdtp_tscm *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[c] = cpu_to_be32(*src);
|
||||
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_tscm *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[c] = cpu_to_be32(*src << 16);
|
||||
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_tscm *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;
|
||||
|
||||
/* The first data channel is for event counter. */
|
||||
buffer += 1;
|
||||
|
||||
for (i = 0; i < frames; ++i) {
|
||||
for (c = 0; c < channels; ++c) {
|
||||
*dst = be32_to_cpu(buffer[c]);
|
||||
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 data_blocks)
|
||||
{
|
||||
struct amdtp_tscm *p = s->protocol;
|
||||
unsigned int channels, i, c;
|
||||
|
||||
channels = p->pcm_channels;
|
||||
|
||||
for (i = 0; i < data_blocks; ++i) {
|
||||
for (c = 0; c < channels; ++c)
|
||||
buffer[c] = 0x00000000;
|
||||
buffer += s->data_block_quadlets;
|
||||
}
|
||||
}
|
||||
|
||||
int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
|
||||
struct snd_pcm_runtime *runtime)
|
||||
{
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Our implementation allows this protocol to deliver 24 bit sample in
|
||||
* 32bit data channel.
|
||||
*/
|
||||
err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return amdtp_stream_add_pcm_hw_constraints(s, runtime);
|
||||
}
|
||||
|
||||
void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
|
||||
{
|
||||
struct amdtp_tscm *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;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
{
|
||||
struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol;
|
||||
struct snd_pcm_substream *pcm;
|
||||
|
||||
pcm = ACCESS_ONCE(s->pcm);
|
||||
if (data_blocks > 0 && pcm)
|
||||
p->transfer_samples(s, pcm, buffer, data_blocks);
|
||||
|
||||
/* A place holder for control messages. */
|
||||
|
||||
return data_blocks;
|
||||
}
|
||||
|
||||
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
|
||||
__be32 *buffer,
|
||||
unsigned int data_blocks,
|
||||
unsigned int *syt)
|
||||
{
|
||||
struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol;
|
||||
struct snd_pcm_substream *pcm;
|
||||
|
||||
/* This field is not used. */
|
||||
*syt = 0x0000;
|
||||
|
||||
pcm = ACCESS_ONCE(s->pcm);
|
||||
if (pcm)
|
||||
p->transfer_samples(s, pcm, buffer, data_blocks);
|
||||
else
|
||||
write_pcm_silence(s, buffer, data_blocks);
|
||||
|
||||
return data_blocks;
|
||||
}
|
||||
|
||||
int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir, unsigned int pcm_channels)
|
||||
{
|
||||
amdtp_stream_process_data_blocks_t process_data_blocks;
|
||||
struct amdtp_tscm *p;
|
||||
unsigned int fmt;
|
||||
int err;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
fmt = AMDTP_FMT_TSCM_TX;
|
||||
process_data_blocks = process_tx_data_blocks;
|
||||
} else {
|
||||
fmt = AMDTP_FMT_TSCM_RX;
|
||||
process_data_blocks = process_rx_data_blocks;
|
||||
}
|
||||
|
||||
err = amdtp_stream_init(s, unit, dir,
|
||||
CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt,
|
||||
process_data_blocks, sizeof(struct amdtp_tscm));
|
||||
if (err < 0)
|
||||
return 0;
|
||||
|
||||
/* Use fixed value for FDF field. */
|
||||
s->fdf = 0x00;
|
||||
|
||||
/* This protocol uses fixed number of data channels for PCM samples. */
|
||||
p = s->protocol;
|
||||
p->pcm_channels = pcm_channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* tascam-hwdep.c - a part of driver for TASCAM FireWire series
|
||||
*
|
||||
* Copyright (c) 2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This codes give three functionality.
|
||||
*
|
||||
* 1.get firewire node information
|
||||
* 2.get notification about starting/stopping stream
|
||||
* 3.lock/unlock stream
|
||||
*/
|
||||
|
||||
#include "tascam.h"
|
||||
|
||||
static long hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
|
||||
long count)
|
||||
{
|
||||
union snd_firewire_event event;
|
||||
|
||||
memset(&event, 0, sizeof(event));
|
||||
|
||||
event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
|
||||
event.lock_status.status = (tscm->dev_lock_count > 0);
|
||||
tscm->dev_lock_changed = false;
|
||||
|
||||
count = min_t(long, count, sizeof(event.lock_status));
|
||||
|
||||
if (copy_to_user(buf, &event, count))
|
||||
return -EFAULT;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
|
||||
loff_t *offset)
|
||||
{
|
||||
struct snd_tscm *tscm = hwdep->private_data;
|
||||
DEFINE_WAIT(wait);
|
||||
union snd_firewire_event event;
|
||||
|
||||
spin_lock_irq(&tscm->lock);
|
||||
|
||||
while (!tscm->dev_lock_changed) {
|
||||
prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
|
||||
spin_unlock_irq(&tscm->lock);
|
||||
schedule();
|
||||
finish_wait(&tscm->hwdep_wait, &wait);
|
||||
if (signal_pending(current))
|
||||
return -ERESTARTSYS;
|
||||
spin_lock_irq(&tscm->lock);
|
||||
}
|
||||
|
||||
memset(&event, 0, sizeof(event));
|
||||
count = hwdep_read_locked(tscm, buf, count);
|
||||
spin_unlock_irq(&tscm->lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
|
||||
poll_table *wait)
|
||||
{
|
||||
struct snd_tscm *tscm = hwdep->private_data;
|
||||
unsigned int events;
|
||||
|
||||
poll_wait(file, &tscm->hwdep_wait, wait);
|
||||
|
||||
spin_lock_irq(&tscm->lock);
|
||||
if (tscm->dev_lock_changed)
|
||||
events = POLLIN | POLLRDNORM;
|
||||
else
|
||||
events = 0;
|
||||
spin_unlock_irq(&tscm->lock);
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
|
||||
{
|
||||
struct fw_device *dev = fw_parent_device(tscm->unit);
|
||||
struct snd_firewire_get_info info;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.type = SNDRV_FIREWIRE_TYPE_TASCAM;
|
||||
info.card = dev->card->index;
|
||||
*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
|
||||
*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
|
||||
strlcpy(info.device_name, dev_name(&dev->device),
|
||||
sizeof(info.device_name));
|
||||
|
||||
if (copy_to_user(arg, &info, sizeof(info)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hwdep_lock(struct snd_tscm *tscm)
|
||||
{
|
||||
int err;
|
||||
|
||||
spin_lock_irq(&tscm->lock);
|
||||
|
||||
if (tscm->dev_lock_count == 0) {
|
||||
tscm->dev_lock_count = -1;
|
||||
err = 0;
|
||||
} else {
|
||||
err = -EBUSY;
|
||||
}
|
||||
|
||||
spin_unlock_irq(&tscm->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hwdep_unlock(struct snd_tscm *tscm)
|
||||
{
|
||||
int err;
|
||||
|
||||
spin_lock_irq(&tscm->lock);
|
||||
|
||||
if (tscm->dev_lock_count == -1) {
|
||||
tscm->dev_lock_count = 0;
|
||||
err = 0;
|
||||
} else {
|
||||
err = -EBADFD;
|
||||
}
|
||||
|
||||
spin_unlock_irq(&tscm->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
|
||||
{
|
||||
struct snd_tscm *tscm = hwdep->private_data;
|
||||
|
||||
spin_lock_irq(&tscm->lock);
|
||||
if (tscm->dev_lock_count == -1)
|
||||
tscm->dev_lock_count = 0;
|
||||
spin_unlock_irq(&tscm->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct snd_tscm *tscm = hwdep->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_FIREWIRE_IOCTL_GET_INFO:
|
||||
return hwdep_get_info(tscm, (void __user *)arg);
|
||||
case SNDRV_FIREWIRE_IOCTL_LOCK:
|
||||
return hwdep_lock(tscm);
|
||||
case SNDRV_FIREWIRE_IOCTL_UNLOCK:
|
||||
return hwdep_unlock(tscm);
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
return hwdep_ioctl(hwdep, file, cmd,
|
||||
(unsigned long)compat_ptr(arg));
|
||||
}
|
||||
#else
|
||||
#define hwdep_compat_ioctl NULL
|
||||
#endif
|
||||
|
||||
static const struct snd_hwdep_ops hwdep_ops = {
|
||||
.read = hwdep_read,
|
||||
.release = hwdep_release,
|
||||
.poll = hwdep_poll,
|
||||
.ioctl = hwdep_ioctl,
|
||||
.ioctl_compat = hwdep_compat_ioctl,
|
||||
};
|
||||
|
||||
int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)
|
||||
{
|
||||
struct snd_hwdep *hwdep;
|
||||
int err;
|
||||
|
||||
err = snd_hwdep_new(tscm->card, "Tascam", 0, &hwdep);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
strcpy(hwdep->name, "Tascam");
|
||||
hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM;
|
||||
hwdep->ops = hwdep_ops;
|
||||
hwdep->private_data = tscm;
|
||||
hwdep->exclusive = true;
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* tascam-midi.c - a part of driver for TASCAM FireWire series
|
||||
*
|
||||
* Copyright (c) 2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include "tascam.h"
|
||||
|
||||
static int midi_capture_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
/* Do nothing. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int midi_playback_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->rmidi->private_data;
|
||||
|
||||
/* Initialize internal status. */
|
||||
tscm->running_status[substream->number] = 0;
|
||||
tscm->on_sysex[substream->number] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int midi_capture_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
/* Do nothing. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int midi_playback_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->rmidi->private_data;
|
||||
|
||||
snd_fw_async_midi_port_finish(&tscm->out_ports[substream->number]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
|
||||
{
|
||||
struct snd_tscm *tscm = substrm->rmidi->private_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tscm->lock, flags);
|
||||
|
||||
if (up)
|
||||
tscm->tx_midi_substreams[substrm->number] = substrm;
|
||||
else
|
||||
tscm->tx_midi_substreams[substrm->number] = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&tscm->lock, flags);
|
||||
}
|
||||
|
||||
static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
|
||||
{
|
||||
struct snd_tscm *tscm = substrm->rmidi->private_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tscm->lock, flags);
|
||||
|
||||
if (up)
|
||||
snd_fw_async_midi_port_run(&tscm->out_ports[substrm->number],
|
||||
substrm);
|
||||
|
||||
spin_unlock_irqrestore(&tscm->lock, flags);
|
||||
}
|
||||
|
||||
static struct snd_rawmidi_ops midi_capture_ops = {
|
||||
.open = midi_capture_open,
|
||||
.close = midi_capture_close,
|
||||
.trigger = midi_capture_trigger,
|
||||
};
|
||||
|
||||
static struct snd_rawmidi_ops midi_playback_ops = {
|
||||
.open = midi_playback_open,
|
||||
.close = midi_playback_close,
|
||||
.trigger = midi_playback_trigger,
|
||||
};
|
||||
|
||||
int snd_tscm_create_midi_devices(struct snd_tscm *tscm)
|
||||
{
|
||||
struct snd_rawmidi *rmidi;
|
||||
struct snd_rawmidi_str *stream;
|
||||
struct snd_rawmidi_substream *subs;
|
||||
int err;
|
||||
|
||||
err = snd_rawmidi_new(tscm->card, tscm->card->driver, 0,
|
||||
tscm->spec->midi_playback_ports,
|
||||
tscm->spec->midi_capture_ports,
|
||||
&rmidi);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
snprintf(rmidi->name, sizeof(rmidi->name),
|
||||
"%s MIDI", tscm->card->shortname);
|
||||
rmidi->private_data = tscm;
|
||||
|
||||
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
|
||||
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
|
||||
&midi_capture_ops);
|
||||
stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
|
||||
|
||||
/* Set port names for MIDI input. */
|
||||
list_for_each_entry(subs, &stream->substreams, list) {
|
||||
/* TODO: support virtual MIDI ports. */
|
||||
if (subs->number < tscm->spec->midi_capture_ports) {
|
||||
/* Hardware MIDI ports. */
|
||||
snprintf(subs->name, sizeof(subs->name),
|
||||
"%s MIDI %d",
|
||||
tscm->card->shortname, subs->number + 1);
|
||||
}
|
||||
}
|
||||
|
||||
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
|
||||
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
|
||||
&midi_playback_ops);
|
||||
stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
|
||||
|
||||
/* Set port names for MIDI ourput. */
|
||||
list_for_each_entry(subs, &stream->substreams, list) {
|
||||
if (subs->number < tscm->spec->midi_playback_ports) {
|
||||
/* Hardware MIDI ports only. */
|
||||
snprintf(subs->name, sizeof(subs->name),
|
||||
"%s MIDI %d",
|
||||
tscm->card->shortname, subs->number + 1);
|
||||
}
|
||||
}
|
||||
|
||||
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* tascam-pcm.c - a part of driver for TASCAM FireWire series
|
||||
*
|
||||
* Copyright (c) 2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include "tascam.h"
|
||||
|
||||
static void set_buffer_params(struct snd_pcm_hardware *hw)
|
||||
{
|
||||
hw->period_bytes_min = 4 * hw->channels_min;
|
||||
hw->period_bytes_max = hw->period_bytes_min * 2048;
|
||||
hw->buffer_bytes_max = hw->period_bytes_max * 2;
|
||||
|
||||
hw->periods_min = 2;
|
||||
hw->periods_max = UINT_MAX;
|
||||
}
|
||||
|
||||
static int pcm_init_hw_params(struct snd_tscm *tscm,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
static const struct snd_pcm_hardware hardware = {
|
||||
.info = SNDRV_PCM_INFO_BATCH |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_JOINT_DUPLEX |
|
||||
SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID,
|
||||
.rates = SNDRV_PCM_RATE_44100 |
|
||||
SNDRV_PCM_RATE_48000 |
|
||||
SNDRV_PCM_RATE_88200 |
|
||||
SNDRV_PCM_RATE_96000,
|
||||
.rate_min = 44100,
|
||||
.rate_max = 96000,
|
||||
.channels_min = 10,
|
||||
.channels_max = 18,
|
||||
};
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct amdtp_stream *stream;
|
||||
unsigned int pcm_channels;
|
||||
|
||||
runtime->hw = hardware;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
|
||||
stream = &tscm->tx_stream;
|
||||
pcm_channels = tscm->spec->pcm_capture_analog_channels;
|
||||
} else {
|
||||
runtime->hw.formats =
|
||||
SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32;
|
||||
stream = &tscm->rx_stream;
|
||||
pcm_channels = tscm->spec->pcm_playback_analog_channels;
|
||||
}
|
||||
|
||||
if (tscm->spec->has_adat)
|
||||
pcm_channels += 8;
|
||||
if (tscm->spec->has_spdif)
|
||||
pcm_channels += 2;
|
||||
runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;
|
||||
|
||||
set_buffer_params(&runtime->hw);
|
||||
|
||||
return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
|
||||
}
|
||||
|
||||
static int pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->private_data;
|
||||
enum snd_tscm_clock clock;
|
||||
unsigned int rate;
|
||||
int err;
|
||||
|
||||
err = snd_tscm_stream_lock_try(tscm);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = pcm_init_hw_params(tscm, substream);
|
||||
if (err < 0)
|
||||
goto err_locked;
|
||||
|
||||
err = snd_tscm_stream_get_clock(tscm, &clock);
|
||||
if (clock != SND_TSCM_CLOCK_INTERNAL ||
|
||||
amdtp_stream_pcm_running(&tscm->rx_stream) ||
|
||||
amdtp_stream_pcm_running(&tscm->tx_stream)) {
|
||||
err = snd_tscm_stream_get_rate(tscm, &rate);
|
||||
if (err < 0)
|
||||
goto err_locked;
|
||||
substream->runtime->hw.rate_min = rate;
|
||||
substream->runtime->hw.rate_max = rate;
|
||||
}
|
||||
|
||||
snd_pcm_set_sync(substream);
|
||||
end:
|
||||
return err;
|
||||
err_locked:
|
||||
snd_tscm_stream_lock_release(tscm);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->private_data;
|
||||
|
||||
snd_tscm_stream_lock_release(tscm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&tscm->mutex);
|
||||
tscm->substreams_counter++;
|
||||
mutex_unlock(&tscm->mutex);
|
||||
}
|
||||
|
||||
amdtp_tscm_set_pcm_format(&tscm->tx_stream, params_format(hw_params));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&tscm->mutex);
|
||||
tscm->substreams_counter++;
|
||||
mutex_unlock(&tscm->mutex);
|
||||
}
|
||||
|
||||
amdtp_tscm_set_pcm_format(&tscm->rx_stream, params_format(hw_params));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->private_data;
|
||||
|
||||
mutex_lock(&tscm->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
tscm->substreams_counter--;
|
||||
|
||||
snd_tscm_stream_stop_duplex(tscm);
|
||||
|
||||
mutex_unlock(&tscm->mutex);
|
||||
|
||||
return snd_pcm_lib_free_vmalloc_buffer(substream);
|
||||
}
|
||||
|
||||
static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->private_data;
|
||||
|
||||
mutex_lock(&tscm->mutex);
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
tscm->substreams_counter--;
|
||||
|
||||
snd_tscm_stream_stop_duplex(tscm);
|
||||
|
||||
mutex_unlock(&tscm->mutex);
|
||||
|
||||
return snd_pcm_lib_free_vmalloc_buffer(substream);
|
||||
}
|
||||
|
||||
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
|
||||
mutex_lock(&tscm->mutex);
|
||||
|
||||
err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
|
||||
if (err >= 0)
|
||||
amdtp_stream_pcm_prepare(&tscm->tx_stream);
|
||||
|
||||
mutex_unlock(&tscm->mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
|
||||
mutex_lock(&tscm->mutex);
|
||||
|
||||
err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
|
||||
if (err >= 0)
|
||||
amdtp_stream_pcm_prepare(&tscm->rx_stream);
|
||||
|
||||
mutex_unlock(&tscm->mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
|
||||
{
|
||||
struct snd_tscm *tscm = sbstrm->private_data;
|
||||
|
||||
return amdtp_stream_pcm_pointer(&tscm->tx_stream);
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
|
||||
{
|
||||
struct snd_tscm *tscm = sbstrm->private_data;
|
||||
|
||||
return amdtp_stream_pcm_pointer(&tscm->rx_stream);
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops pcm_capture_ops = {
|
||||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = pcm_capture_hw_params,
|
||||
.hw_free = pcm_capture_hw_free,
|
||||
.prepare = pcm_capture_prepare,
|
||||
.trigger = pcm_capture_trigger,
|
||||
.pointer = pcm_capture_pointer,
|
||||
.page = snd_pcm_lib_get_vmalloc_page,
|
||||
};
|
||||
|
||||
static struct snd_pcm_ops pcm_playback_ops = {
|
||||
.open = pcm_open,
|
||||
.close = pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = pcm_playback_hw_params,
|
||||
.hw_free = pcm_playback_hw_free,
|
||||
.prepare = pcm_playback_prepare,
|
||||
.trigger = pcm_playback_trigger,
|
||||
.pointer = pcm_playback_pointer,
|
||||
.page = snd_pcm_lib_get_vmalloc_page,
|
||||
.mmap = snd_pcm_lib_mmap_vmalloc,
|
||||
};
|
||||
|
||||
int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
|
||||
{
|
||||
struct snd_pcm *pcm;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
pcm->private_data = tscm;
|
||||
snprintf(pcm->name, sizeof(pcm->name),
|
||||
"%s PCM", tscm->card->shortname);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* tascam-proc.h - a part of driver for TASCAM FireWire series
|
||||
*
|
||||
* Copyright (c) 2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include "./tascam.h"
|
||||
|
||||
static void proc_read_firmware(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct snd_tscm *tscm = entry->private_data;
|
||||
__be32 data;
|
||||
unsigned int reg, fpga, arm, hw;
|
||||
int err;
|
||||
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_REGISTER,
|
||||
&data, sizeof(data), 0);
|
||||
if (err < 0)
|
||||
return;
|
||||
reg = be32_to_cpu(data);
|
||||
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_FPGA,
|
||||
&data, sizeof(data), 0);
|
||||
if (err < 0)
|
||||
return;
|
||||
fpga = be32_to_cpu(data);
|
||||
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_ARM,
|
||||
&data, sizeof(data), 0);
|
||||
if (err < 0)
|
||||
return;
|
||||
arm = be32_to_cpu(data);
|
||||
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_HW,
|
||||
&data, sizeof(data), 0);
|
||||
if (err < 0)
|
||||
return;
|
||||
hw = be32_to_cpu(data);
|
||||
|
||||
snd_iprintf(buffer, "Register: %d (0x%08x)\n", reg & 0xffff, reg);
|
||||
snd_iprintf(buffer, "FPGA: %d (0x%08x)\n", fpga & 0xffff, fpga);
|
||||
snd_iprintf(buffer, "ARM: %d (0x%08x)\n", arm & 0xffff, arm);
|
||||
snd_iprintf(buffer, "Hardware: %d (0x%08x)\n", hw >> 16, hw);
|
||||
}
|
||||
|
||||
static void add_node(struct snd_tscm *tscm, struct snd_info_entry *root,
|
||||
const char *name,
|
||||
void (*op)(struct snd_info_entry *e,
|
||||
struct snd_info_buffer *b))
|
||||
{
|
||||
struct snd_info_entry *entry;
|
||||
|
||||
entry = snd_info_create_card_entry(tscm->card, name, root);
|
||||
if (entry == NULL)
|
||||
return;
|
||||
|
||||
snd_info_set_text_ops(entry, tscm, op);
|
||||
if (snd_info_register(entry) < 0)
|
||||
snd_info_free_entry(entry);
|
||||
}
|
||||
|
||||
void snd_tscm_proc_init(struct snd_tscm *tscm)
|
||||
{
|
||||
struct snd_info_entry *root;
|
||||
|
||||
/*
|
||||
* All nodes are automatically removed at snd_card_disconnect(),
|
||||
* by following to link list.
|
||||
*/
|
||||
root = snd_info_create_card_entry(tscm->card, "firewire",
|
||||
tscm->card->proc_root);
|
||||
if (root == NULL)
|
||||
return;
|
||||
root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
if (snd_info_register(root) < 0) {
|
||||
snd_info_free_entry(root);
|
||||
return;
|
||||
}
|
||||
|
||||
add_node(tscm, root, "firmware", proc_read_firmware);
|
||||
}
|
||||
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
* tascam-stream.c - a part of driver for TASCAM FireWire series
|
||||
*
|
||||
* Copyright (c) 2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include "tascam.h"
|
||||
|
||||
#define CALLBACK_TIMEOUT 500
|
||||
|
||||
static int get_clock(struct snd_tscm *tscm, u32 *data)
|
||||
{
|
||||
__be32 reg;
|
||||
int err;
|
||||
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
|
||||
®, sizeof(reg), 0);
|
||||
if (err >= 0)
|
||||
*data = be32_to_cpu(reg);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int set_clock(struct snd_tscm *tscm, unsigned int rate,
|
||||
enum snd_tscm_clock clock)
|
||||
{
|
||||
u32 data;
|
||||
__be32 reg;
|
||||
int err;
|
||||
|
||||
err = get_clock(tscm, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
data &= 0x0000ffff;
|
||||
|
||||
if (rate > 0) {
|
||||
data &= 0x000000ff;
|
||||
/* Base rate. */
|
||||
if ((rate % 44100) == 0) {
|
||||
data |= 0x00000100;
|
||||
/* Multiplier. */
|
||||
if (rate / 44100 == 2)
|
||||
data |= 0x00008000;
|
||||
} else if ((rate % 48000) == 0) {
|
||||
data |= 0x00000200;
|
||||
/* Multiplier. */
|
||||
if (rate / 48000 == 2)
|
||||
data |= 0x00008000;
|
||||
} else {
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
if (clock != INT_MAX) {
|
||||
data &= 0x0000ff00;
|
||||
data |= clock + 1;
|
||||
}
|
||||
|
||||
reg = cpu_to_be32(data);
|
||||
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (data & 0x00008000)
|
||||
reg = cpu_to_be32(0x0000001a);
|
||||
else
|
||||
reg = cpu_to_be32(0x0000000d);
|
||||
|
||||
return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_MULTIPLEX_MODE,
|
||||
®, sizeof(reg), 0);
|
||||
}
|
||||
|
||||
int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)
|
||||
{
|
||||
u32 data = 0x0;
|
||||
unsigned int trials = 0;
|
||||
int err;
|
||||
|
||||
while (data == 0x0 || trials++ < 5) {
|
||||
err = get_clock(tscm, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
data = (data & 0xff000000) >> 24;
|
||||
}
|
||||
|
||||
/* Check base rate. */
|
||||
if ((data & 0x0f) == 0x01)
|
||||
*rate = 44100;
|
||||
else if ((data & 0x0f) == 0x02)
|
||||
*rate = 48000;
|
||||
else
|
||||
return -EAGAIN;
|
||||
|
||||
/* Check multiplier. */
|
||||
if ((data & 0xf0) == 0x80)
|
||||
*rate *= 2;
|
||||
else if ((data & 0xf0) != 0x00)
|
||||
return -EAGAIN;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int snd_tscm_stream_get_clock(struct snd_tscm *tscm, enum snd_tscm_clock *clock)
|
||||
{
|
||||
u32 data;
|
||||
int err;
|
||||
|
||||
err = get_clock(tscm, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
*clock = ((data & 0x00ff0000) >> 16) - 1;
|
||||
if (*clock < 0 || *clock > SND_TSCM_CLOCK_ADAT)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enable_data_channels(struct snd_tscm *tscm)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
data = 0;
|
||||
for (i = 0; i < tscm->spec->pcm_capture_analog_channels; ++i)
|
||||
data |= BIT(i);
|
||||
if (tscm->spec->has_adat)
|
||||
data |= 0x0000ff00;
|
||||
if (tscm->spec->has_spdif)
|
||||
data |= 0x00030000;
|
||||
|
||||
reg = cpu_to_be32(data);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_TX_PCM_CHANNELS,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
data = 0;
|
||||
for (i = 0; i < tscm->spec->pcm_playback_analog_channels; ++i)
|
||||
data |= BIT(i);
|
||||
if (tscm->spec->has_adat)
|
||||
data |= 0x0000ff00;
|
||||
if (tscm->spec->has_spdif)
|
||||
data |= 0x00030000;
|
||||
|
||||
reg = cpu_to_be32(data);
|
||||
return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_RX_PCM_CHANNELS,
|
||||
®, sizeof(reg), 0);
|
||||
}
|
||||
|
||||
static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
|
||||
{
|
||||
__be32 reg;
|
||||
int err;
|
||||
|
||||
/* Set an option for unknown purpose. */
|
||||
reg = cpu_to_be32(0x00200000);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = enable_data_channels(tscm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return set_clock(tscm, rate, INT_MAX);
|
||||
}
|
||||
|
||||
static void finish_session(struct snd_tscm *tscm)
|
||||
{
|
||||
__be32 reg;
|
||||
|
||||
reg = 0;
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
|
||||
®, sizeof(reg), 0);
|
||||
|
||||
reg = 0;
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
|
||||
®, sizeof(reg), 0);
|
||||
|
||||
}
|
||||
|
||||
static int begin_session(struct snd_tscm *tscm)
|
||||
{
|
||||
__be32 reg;
|
||||
int err;
|
||||
|
||||
reg = cpu_to_be32(0x00000001);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
reg = cpu_to_be32(0x00000001);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Set an option for unknown purpose. */
|
||||
reg = cpu_to_be32(0x00002000);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Start multiplexing PCM samples on packets. */
|
||||
reg = cpu_to_be32(0x00000001);
|
||||
return snd_fw_transaction(tscm->unit,
|
||||
TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_ON,
|
||||
®, sizeof(reg), 0);
|
||||
}
|
||||
|
||||
static void release_resources(struct snd_tscm *tscm)
|
||||
{
|
||||
__be32 reg;
|
||||
|
||||
/* Unregister channels. */
|
||||
reg = cpu_to_be32(0x00000000);
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
|
||||
®, sizeof(reg), 0);
|
||||
reg = cpu_to_be32(0x00000000);
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
|
||||
®, sizeof(reg), 0);
|
||||
reg = cpu_to_be32(0x00000000);
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
|
||||
®, sizeof(reg), 0);
|
||||
|
||||
/* Release isochronous resources. */
|
||||
fw_iso_resources_free(&tscm->tx_resources);
|
||||
fw_iso_resources_free(&tscm->rx_resources);
|
||||
}
|
||||
|
||||
static int keep_resources(struct snd_tscm *tscm, unsigned int rate)
|
||||
{
|
||||
__be32 reg;
|
||||
int err;
|
||||
|
||||
/* Keep resources for in-stream. */
|
||||
err = amdtp_tscm_set_parameters(&tscm->tx_stream, rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = fw_iso_resources_allocate(&tscm->tx_resources,
|
||||
amdtp_stream_get_max_payload(&tscm->tx_stream),
|
||||
fw_parent_device(tscm->unit)->max_speed);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
/* Keep resources for out-stream. */
|
||||
err = amdtp_tscm_set_parameters(&tscm->rx_stream, rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = fw_iso_resources_allocate(&tscm->rx_resources,
|
||||
amdtp_stream_get_max_payload(&tscm->rx_stream),
|
||||
fw_parent_device(tscm->unit)->max_speed);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Register the isochronous channel for transmitting stream. */
|
||||
reg = cpu_to_be32(tscm->tx_resources.channel);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
/* Unknown */
|
||||
reg = cpu_to_be32(0x00000002);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
/* Register the isochronous channel for receiving stream. */
|
||||
reg = cpu_to_be32(tscm->rx_resources.channel);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
release_resources(tscm);
|
||||
return err;
|
||||
}
|
||||
|
||||
int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
|
||||
{
|
||||
unsigned int pcm_channels;
|
||||
int err;
|
||||
|
||||
/* For out-stream. */
|
||||
err = fw_iso_resources_init(&tscm->rx_resources, tscm->unit);
|
||||
if (err < 0)
|
||||
return err;
|
||||
pcm_channels = tscm->spec->pcm_playback_analog_channels;
|
||||
if (tscm->spec->has_adat)
|
||||
pcm_channels += 8;
|
||||
if (tscm->spec->has_spdif)
|
||||
pcm_channels += 2;
|
||||
err = amdtp_tscm_init(&tscm->rx_stream, tscm->unit, AMDTP_OUT_STREAM,
|
||||
pcm_channels);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* For in-stream. */
|
||||
err = fw_iso_resources_init(&tscm->tx_resources, tscm->unit);
|
||||
if (err < 0)
|
||||
return err;
|
||||
pcm_channels = tscm->spec->pcm_capture_analog_channels;
|
||||
if (tscm->spec->has_adat)
|
||||
pcm_channels += 8;
|
||||
if (tscm->spec->has_spdif)
|
||||
pcm_channels += 2;
|
||||
err = amdtp_tscm_init(&tscm->tx_stream, tscm->unit, AMDTP_IN_STREAM,
|
||||
pcm_channels);
|
||||
if (err < 0)
|
||||
amdtp_stream_destroy(&tscm->rx_stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* At bus reset, streaming is stopped and some registers are clear. */
|
||||
void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
|
||||
{
|
||||
amdtp_stream_pcm_abort(&tscm->tx_stream);
|
||||
amdtp_stream_stop(&tscm->tx_stream);
|
||||
|
||||
amdtp_stream_pcm_abort(&tscm->rx_stream);
|
||||
amdtp_stream_stop(&tscm->rx_stream);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function should be called before starting streams or after stopping
|
||||
* streams.
|
||||
*/
|
||||
void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
|
||||
{
|
||||
amdtp_stream_destroy(&tscm->rx_stream);
|
||||
amdtp_stream_destroy(&tscm->tx_stream);
|
||||
|
||||
fw_iso_resources_destroy(&tscm->rx_resources);
|
||||
fw_iso_resources_destroy(&tscm->tx_resources);
|
||||
}
|
||||
|
||||
int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
|
||||
{
|
||||
unsigned int curr_rate;
|
||||
int err;
|
||||
|
||||
if (tscm->substreams_counter == 0)
|
||||
return 0;
|
||||
|
||||
err = snd_tscm_stream_get_rate(tscm, &curr_rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (curr_rate != rate ||
|
||||
amdtp_streaming_error(&tscm->tx_stream) ||
|
||||
amdtp_streaming_error(&tscm->rx_stream)) {
|
||||
finish_session(tscm);
|
||||
|
||||
amdtp_stream_stop(&tscm->tx_stream);
|
||||
amdtp_stream_stop(&tscm->rx_stream);
|
||||
|
||||
release_resources(tscm);
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&tscm->tx_stream)) {
|
||||
amdtp_stream_set_sync(CIP_SYNC_TO_DEVICE,
|
||||
&tscm->tx_stream, &tscm->rx_stream);
|
||||
err = keep_resources(tscm, rate);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = set_stream_formats(tscm, rate);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = begin_session(tscm);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_stream_start(&tscm->tx_stream,
|
||||
tscm->tx_resources.channel,
|
||||
fw_parent_device(tscm->unit)->max_speed);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&tscm->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&tscm->rx_stream)) {
|
||||
err = amdtp_stream_start(&tscm->rx_stream,
|
||||
tscm->rx_resources.channel,
|
||||
fw_parent_device(tscm->unit)->max_speed);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&tscm->rx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
amdtp_stream_stop(&tscm->tx_stream);
|
||||
amdtp_stream_stop(&tscm->rx_stream);
|
||||
|
||||
finish_session(tscm);
|
||||
release_resources(tscm);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
|
||||
{
|
||||
if (tscm->substreams_counter > 0)
|
||||
return;
|
||||
|
||||
amdtp_stream_stop(&tscm->tx_stream);
|
||||
amdtp_stream_stop(&tscm->rx_stream);
|
||||
|
||||
finish_session(tscm);
|
||||
release_resources(tscm);
|
||||
}
|
||||
|
||||
void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)
|
||||
{
|
||||
tscm->dev_lock_changed = true;
|
||||
wake_up(&tscm->hwdep_wait);
|
||||
}
|
||||
|
||||
int snd_tscm_stream_lock_try(struct snd_tscm *tscm)
|
||||
{
|
||||
int err;
|
||||
|
||||
spin_lock_irq(&tscm->lock);
|
||||
|
||||
/* user land lock this */
|
||||
if (tscm->dev_lock_count < 0) {
|
||||
err = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* this is the first time */
|
||||
if (tscm->dev_lock_count++ == 0)
|
||||
snd_tscm_stream_lock_changed(tscm);
|
||||
err = 0;
|
||||
end:
|
||||
spin_unlock_irq(&tscm->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_tscm_stream_lock_release(struct snd_tscm *tscm)
|
||||
{
|
||||
spin_lock_irq(&tscm->lock);
|
||||
|
||||
if (WARN_ON(tscm->dev_lock_count <= 0))
|
||||
goto end;
|
||||
if (--tscm->dev_lock_count == 0)
|
||||
snd_tscm_stream_lock_changed(tscm);
|
||||
end:
|
||||
spin_unlock_irq(&tscm->lock);
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* tascam-transaction.c - a part of driver for TASCAM FireWire series
|
||||
*
|
||||
* Copyright (c) 2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include "tascam.h"
|
||||
|
||||
/*
|
||||
* When return minus value, given argument is not MIDI status.
|
||||
* When return 0, given argument is a beginning of system exclusive.
|
||||
* When return the others, given argument is MIDI data.
|
||||
*/
|
||||
static inline int calculate_message_bytes(u8 status)
|
||||
{
|
||||
switch (status) {
|
||||
case 0xf6: /* Tune request. */
|
||||
case 0xf8: /* Timing clock. */
|
||||
case 0xfa: /* Start. */
|
||||
case 0xfb: /* Continue. */
|
||||
case 0xfc: /* Stop. */
|
||||
case 0xfe: /* Active sensing. */
|
||||
case 0xff: /* System reset. */
|
||||
return 1;
|
||||
case 0xf1: /* MIDI time code quarter frame. */
|
||||
case 0xf3: /* Song select. */
|
||||
return 2;
|
||||
case 0xf2: /* Song position pointer. */
|
||||
return 3;
|
||||
case 0xf0: /* Exclusive. */
|
||||
return 0;
|
||||
case 0xf7: /* End of exclusive. */
|
||||
break;
|
||||
case 0xf4: /* Undefined. */
|
||||
case 0xf5: /* Undefined. */
|
||||
case 0xf9: /* Undefined. */
|
||||
case 0xfd: /* Undefined. */
|
||||
break;
|
||||
default:
|
||||
switch (status & 0xf0) {
|
||||
case 0x80: /* Note on. */
|
||||
case 0x90: /* Note off. */
|
||||
case 0xa0: /* Polyphonic key pressure. */
|
||||
case 0xb0: /* Control change and Mode change. */
|
||||
case 0xe0: /* Pitch bend change. */
|
||||
return 3;
|
||||
case 0xc0: /* Program change. */
|
||||
case 0xd0: /* Channel pressure. */
|
||||
return 2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf)
|
||||
{
|
||||
struct snd_tscm *tscm = substream->rmidi->private_data;
|
||||
unsigned int port = substream->number;
|
||||
int i, len, consume;
|
||||
u8 *label, *msg;
|
||||
u8 status;
|
||||
|
||||
/* The first byte is used for label, the rest for MIDI bytes. */
|
||||
label = buf;
|
||||
msg = buf + 1;
|
||||
|
||||
consume = snd_rawmidi_transmit_peek(substream, msg, 3);
|
||||
if (consume == 0)
|
||||
return 0;
|
||||
|
||||
/* On exclusive message. */
|
||||
if (tscm->on_sysex[port]) {
|
||||
/* Seek the end of exclusives. */
|
||||
for (i = 0; i < consume; ++i) {
|
||||
if (msg[i] == 0xf7) {
|
||||
tscm->on_sysex[port] = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* At the end of exclusive message, use label 0x07. */
|
||||
if (!tscm->on_sysex[port]) {
|
||||
consume = i + 1;
|
||||
*label = (port << 4) | 0x07;
|
||||
/* During exclusive message, use label 0x04. */
|
||||
} else if (consume == 3) {
|
||||
*label = (port << 4) | 0x04;
|
||||
/* We need to fill whole 3 bytes. Go to next change. */
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = consume;
|
||||
} else {
|
||||
/* The beginning of exclusives. */
|
||||
if (msg[0] == 0xf0) {
|
||||
/* Transfer it in next chance in another condition. */
|
||||
tscm->on_sysex[port] = true;
|
||||
return 0;
|
||||
} else {
|
||||
/* On running-status. */
|
||||
if ((msg[0] & 0x80) != 0x80)
|
||||
status = tscm->running_status[port];
|
||||
else
|
||||
status = msg[0];
|
||||
|
||||
/* Calculate consume bytes. */
|
||||
len = calculate_message_bytes(status);
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
|
||||
/* On running-status. */
|
||||
if ((msg[0] & 0x80) != 0x80) {
|
||||
/* Enough MIDI bytes were not retrieved. */
|
||||
if (consume < len - 1)
|
||||
return 0;
|
||||
consume = len - 1;
|
||||
|
||||
msg[2] = msg[1];
|
||||
msg[1] = msg[0];
|
||||
msg[0] = tscm->running_status[port];
|
||||
} else {
|
||||
/* Enough MIDI bytes were not retrieved. */
|
||||
if (consume < len)
|
||||
return 0;
|
||||
consume = len;
|
||||
|
||||
tscm->running_status[port] = msg[0];
|
||||
}
|
||||
}
|
||||
|
||||
*label = (port << 4) | (msg[0] >> 4);
|
||||
}
|
||||
|
||||
if (len > 0 && len < 3)
|
||||
memset(msg + len, 0, 3 - len);
|
||||
|
||||
return consume;
|
||||
}
|
||||
|
||||
static void handle_midi_tx(struct fw_card *card, struct fw_request *request,
|
||||
int tcode, int destination, int source,
|
||||
int generation, unsigned long long offset,
|
||||
void *data, size_t length, void *callback_data)
|
||||
{
|
||||
struct snd_tscm *tscm = callback_data;
|
||||
u32 *buf = (u32 *)data;
|
||||
unsigned int messages;
|
||||
unsigned int i;
|
||||
unsigned int port;
|
||||
struct snd_rawmidi_substream *substream;
|
||||
u8 *b;
|
||||
int bytes;
|
||||
|
||||
if (offset != tscm->async_handler.offset)
|
||||
goto end;
|
||||
|
||||
messages = length / 8;
|
||||
for (i = 0; i < messages; i++) {
|
||||
b = (u8 *)(buf + i * 2);
|
||||
|
||||
port = b[0] >> 4;
|
||||
/* TODO: support virtual MIDI ports. */
|
||||
if (port >= tscm->spec->midi_capture_ports)
|
||||
goto end;
|
||||
|
||||
/* Assume the message length. */
|
||||
bytes = calculate_message_bytes(b[1]);
|
||||
/* On MIDI data or exclusives. */
|
||||
if (bytes <= 0) {
|
||||
/* Seek the end of exclusives. */
|
||||
for (bytes = 1; bytes < 4; bytes++) {
|
||||
if (b[bytes] == 0xf7)
|
||||
break;
|
||||
}
|
||||
if (bytes == 4)
|
||||
bytes = 3;
|
||||
}
|
||||
|
||||
substream = ACCESS_ONCE(tscm->tx_midi_substreams[port]);
|
||||
if (substream != NULL)
|
||||
snd_rawmidi_receive(substream, b + 1, bytes);
|
||||
}
|
||||
end:
|
||||
fw_send_response(card, request, RCODE_COMPLETE);
|
||||
}
|
||||
|
||||
int snd_tscm_transaction_register(struct snd_tscm *tscm)
|
||||
{
|
||||
static const struct fw_address_region resp_register_region = {
|
||||
.start = 0xffffe0000000ull,
|
||||
.end = 0xffffe000ffffull,
|
||||
};
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Usually, two quadlets are transferred by one transaction. The first
|
||||
* quadlet has MIDI messages, the rest includes timestamp.
|
||||
* Sometimes, 8 set of the data is transferred by a block transaction.
|
||||
*/
|
||||
tscm->async_handler.length = 8 * 8;
|
||||
tscm->async_handler.address_callback = handle_midi_tx;
|
||||
tscm->async_handler.callback_data = tscm;
|
||||
|
||||
err = fw_core_add_address_handler(&tscm->async_handler,
|
||||
&resp_register_region);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = snd_tscm_transaction_reregister(tscm);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) {
|
||||
err = snd_fw_async_midi_port_init(
|
||||
&tscm->out_ports[i], tscm->unit,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_RX_QUAD,
|
||||
4, fill_message);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
return err;
|
||||
error:
|
||||
fw_core_remove_address_handler(&tscm->async_handler);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* At bus reset, these registers are cleared. */
|
||||
int snd_tscm_transaction_reregister(struct snd_tscm *tscm)
|
||||
{
|
||||
struct fw_device *device = fw_parent_device(tscm->unit);
|
||||
__be32 reg;
|
||||
int err;
|
||||
|
||||
/* Register messaging address. Block transaction is not allowed. */
|
||||
reg = cpu_to_be32((device->card->node_id << 16) |
|
||||
(tscm->async_handler.offset >> 32));
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
reg = cpu_to_be32(tscm->async_handler.offset);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Turn on messaging. */
|
||||
reg = cpu_to_be32(0x00000001);
|
||||
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
|
||||
®, sizeof(reg), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Turn on FireWire LED. */
|
||||
reg = cpu_to_be32(0x0001008e);
|
||||
return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
|
||||
®, sizeof(reg), 0);
|
||||
}
|
||||
|
||||
void snd_tscm_transaction_unregister(struct snd_tscm *tscm)
|
||||
{
|
||||
__be32 reg;
|
||||
unsigned int i;
|
||||
|
||||
/* Turn off FireWire LED. */
|
||||
reg = cpu_to_be32(0x0000008e);
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
|
||||
®, sizeof(reg), 0);
|
||||
|
||||
/* Turn off messaging. */
|
||||
reg = cpu_to_be32(0x00000000);
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
|
||||
®, sizeof(reg), 0);
|
||||
|
||||
/* Unregister the address. */
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
|
||||
®, sizeof(reg), 0);
|
||||
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||
TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
|
||||
®, sizeof(reg), 0);
|
||||
|
||||
fw_core_remove_address_handler(&tscm->async_handler);
|
||||
for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++)
|
||||
snd_fw_async_midi_port_destroy(&tscm->out_ports[i]);
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* tascam.c - a part of driver for TASCAM FireWire series
|
||||
*
|
||||
* Copyright (c) 2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#include "tascam.h"
|
||||
|
||||
MODULE_DESCRIPTION("TASCAM FireWire series Driver");
|
||||
MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
static struct snd_tscm_spec model_specs[] = {
|
||||
{
|
||||
.name = "FW-1884",
|
||||
.has_adat = true,
|
||||
.has_spdif = true,
|
||||
.pcm_capture_analog_channels = 8,
|
||||
.pcm_playback_analog_channels = 8,
|
||||
.midi_capture_ports = 4,
|
||||
.midi_playback_ports = 4,
|
||||
.is_controller = true,
|
||||
},
|
||||
{
|
||||
.name = "FW-1082",
|
||||
.has_adat = false,
|
||||
.has_spdif = true,
|
||||
.pcm_capture_analog_channels = 8,
|
||||
.pcm_playback_analog_channels = 2,
|
||||
.midi_capture_ports = 2,
|
||||
.midi_playback_ports = 2,
|
||||
.is_controller = true,
|
||||
},
|
||||
/* FW-1804 may be supported. */
|
||||
};
|
||||
|
||||
static int identify_model(struct snd_tscm *tscm)
|
||||
{
|
||||
struct fw_device *fw_dev = fw_parent_device(tscm->unit);
|
||||
const u32 *config_rom = fw_dev->config_rom;
|
||||
char model[9];
|
||||
unsigned int i;
|
||||
u8 c;
|
||||
|
||||
if (fw_dev->config_rom_length < 30) {
|
||||
dev_err(&tscm->unit->device,
|
||||
"Configuration ROM is too short.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Pick up model name from certain addresses. */
|
||||
for (i = 0; i < 8; i++) {
|
||||
c = config_rom[28 + i / 4] >> (24 - 8 * (i % 4));
|
||||
if (c == '\0')
|
||||
break;
|
||||
model[i] = c;
|
||||
}
|
||||
model[i] = '\0';
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(model_specs); i++) {
|
||||
if (strcmp(model, model_specs[i].name) == 0) {
|
||||
tscm->spec = &model_specs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tscm->spec == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
strcpy(tscm->card->driver, "FW-TASCAM");
|
||||
strcpy(tscm->card->shortname, model);
|
||||
strcpy(tscm->card->mixername, model);
|
||||
snprintf(tscm->card->longname, sizeof(tscm->card->longname),
|
||||
"TASCAM %s, GUID %08x%08x at %s, S%d", model,
|
||||
fw_dev->config_rom[3], fw_dev->config_rom[4],
|
||||
dev_name(&tscm->unit->device), 100 << fw_dev->max_speed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tscm_card_free(struct snd_card *card)
|
||||
{
|
||||
struct snd_tscm *tscm = card->private_data;
|
||||
|
||||
snd_tscm_transaction_unregister(tscm);
|
||||
snd_tscm_stream_destroy_duplex(tscm);
|
||||
|
||||
fw_unit_put(tscm->unit);
|
||||
|
||||
mutex_destroy(&tscm->mutex);
|
||||
}
|
||||
|
||||
static int snd_tscm_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct snd_tscm *tscm;
|
||||
int err;
|
||||
|
||||
/* create card */
|
||||
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
|
||||
sizeof(struct snd_tscm), &card);
|
||||
if (err < 0)
|
||||
return err;
|
||||
card->private_free = tscm_card_free;
|
||||
|
||||
/* initialize myself */
|
||||
tscm = card->private_data;
|
||||
tscm->card = card;
|
||||
tscm->unit = fw_unit_get(unit);
|
||||
|
||||
mutex_init(&tscm->mutex);
|
||||
spin_lock_init(&tscm->lock);
|
||||
init_waitqueue_head(&tscm->hwdep_wait);
|
||||
|
||||
err = identify_model(tscm);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
snd_tscm_proc_init(tscm);
|
||||
|
||||
err = snd_tscm_stream_init_duplex(tscm);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_tscm_create_pcm_devices(tscm);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_tscm_transaction_register(tscm);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_tscm_create_midi_devices(tscm);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_tscm_create_hwdep_device(tscm);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
dev_set_drvdata(&unit->device, tscm);
|
||||
|
||||
return err;
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void snd_tscm_update(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
|
||||
|
||||
snd_tscm_transaction_reregister(tscm);
|
||||
|
||||
mutex_lock(&tscm->mutex);
|
||||
snd_tscm_stream_update_duplex(tscm);
|
||||
mutex_unlock(&tscm->mutex);
|
||||
}
|
||||
|
||||
static void snd_tscm_remove(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
|
||||
|
||||
/* No need to wait for releasing card object in this context. */
|
||||
snd_card_free_when_closed(tscm->card);
|
||||
}
|
||||
|
||||
static const struct ieee1394_device_id snd_tscm_id_table[] = {
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_SPECIFIER_ID,
|
||||
.vendor_id = 0x00022e,
|
||||
.specifier_id = 0x00022e,
|
||||
},
|
||||
/* FE-08 requires reverse-engineering because it just has faders. */
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table);
|
||||
|
||||
static struct fw_driver tscm_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "snd-firewire-tascam",
|
||||
.bus = &fw_bus_type,
|
||||
},
|
||||
.probe = snd_tscm_probe,
|
||||
.update = snd_tscm_update,
|
||||
.remove = snd_tscm_remove,
|
||||
.id_table = snd_tscm_id_table,
|
||||
};
|
||||
|
||||
static int __init snd_tscm_init(void)
|
||||
{
|
||||
return driver_register(&tscm_driver.driver);
|
||||
}
|
||||
|
||||
static void __exit snd_tscm_exit(void)
|
||||
{
|
||||
driver_unregister(&tscm_driver.driver);
|
||||
}
|
||||
|
||||
module_init(snd_tscm_init);
|
||||
module_exit(snd_tscm_exit);
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* tascam.h - a part of driver for TASCAM FireWire series
|
||||
*
|
||||
* Copyright (c) 2015 Takashi Sakamoto
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License, version 2.
|
||||
*/
|
||||
|
||||
#ifndef SOUND_TASCAM_H_INCLUDED
|
||||
#define SOUND_TASCAM_H_INCLUDED
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/firewire.h>
|
||||
#include <linux/firewire-constants.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/firewire.h>
|
||||
#include <sound/hwdep.h>
|
||||
#include <sound/rawmidi.h>
|
||||
|
||||
#include "../lib.h"
|
||||
#include "../amdtp-stream.h"
|
||||
#include "../iso-resources.h"
|
||||
|
||||
struct snd_tscm_spec {
|
||||
const char *const name;
|
||||
bool has_adat;
|
||||
bool has_spdif;
|
||||
unsigned int pcm_capture_analog_channels;
|
||||
unsigned int pcm_playback_analog_channels;
|
||||
unsigned int midi_capture_ports;
|
||||
unsigned int midi_playback_ports;
|
||||
bool is_controller;
|
||||
};
|
||||
|
||||
#define TSCM_MIDI_IN_PORT_MAX 4
|
||||
#define TSCM_MIDI_OUT_PORT_MAX 4
|
||||
|
||||
struct snd_tscm {
|
||||
struct snd_card *card;
|
||||
struct fw_unit *unit;
|
||||
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
const struct snd_tscm_spec *spec;
|
||||
|
||||
struct fw_iso_resources tx_resources;
|
||||
struct fw_iso_resources rx_resources;
|
||||
struct amdtp_stream tx_stream;
|
||||
struct amdtp_stream rx_stream;
|
||||
unsigned int substreams_counter;
|
||||
|
||||
int dev_lock_count;
|
||||
bool dev_lock_changed;
|
||||
wait_queue_head_t hwdep_wait;
|
||||
|
||||
/* For MIDI message incoming transactions. */
|
||||
struct fw_address_handler async_handler;
|
||||
struct snd_rawmidi_substream *tx_midi_substreams[TSCM_MIDI_IN_PORT_MAX];
|
||||
|
||||
/* For MIDI message outgoing transactions. */
|
||||
struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX];
|
||||
u8 running_status[TSCM_MIDI_OUT_PORT_MAX];
|
||||
bool on_sysex[TSCM_MIDI_OUT_PORT_MAX];
|
||||
|
||||
/* For control messages. */
|
||||
struct snd_firewire_tascam_status *status;
|
||||
};
|
||||
|
||||
#define TSCM_ADDR_BASE 0xffff00000000ull
|
||||
|
||||
#define TSCM_OFFSET_FIRMWARE_REGISTER 0x0000
|
||||
#define TSCM_OFFSET_FIRMWARE_FPGA 0x0004
|
||||
#define TSCM_OFFSET_FIRMWARE_ARM 0x0008
|
||||
#define TSCM_OFFSET_FIRMWARE_HW 0x000c
|
||||
|
||||
#define TSCM_OFFSET_ISOC_TX_CH 0x0200
|
||||
#define TSCM_OFFSET_UNKNOWN 0x0204
|
||||
#define TSCM_OFFSET_START_STREAMING 0x0208
|
||||
#define TSCM_OFFSET_ISOC_RX_CH 0x020c
|
||||
#define TSCM_OFFSET_ISOC_RX_ON 0x0210 /* Little conviction. */
|
||||
#define TSCM_OFFSET_TX_PCM_CHANNELS 0x0214
|
||||
#define TSCM_OFFSET_RX_PCM_CHANNELS 0x0218
|
||||
#define TSCM_OFFSET_MULTIPLEX_MODE 0x021c
|
||||
#define TSCM_OFFSET_ISOC_TX_ON 0x0220
|
||||
/* Unknown 0x0224 */
|
||||
#define TSCM_OFFSET_CLOCK_STATUS 0x0228
|
||||
#define TSCM_OFFSET_SET_OPTION 0x022c
|
||||
|
||||
#define TSCM_OFFSET_MIDI_TX_ON 0x0300
|
||||
#define TSCM_OFFSET_MIDI_TX_ADDR_HI 0x0304
|
||||
#define TSCM_OFFSET_MIDI_TX_ADDR_LO 0x0308
|
||||
|
||||
#define TSCM_OFFSET_LED_POWER 0x0404
|
||||
|
||||
#define TSCM_OFFSET_MIDI_RX_QUAD 0x4000
|
||||
|
||||
enum snd_tscm_clock {
|
||||
SND_TSCM_CLOCK_INTERNAL = 0,
|
||||
SND_TSCM_CLOCK_WORD = 1,
|
||||
SND_TSCM_CLOCK_SPDIF = 2,
|
||||
SND_TSCM_CLOCK_ADAT = 3,
|
||||
};
|
||||
|
||||
int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir, unsigned int pcm_channels);
|
||||
int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate);
|
||||
int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
|
||||
struct snd_pcm_runtime *runtime);
|
||||
void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format);
|
||||
|
||||
int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate);
|
||||
int snd_tscm_stream_get_clock(struct snd_tscm *tscm,
|
||||
enum snd_tscm_clock *clock);
|
||||
int snd_tscm_stream_init_duplex(struct snd_tscm *tscm);
|
||||
void snd_tscm_stream_update_duplex(struct snd_tscm *tscm);
|
||||
void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm);
|
||||
int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate);
|
||||
void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm);
|
||||
|
||||
void snd_tscm_stream_lock_changed(struct snd_tscm *tscm);
|
||||
int snd_tscm_stream_lock_try(struct snd_tscm *tscm);
|
||||
void snd_tscm_stream_lock_release(struct snd_tscm *tscm);
|
||||
|
||||
int snd_tscm_transaction_register(struct snd_tscm *tscm);
|
||||
int snd_tscm_transaction_reregister(struct snd_tscm *tscm);
|
||||
void snd_tscm_transaction_unregister(struct snd_tscm *tscm);
|
||||
|
||||
void snd_tscm_proc_init(struct snd_tscm *tscm);
|
||||
|
||||
int snd_tscm_create_pcm_devices(struct snd_tscm *tscm);
|
||||
|
||||
int snd_tscm_create_midi_devices(struct snd_tscm *tscm);
|
||||
|
||||
int snd_tscm_create_hwdep_device(struct snd_tscm *tscm);
|
||||
|
||||
#endif
|
||||
@@ -227,7 +227,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup);
|
||||
void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link,
|
||||
int stream)
|
||||
{
|
||||
snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 0);
|
||||
snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_link_set_stream_id);
|
||||
|
||||
@@ -385,14 +385,13 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type)
|
||||
break;
|
||||
|
||||
case HDAC_EXT_STREAM_TYPE_HOST:
|
||||
if (stream->decoupled) {
|
||||
if (stream->decoupled && !stream->link_locked)
|
||||
snd_hdac_ext_stream_decouple(ebus, stream, false);
|
||||
snd_hdac_stream_release(&stream->hstream);
|
||||
}
|
||||
snd_hdac_stream_release(&stream->hstream);
|
||||
break;
|
||||
|
||||
case HDAC_EXT_STREAM_TYPE_LINK:
|
||||
if (stream->decoupled)
|
||||
if (stream->decoupled && !stream->hstream.opened)
|
||||
snd_hdac_ext_stream_decouple(ebus, stream, false);
|
||||
spin_lock_irq(&bus->reg_lock);
|
||||
stream->link_locked = 0;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/export.h>
|
||||
#include <sound/hdaudio.h>
|
||||
|
||||
@@ -63,9 +64,21 @@ static int hda_bus_match(struct device *dev, struct device_driver *drv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int hda_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
char modalias[32];
|
||||
|
||||
snd_hdac_codec_modalias(dev_to_hdac_dev(dev), modalias,
|
||||
sizeof(modalias));
|
||||
if (add_uevent_var(env, "MODALIAS=%s", modalias))
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bus_type snd_hda_bus_type = {
|
||||
.name = "hdaudio",
|
||||
.match = hda_bus_match,
|
||||
.uevent = hda_uevent,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(snd_hda_bus_type);
|
||||
|
||||
|
||||
@@ -172,6 +172,15 @@ static void process_unsol_events(struct work_struct *work)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hdac_bus_add_device - Add a codec to bus
|
||||
* @bus: HDA core bus
|
||||
* @codec: HDA core device to add
|
||||
*
|
||||
* Adds the given codec to the list in the bus. The caddr_tbl array
|
||||
* and codec_powered bits are updated, as well.
|
||||
* Returns zero if success, or a negative error code.
|
||||
*/
|
||||
int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
|
||||
{
|
||||
if (bus->caddr_tbl[codec->addr]) {
|
||||
@@ -188,6 +197,11 @@ int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device);
|
||||
|
||||
/**
|
||||
* snd_hdac_bus_remove_device - Remove a codec from bus
|
||||
* @bus: HDA core bus
|
||||
* @codec: HDA core device to remove
|
||||
*/
|
||||
void snd_hdac_bus_remove_device(struct hdac_bus *bus,
|
||||
struct hdac_device *codec)
|
||||
{
|
||||
|
||||
+122
-2
@@ -163,6 +163,43 @@ void snd_hdac_device_unregister(struct hdac_device *codec)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_device_unregister);
|
||||
|
||||
/**
|
||||
* snd_hdac_device_set_chip_name - set/update the codec name
|
||||
* @codec: the HDAC device
|
||||
* @name: name string to set
|
||||
*
|
||||
* Returns 0 if the name is set or updated, or a negative error code.
|
||||
*/
|
||||
int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name)
|
||||
{
|
||||
char *newname;
|
||||
|
||||
if (!name)
|
||||
return 0;
|
||||
newname = kstrdup(name, GFP_KERNEL);
|
||||
if (!newname)
|
||||
return -ENOMEM;
|
||||
kfree(codec->chip_name);
|
||||
codec->chip_name = newname;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name);
|
||||
|
||||
/**
|
||||
* snd_hdac_codec_modalias - give the module alias name
|
||||
* @codec: HDAC device
|
||||
* @buf: string buffer to store
|
||||
* @size: string buffer size
|
||||
*
|
||||
* Returns the size of string, like snprintf(), or a negative error code.
|
||||
*/
|
||||
int snd_hdac_codec_modalias(struct hdac_device *codec, char *buf, size_t size)
|
||||
{
|
||||
return snprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n",
|
||||
codec->vendor_id, codec->revision_id, codec->type);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
|
||||
|
||||
/**
|
||||
* snd_hdac_make_cmd - compose a 32bit command word to be sent to the
|
||||
* HD-audio controller
|
||||
@@ -592,8 +629,10 @@ int snd_hdac_power_down_pm(struct hdac_device *codec)
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enable/disable the link power for a codec.
|
||||
/**
|
||||
* snd_hdac_link_power - Enable/disable the link power for a codec
|
||||
* @codec: the codec object
|
||||
* @bool: enable or disable the link power
|
||||
*/
|
||||
int snd_hdac_link_power(struct hdac_device *codec, bool enable)
|
||||
{
|
||||
@@ -952,3 +991,84 @@ bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid,
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format);
|
||||
|
||||
static unsigned int codec_read(struct hdac_device *hdac, hda_nid_t nid,
|
||||
int flags, unsigned int verb, unsigned int parm)
|
||||
{
|
||||
unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
|
||||
unsigned int res;
|
||||
|
||||
if (snd_hdac_exec_verb(hdac, cmd, flags, &res))
|
||||
return -1;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int codec_write(struct hdac_device *hdac, hda_nid_t nid,
|
||||
int flags, unsigned int verb, unsigned int parm)
|
||||
{
|
||||
unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
|
||||
|
||||
return snd_hdac_exec_verb(hdac, cmd, flags, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hdac_codec_read - send a command and get the response
|
||||
* @hdac: the HDAC device
|
||||
* @nid: NID to send the command
|
||||
* @flags: optional bit flags
|
||||
* @verb: the verb to send
|
||||
* @parm: the parameter for the verb
|
||||
*
|
||||
* Send a single command and read the corresponding response.
|
||||
*
|
||||
* Returns the obtained response value, or -1 for an error.
|
||||
*/
|
||||
int snd_hdac_codec_read(struct hdac_device *hdac, hda_nid_t nid,
|
||||
int flags, unsigned int verb, unsigned int parm)
|
||||
{
|
||||
return codec_read(hdac, nid, flags, verb, parm);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_codec_read);
|
||||
|
||||
/**
|
||||
* snd_hdac_codec_write - send a single command without waiting for response
|
||||
* @hdac: the HDAC device
|
||||
* @nid: NID to send the command
|
||||
* @flags: optional bit flags
|
||||
* @verb: the verb to send
|
||||
* @parm: the parameter for the verb
|
||||
*
|
||||
* Send a single command without waiting for response.
|
||||
*
|
||||
* Returns 0 if successful, or a negative error code.
|
||||
*/
|
||||
int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid,
|
||||
int flags, unsigned int verb, unsigned int parm)
|
||||
{
|
||||
return codec_write(hdac, nid, flags, verb, parm);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_codec_write);
|
||||
|
||||
/**
|
||||
* snd_hdac_check_power_state - check whether the actual power state matches
|
||||
* with the target state
|
||||
*
|
||||
* @hdac: the HDAC device
|
||||
* @nid: NID to send the command
|
||||
* @target_state: target state to check for
|
||||
*
|
||||
* Return true if state matches, false if not
|
||||
*/
|
||||
bool snd_hdac_check_power_state(struct hdac_device *hdac,
|
||||
hda_nid_t nid, unsigned int target_state)
|
||||
{
|
||||
unsigned int state = codec_read(hdac, nid, 0,
|
||||
AC_VERB_GET_POWER_STATE, 0);
|
||||
|
||||
if (state & AC_PWRST_ERROR)
|
||||
return true;
|
||||
state = (state >> 4) & 0x0f;
|
||||
return (state == target_state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_check_power_state);
|
||||
|
||||
@@ -23,6 +23,19 @@
|
||||
|
||||
static struct i915_audio_component *hdac_acomp;
|
||||
|
||||
/**
|
||||
* snd_hdac_set_codec_wakeup - Enable / disable HDMI/DP codec wakeup
|
||||
* @bus: HDA core bus
|
||||
* @enable: enable or disable the wakeup
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with i915 graphics.
|
||||
*
|
||||
* This function should be called during the chip reset, also called at
|
||||
* resume for updating STATESTS register read.
|
||||
*
|
||||
* Returns zero for success or a negative error code.
|
||||
*/
|
||||
int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
|
||||
{
|
||||
struct i915_audio_component *acomp = bus->audio_component;
|
||||
@@ -45,6 +58,19 @@ int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_set_codec_wakeup);
|
||||
|
||||
/**
|
||||
* snd_hdac_display_power - Power up / down the power refcount
|
||||
* @bus: HDA core bus
|
||||
* @enable: power up or down
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with i915 graphics.
|
||||
*
|
||||
* This function manages a refcount and calls the i915 get_power() and
|
||||
* put_power() ops accordingly, toggling the codec wakeup, too.
|
||||
*
|
||||
* Returns zero for success or a negative error code.
|
||||
*/
|
||||
int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
|
||||
{
|
||||
struct i915_audio_component *acomp = bus->audio_component;
|
||||
@@ -71,6 +97,16 @@ int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_display_power);
|
||||
|
||||
/**
|
||||
* snd_hdac_get_display_clk - Get CDCLK in kHz
|
||||
* @bus: HDA core bus
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with i915 graphics.
|
||||
*
|
||||
* This function queries CDCLK value in kHz from the graphics driver and
|
||||
* returns the value. A negative code is returned in error.
|
||||
*/
|
||||
int snd_hdac_get_display_clk(struct hdac_bus *bus)
|
||||
{
|
||||
struct i915_audio_component *acomp = bus->audio_component;
|
||||
@@ -134,6 +170,17 @@ static int hdac_component_master_match(struct device *dev, void *data)
|
||||
return !strcmp(dev->driver->name, "i915");
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hdac_i915_register_notifier - Register i915 audio component ops
|
||||
* @aops: i915 audio component ops
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with i915 graphics.
|
||||
*
|
||||
* This function sets the given ops to be called by the i915 graphics driver.
|
||||
*
|
||||
* Returns zero for success or a negative error code.
|
||||
*/
|
||||
int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops *aops)
|
||||
{
|
||||
if (WARN_ON(!hdac_acomp))
|
||||
@@ -144,6 +191,18 @@ int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_i915_register_notifier);
|
||||
|
||||
/**
|
||||
* snd_hdac_i915_init - Initialize i915 audio component
|
||||
* @bus: HDA core bus
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with i915 graphics.
|
||||
*
|
||||
* This function initializes and sets up the audio component to communicate
|
||||
* with i915 graphics driver.
|
||||
*
|
||||
* Returns zero for success or a negative error code.
|
||||
*/
|
||||
int snd_hdac_i915_init(struct hdac_bus *bus)
|
||||
{
|
||||
struct component_match *match = NULL;
|
||||
@@ -187,6 +246,17 @@ out_err:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_i915_init);
|
||||
|
||||
/**
|
||||
* snd_hdac_i915_exit - Finalize i915 audio component
|
||||
* @bus: HDA core bus
|
||||
*
|
||||
* This function is supposed to be used only by a HD-audio controller
|
||||
* driver that needs the interaction with i915 graphics.
|
||||
*
|
||||
* This function releases the i915 audio component that has been used.
|
||||
*
|
||||
* Returns zero for success or a negative error code.
|
||||
*/
|
||||
int snd_hdac_i915_exit(struct hdac_bus *bus)
|
||||
{
|
||||
struct device *dev = bus->dev;
|
||||
|
||||
@@ -339,6 +339,12 @@ static const struct regmap_config hda_regmap_cfg = {
|
||||
.use_single_rw = true,
|
||||
};
|
||||
|
||||
/**
|
||||
* snd_hdac_regmap_init - Initialize regmap for HDA register accesses
|
||||
* @codec: the codec object
|
||||
*
|
||||
* Returns zero for success or a negative error code.
|
||||
*/
|
||||
int snd_hdac_regmap_init(struct hdac_device *codec)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
@@ -352,6 +358,10 @@ int snd_hdac_regmap_init(struct hdac_device *codec)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_regmap_init);
|
||||
|
||||
/**
|
||||
* snd_hdac_regmap_init - Release the regmap from HDA codec
|
||||
* @codec: the codec object
|
||||
*/
|
||||
void snd_hdac_regmap_exit(struct hdac_device *codec)
|
||||
{
|
||||
if (codec->regmap) {
|
||||
|
||||
@@ -426,7 +426,8 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods);
|
||||
|
||||
/* snd_hdac_stream_set_params - set stream parameters
|
||||
/**
|
||||
* snd_hdac_stream_set_params - set stream parameters
|
||||
* @azx_dev: HD-audio core stream for which parameters are to be set
|
||||
* @format_val: format value parameter
|
||||
*
|
||||
|
||||
@@ -45,6 +45,13 @@ CODEC_ATTR(mfg);
|
||||
CODEC_ATTR_STR(vendor_name);
|
||||
CODEC_ATTR_STR(chip_name);
|
||||
|
||||
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snd_hdac_codec_modalias(dev_to_hdac_dev(dev), buf, 256);
|
||||
}
|
||||
static DEVICE_ATTR_RO(modalias);
|
||||
|
||||
static struct attribute *hdac_dev_attrs[] = {
|
||||
&dev_attr_type.attr,
|
||||
&dev_attr_vendor_id.attr,
|
||||
@@ -54,6 +61,7 @@ static struct attribute *hdac_dev_attrs[] = {
|
||||
&dev_attr_mfg.attr,
|
||||
&dev_attr_vendor_name.attr,
|
||||
&dev_attr_chip_name.attr,
|
||||
&dev_attr_modalias.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
@@ -1864,7 +1864,7 @@ int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device)
|
||||
/* global setup */
|
||||
pcm->info_flags = 0;
|
||||
strcpy(pcm->name, "CS46xx - IEC958");
|
||||
chip->pcm_rear = pcm;
|
||||
chip->pcm_iec958 = pcm;
|
||||
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
||||
snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
|
||||
@@ -2528,7 +2528,7 @@ int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
|
||||
#ifdef CONFIG_SND_CS46XX_NEW_DSP
|
||||
if (chip->nr_ac97_codecs == 1) {
|
||||
unsigned int id2 = chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]->id & 0xffff;
|
||||
if (id2 == 0x592b || id2 == 0x592d) {
|
||||
if ((id2 & 0xfff0) == 0x5920) { /* CS4294 and CS4298 */
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&snd_cs46xx_front_dup_ctl, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
@@ -3780,6 +3780,11 @@ static int snd_cs46xx_suspend(struct device *dev)
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
chip->in_suspend = 1;
|
||||
snd_pcm_suspend_all(chip->pcm);
|
||||
#ifdef CONFIG_SND_CS46XX_NEW_DSP
|
||||
snd_pcm_suspend_all(chip->pcm_rear);
|
||||
snd_pcm_suspend_all(chip->pcm_center_lfe);
|
||||
snd_pcm_suspend_all(chip->pcm_iec958);
|
||||
#endif
|
||||
// chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL);
|
||||
// chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE);
|
||||
|
||||
|
||||
+43
-26
@@ -15,21 +15,22 @@
|
||||
#include "hda_local.h"
|
||||
|
||||
/*
|
||||
* find a matching codec preset
|
||||
* find a matching codec id
|
||||
*/
|
||||
static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv)
|
||||
{
|
||||
struct hda_codec *codec = container_of(dev, struct hda_codec, core);
|
||||
struct hda_codec_driver *driver =
|
||||
container_of(drv, struct hda_codec_driver, core);
|
||||
const struct hda_codec_preset *preset;
|
||||
const struct hda_device_id *list;
|
||||
/* check probe_id instead of vendor_id if set */
|
||||
u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id;
|
||||
u32 rev_id = codec->core.revision_id;
|
||||
|
||||
for (preset = driver->preset; preset->id; preset++) {
|
||||
if (preset->id == id &&
|
||||
(!preset->rev || preset->rev == codec->core.revision_id)) {
|
||||
codec->preset = preset;
|
||||
for (list = driver->id; list->vendor_id; list++) {
|
||||
if (list->vendor_id == id &&
|
||||
(!list->rev_id || list->rev_id == rev_id)) {
|
||||
codec->preset = list;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -45,26 +46,45 @@ static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
|
||||
codec->patch_ops.unsol_event(codec, ev);
|
||||
}
|
||||
|
||||
/* reset the codec name from the preset */
|
||||
static int codec_refresh_name(struct hda_codec *codec, const char *name)
|
||||
/**
|
||||
* snd_hda_codec_set_name - set the codec name
|
||||
* @codec: the HDA codec
|
||||
* @name: name string to set
|
||||
*/
|
||||
int snd_hda_codec_set_name(struct hda_codec *codec, const char *name)
|
||||
{
|
||||
if (name) {
|
||||
kfree(codec->core.chip_name);
|
||||
codec->core.chip_name = kstrdup(name, GFP_KERNEL);
|
||||
int err;
|
||||
|
||||
if (!name)
|
||||
return 0;
|
||||
err = snd_hdac_device_set_chip_name(&codec->core, name);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* update the mixer name */
|
||||
if (!*codec->card->mixername ||
|
||||
codec->bus->mixer_assigned >= codec->core.addr) {
|
||||
snprintf(codec->card->mixername,
|
||||
sizeof(codec->card->mixername), "%s %s",
|
||||
codec->core.vendor_name, codec->core.chip_name);
|
||||
codec->bus->mixer_assigned = codec->core.addr;
|
||||
}
|
||||
return codec->core.chip_name ? 0 : -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_codec_set_name);
|
||||
|
||||
static int hda_codec_driver_probe(struct device *dev)
|
||||
{
|
||||
struct hda_codec *codec = dev_to_hda_codec(dev);
|
||||
struct module *owner = dev->driver->owner;
|
||||
hda_codec_patch_t patch;
|
||||
int err;
|
||||
|
||||
if (WARN_ON(!codec->preset))
|
||||
return -EINVAL;
|
||||
|
||||
err = codec_refresh_name(codec, codec->preset->name);
|
||||
err = snd_hda_codec_set_name(codec, codec->preset->name);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = snd_hdac_regmap_init(&codec->core);
|
||||
@@ -76,9 +96,12 @@ static int hda_codec_driver_probe(struct device *dev)
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = codec->preset->patch(codec);
|
||||
if (err < 0)
|
||||
goto error_module;
|
||||
patch = (hda_codec_patch_t)codec->preset->driver_data;
|
||||
if (patch) {
|
||||
err = patch(codec);
|
||||
if (err < 0)
|
||||
goto error_module;
|
||||
}
|
||||
|
||||
err = snd_hda_codec_build_pcms(codec);
|
||||
if (err < 0)
|
||||
@@ -155,11 +178,10 @@ static inline bool codec_probed(struct hda_codec *codec)
|
||||
static void codec_bind_module(struct hda_codec *codec)
|
||||
{
|
||||
#ifdef MODULE
|
||||
request_module("snd-hda-codec-id:%08x", codec->core.vendor_id);
|
||||
if (codec_probed(codec))
|
||||
return;
|
||||
request_module("snd-hda-codec-id:%04x*",
|
||||
(codec->core.vendor_id >> 16) & 0xffff);
|
||||
char modalias[32];
|
||||
|
||||
snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias));
|
||||
request_module(modalias);
|
||||
if (codec_probed(codec))
|
||||
return;
|
||||
#endif
|
||||
@@ -251,11 +273,6 @@ int snd_hda_codec_configure(struct hda_codec *codec)
|
||||
}
|
||||
}
|
||||
|
||||
/* audio codec should override the mixer name */
|
||||
if (codec->core.afg || !*codec->card->mixername)
|
||||
snprintf(codec->card->mixername,
|
||||
sizeof(codec->card->mixername), "%s %s",
|
||||
codec->core.vendor_name, codec->core.chip_name);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
|
||||
@@ -90,50 +90,6 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd,
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_codec_read - send a command and get the response
|
||||
* @codec: the HDA codec
|
||||
* @nid: NID to send the command
|
||||
* @flags: optional bit flags
|
||||
* @verb: the verb to send
|
||||
* @parm: the parameter for the verb
|
||||
*
|
||||
* Send a single command and read the corresponding response.
|
||||
*
|
||||
* Returns the obtained response value, or -1 for an error.
|
||||
*/
|
||||
unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
|
||||
int flags,
|
||||
unsigned int verb, unsigned int parm)
|
||||
{
|
||||
unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm);
|
||||
unsigned int res;
|
||||
if (snd_hdac_exec_verb(&codec->core, cmd, flags, &res))
|
||||
return -1;
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_codec_read);
|
||||
|
||||
/**
|
||||
* snd_hda_codec_write - send a single command without waiting for response
|
||||
* @codec: the HDA codec
|
||||
* @nid: NID to send the command
|
||||
* @flags: optional bit flags
|
||||
* @verb: the verb to send
|
||||
* @parm: the parameter for the verb
|
||||
*
|
||||
* Send a single command without waiting for response.
|
||||
*
|
||||
* Returns 0 if successful, or a negative error code.
|
||||
*/
|
||||
int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
|
||||
unsigned int verb, unsigned int parm)
|
||||
{
|
||||
unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm);
|
||||
return snd_hdac_exec_verb(&codec->core, cmd, flags, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hda_codec_write);
|
||||
|
||||
/**
|
||||
* snd_hda_sequence_write - sequence writes
|
||||
* @codec: the HDA codec
|
||||
|
||||
+28
-16
@@ -22,6 +22,7 @@
|
||||
#define __SOUND_HDA_CODEC_H
|
||||
|
||||
#include <linux/kref.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/pcm.h>
|
||||
@@ -69,6 +70,7 @@ struct hda_bus {
|
||||
unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
|
||||
|
||||
int primary_dig_out_type; /* primary digital out PCM type */
|
||||
unsigned int mixer_assigned; /* codec addr for mixer name */
|
||||
};
|
||||
|
||||
/* from hdac_bus to hda_bus */
|
||||
@@ -80,19 +82,21 @@ struct hda_bus {
|
||||
* Known codecs have the patch to build and set up the controls/PCMs
|
||||
* better than the generic parser.
|
||||
*/
|
||||
struct hda_codec_preset {
|
||||
unsigned int id;
|
||||
unsigned int rev;
|
||||
const char *name;
|
||||
int (*patch)(struct hda_codec *codec);
|
||||
};
|
||||
typedef int (*hda_codec_patch_t)(struct hda_codec *);
|
||||
|
||||
#define HDA_CODEC_ID_GENERIC_HDMI 0x00000101
|
||||
#define HDA_CODEC_ID_GENERIC 0x00000201
|
||||
|
||||
#define HDA_CODEC_REV_ENTRY(_vid, _rev, _name, _patch) \
|
||||
{ .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \
|
||||
.api_version = HDA_DEV_LEGACY, \
|
||||
.driver_data = (unsigned long)(_patch) }
|
||||
#define HDA_CODEC_ENTRY(_vid, _name, _patch) \
|
||||
HDA_CODEC_REV_ENTRY(_vid, 0, _name, _patch)
|
||||
|
||||
struct hda_codec_driver {
|
||||
struct hdac_driver core;
|
||||
const struct hda_codec_preset *preset;
|
||||
const struct hda_device_id *id;
|
||||
};
|
||||
|
||||
int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
|
||||
@@ -183,7 +187,7 @@ struct hda_codec {
|
||||
u32 probe_id; /* overridden id for probing */
|
||||
|
||||
/* detected preset */
|
||||
const struct hda_codec_preset *preset;
|
||||
const struct hda_device_id *preset;
|
||||
const char *modelname; /* model name for preset */
|
||||
|
||||
/* set by patch */
|
||||
@@ -297,10 +301,6 @@ struct hda_codec {
|
||||
/*
|
||||
* constructors
|
||||
*/
|
||||
int snd_hda_bus_new(struct snd_card *card,
|
||||
const struct hdac_bus_ops *ops,
|
||||
const struct hdac_io_ops *io_ops,
|
||||
struct hda_bus **busp);
|
||||
int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
|
||||
unsigned int codec_addr, struct hda_codec **codecp);
|
||||
int snd_hda_codec_configure(struct hda_codec *codec);
|
||||
@@ -309,11 +309,21 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec);
|
||||
/*
|
||||
* low level functions
|
||||
*/
|
||||
unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
|
||||
static inline unsigned int
|
||||
snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
|
||||
int flags,
|
||||
unsigned int verb, unsigned int parm);
|
||||
int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
|
||||
unsigned int verb, unsigned int parm);
|
||||
unsigned int verb, unsigned int parm)
|
||||
{
|
||||
return snd_hdac_codec_read(&codec->core, nid, flags, verb, parm);
|
||||
}
|
||||
|
||||
static inline int
|
||||
snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
|
||||
unsigned int verb, unsigned int parm)
|
||||
{
|
||||
return snd_hdac_codec_write(&codec->core, nid, flags, verb, parm);
|
||||
}
|
||||
|
||||
#define snd_hda_param_read(codec, nid, param) \
|
||||
snd_hdac_read_parm(&(codec)->core, nid, param)
|
||||
#define snd_hda_get_sub_nodes(codec, nid, start_nid) \
|
||||
@@ -453,6 +463,8 @@ void snd_hda_unlock_devices(struct hda_bus *bus);
|
||||
void snd_hda_bus_reset(struct hda_bus *bus);
|
||||
void snd_hda_bus_reset_codecs(struct hda_bus *bus);
|
||||
|
||||
int snd_hda_codec_set_name(struct hda_codec *codec, const char *name);
|
||||
|
||||
/*
|
||||
* power management
|
||||
*/
|
||||
|
||||
@@ -1045,6 +1045,7 @@ int azx_bus_init(struct azx *chip, const char *model,
|
||||
mutex_init(&bus->prepare_mutex);
|
||||
bus->pci = chip->pci;
|
||||
bus->modelname = model;
|
||||
bus->mixer_assigned = -1;
|
||||
bus->core.snoop = azx_snoop(chip);
|
||||
if (chip->get_position[0] != azx_get_pos_lpib ||
|
||||
chip->get_position[1] != azx_get_pos_lpib)
|
||||
@@ -1059,6 +1060,9 @@ int azx_bus_init(struct azx *chip, const char *model,
|
||||
bus->needs_damn_long_delay = 1;
|
||||
}
|
||||
|
||||
if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY)
|
||||
bus->core.align_bdle_4k = true;
|
||||
|
||||
/* AMD chipsets often cause the communication stalls upon certain
|
||||
* sequence like the pin-detection. It seems that forcing the synced
|
||||
* access works around the stall. Grrr...
|
||||
|
||||
@@ -5877,13 +5877,14 @@ error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct hda_codec_preset snd_hda_preset_generic[] = {
|
||||
{ .id = HDA_CODEC_ID_GENERIC, .patch = snd_hda_parse_generic_codec },
|
||||
static const struct hda_device_id snd_hda_id_generic[] = {
|
||||
HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec),
|
||||
{} /* terminator */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic);
|
||||
|
||||
static struct hda_codec_driver generic_driver = {
|
||||
.preset = snd_hda_preset_generic,
|
||||
.id = snd_hda_id_generic,
|
||||
};
|
||||
|
||||
module_hda_codec_driver(generic_driver);
|
||||
|
||||
@@ -334,6 +334,7 @@ enum {
|
||||
|
||||
#define AZX_DCAPS_PRESET_CTHDA \
|
||||
(AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB |\
|
||||
AZX_DCAPS_NO_64BIT |\
|
||||
AZX_DCAPS_4K_BDLE_BOUNDARY | AZX_DCAPS_SNOOP_OFF)
|
||||
|
||||
/*
|
||||
@@ -2104,6 +2105,11 @@ static const struct pci_device_id azx_ids[] = {
|
||||
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
|
||||
{ PCI_DEVICE(0x8086, 0x8d21),
|
||||
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
|
||||
/* Lewisburg */
|
||||
{ PCI_DEVICE(0x8086, 0xa1f0),
|
||||
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
|
||||
{ PCI_DEVICE(0x8086, 0xa270),
|
||||
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
|
||||
/* Lynx Point-LP */
|
||||
{ PCI_DEVICE(0x8086, 0x9c20),
|
||||
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
|
||||
@@ -2284,11 +2290,13 @@ static const struct pci_device_id azx_ids[] = {
|
||||
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
|
||||
.class_mask = 0xffffff,
|
||||
.driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
|
||||
AZX_DCAPS_NO_64BIT |
|
||||
AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB },
|
||||
#else
|
||||
/* this entry seems still valid -- i.e. without emu20kx chip */
|
||||
{ PCI_DEVICE(0x1102, 0x0009),
|
||||
.driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
|
||||
AZX_DCAPS_NO_64BIT |
|
||||
AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB },
|
||||
#endif
|
||||
/* CM8888 */
|
||||
|
||||
@@ -681,12 +681,7 @@ static inline bool
|
||||
snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
|
||||
unsigned int target_state)
|
||||
{
|
||||
unsigned int state = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_POWER_STATE, 0);
|
||||
if (state & AC_PWRST_ERROR)
|
||||
return true;
|
||||
state = (state >> 4) & 0x0f;
|
||||
return (state == target_state);
|
||||
return snd_hdac_check_power_state(&codec->core, nid, target_state);
|
||||
}
|
||||
|
||||
unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
|
||||
|
||||
@@ -595,8 +595,7 @@ static void parse_model_mode(char *buf, struct hda_bus *bus,
|
||||
static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
|
||||
struct hda_codec **codecp)
|
||||
{
|
||||
kfree((*codecp)->core.chip_name);
|
||||
(*codecp)->core.chip_name = kstrdup(buf, GFP_KERNEL);
|
||||
snd_hda_codec_set_name(*codecp, buf);
|
||||
}
|
||||
|
||||
#define DEFINE_PARSE_ID_MODE(name) \
|
||||
|
||||
@@ -1165,32 +1165,31 @@ static int patch_ad1882(struct hda_codec *codec)
|
||||
/*
|
||||
* patch entries
|
||||
*/
|
||||
static const struct hda_codec_preset snd_hda_preset_analog[] = {
|
||||
{ .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884 },
|
||||
{ .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
|
||||
{ .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884 },
|
||||
{ .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
|
||||
{ .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884 },
|
||||
{ .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884 },
|
||||
{ .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
|
||||
{ .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
|
||||
{ .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1884 },
|
||||
{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
|
||||
{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
|
||||
{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
|
||||
{ .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
|
||||
{ .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
|
||||
{ .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
|
||||
static const struct hda_device_id snd_hda_id_analog[] = {
|
||||
HDA_CODEC_ENTRY(0x11d4184a, "AD1884A", patch_ad1884),
|
||||
HDA_CODEC_ENTRY(0x11d41882, "AD1882", patch_ad1882),
|
||||
HDA_CODEC_ENTRY(0x11d41883, "AD1883", patch_ad1884),
|
||||
HDA_CODEC_ENTRY(0x11d41884, "AD1884", patch_ad1884),
|
||||
HDA_CODEC_ENTRY(0x11d4194a, "AD1984A", patch_ad1884),
|
||||
HDA_CODEC_ENTRY(0x11d4194b, "AD1984B", patch_ad1884),
|
||||
HDA_CODEC_ENTRY(0x11d41981, "AD1981", patch_ad1981),
|
||||
HDA_CODEC_ENTRY(0x11d41983, "AD1983", patch_ad1983),
|
||||
HDA_CODEC_ENTRY(0x11d41984, "AD1984", patch_ad1884),
|
||||
HDA_CODEC_ENTRY(0x11d41986, "AD1986A", patch_ad1986a),
|
||||
HDA_CODEC_ENTRY(0x11d41988, "AD1988", patch_ad1988),
|
||||
HDA_CODEC_ENTRY(0x11d4198b, "AD1988B", patch_ad1988),
|
||||
HDA_CODEC_ENTRY(0x11d4882a, "AD1882A", patch_ad1882),
|
||||
HDA_CODEC_ENTRY(0x11d4989a, "AD1989A", patch_ad1988),
|
||||
HDA_CODEC_ENTRY(0x11d4989b, "AD1989B", patch_ad1988),
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:11d4*");
|
||||
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_analog);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Analog Devices HD-audio codec");
|
||||
|
||||
static struct hda_codec_driver analog_driver = {
|
||||
.preset = snd_hda_preset_analog,
|
||||
.id = snd_hda_id_analog,
|
||||
};
|
||||
|
||||
module_hda_codec_driver(analog_driver);
|
||||
|
||||
@@ -83,22 +83,19 @@ static int patch_ca0110(struct hda_codec *codec)
|
||||
/*
|
||||
* patch entries
|
||||
*/
|
||||
static const struct hda_codec_preset snd_hda_preset_ca0110[] = {
|
||||
{ .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
|
||||
{ .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
|
||||
{ .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },
|
||||
static const struct hda_device_id snd_hda_id_ca0110[] = {
|
||||
HDA_CODEC_ENTRY(0x1102000a, "CA0110-IBG", patch_ca0110),
|
||||
HDA_CODEC_ENTRY(0x1102000b, "CA0110-IBG", patch_ca0110),
|
||||
HDA_CODEC_ENTRY(0x1102000d, "SB0880 X-Fi", patch_ca0110),
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:1102000a");
|
||||
MODULE_ALIAS("snd-hda-codec-id:1102000b");
|
||||
MODULE_ALIAS("snd-hda-codec-id:1102000d");
|
||||
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0110);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
|
||||
|
||||
static struct hda_codec_driver ca0110_driver = {
|
||||
.preset = snd_hda_preset_ca0110,
|
||||
.id = snd_hda_id_ca0110,
|
||||
};
|
||||
|
||||
module_hda_codec_driver(ca0110_driver);
|
||||
|
||||
@@ -2673,13 +2673,13 @@ static bool dspload_wait_loaded(struct hda_codec *codec)
|
||||
|
||||
do {
|
||||
if (dspload_is_loaded(codec)) {
|
||||
pr_info("ca0132 DOWNLOAD OK :-) DSP IS RUNNING.\n");
|
||||
codec_info(codec, "ca0132 DSP downloaded and running\n");
|
||||
return true;
|
||||
}
|
||||
msleep(20);
|
||||
} while (time_before(jiffies, timeout));
|
||||
|
||||
pr_err("ca0132 DOWNLOAD FAILED!!! DSP IS NOT RUNNING.\n");
|
||||
codec_err(codec, "ca0132 failed to download DSP\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4375,7 +4375,7 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec)
|
||||
|
||||
dsp_os_image = (struct dsp_image_seg *)(fw_entry->data);
|
||||
if (dspload_image(codec, dsp_os_image, 0, 0, true, 0)) {
|
||||
pr_err("ca0132 dspload_image failed.\n");
|
||||
codec_err(codec, "ca0132 DSP load image failed\n");
|
||||
goto exit_download;
|
||||
}
|
||||
|
||||
@@ -4778,18 +4778,17 @@ static int patch_ca0132(struct hda_codec *codec)
|
||||
/*
|
||||
* patch entries
|
||||
*/
|
||||
static struct hda_codec_preset snd_hda_preset_ca0132[] = {
|
||||
{ .id = 0x11020011, .name = "CA0132", .patch = patch_ca0132 },
|
||||
static struct hda_device_id snd_hda_id_ca0132[] = {
|
||||
HDA_CODEC_ENTRY(0x11020011, "CA0132", patch_ca0132),
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:11020011");
|
||||
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0132);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Creative Sound Core3D codec");
|
||||
|
||||
static struct hda_codec_driver ca0132_driver = {
|
||||
.preset = snd_hda_preset_ca0132,
|
||||
.id = snd_hda_id_ca0132,
|
||||
};
|
||||
|
||||
module_hda_codec_driver(ca0132_driver);
|
||||
|
||||
@@ -570,6 +570,7 @@ static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid)
|
||||
return NULL;
|
||||
codec->spec = spec;
|
||||
spec->vendor_nid = vendor_nid;
|
||||
codec->power_save_node = 1;
|
||||
snd_hda_gen_spec_init(&spec->gen);
|
||||
|
||||
return spec;
|
||||
@@ -1200,26 +1201,21 @@ static int patch_cs4213(struct hda_codec *codec)
|
||||
/*
|
||||
* patch entries
|
||||
*/
|
||||
static const struct hda_codec_preset snd_hda_preset_cirrus[] = {
|
||||
{ .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x },
|
||||
{ .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x },
|
||||
{ .id = 0x10134208, .name = "CS4208", .patch = patch_cs4208 },
|
||||
{ .id = 0x10134210, .name = "CS4210", .patch = patch_cs4210 },
|
||||
{ .id = 0x10134213, .name = "CS4213", .patch = patch_cs4213 },
|
||||
static const struct hda_device_id snd_hda_id_cirrus[] = {
|
||||
HDA_CODEC_ENTRY(0x10134206, "CS4206", patch_cs420x),
|
||||
HDA_CODEC_ENTRY(0x10134207, "CS4207", patch_cs420x),
|
||||
HDA_CODEC_ENTRY(0x10134208, "CS4208", patch_cs4208),
|
||||
HDA_CODEC_ENTRY(0x10134210, "CS4210", patch_cs4210),
|
||||
HDA_CODEC_ENTRY(0x10134213, "CS4213", patch_cs4213),
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:10134206");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10134207");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10134208");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10134210");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10134213");
|
||||
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cirrus);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
|
||||
|
||||
static struct hda_codec_driver cirrus_driver = {
|
||||
.preset = snd_hda_preset_cirrus,
|
||||
.id = snd_hda_id_cirrus,
|
||||
};
|
||||
|
||||
module_hda_codec_driver(cirrus_driver);
|
||||
|
||||
@@ -123,22 +123,19 @@ static int patch_cmi8888(struct hda_codec *codec)
|
||||
/*
|
||||
* patch entries
|
||||
*/
|
||||
static const struct hda_codec_preset snd_hda_preset_cmedia[] = {
|
||||
{ .id = 0x13f68888, .name = "CMI8888", .patch = patch_cmi8888 },
|
||||
{ .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
|
||||
{ .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
|
||||
static const struct hda_device_id snd_hda_id_cmedia[] = {
|
||||
HDA_CODEC_ENTRY(0x13f68888, "CMI8888", patch_cmi8888),
|
||||
HDA_CODEC_ENTRY(0x13f69880, "CMI9880", patch_cmi9880),
|
||||
HDA_CODEC_ENTRY(0x434d4980, "CMI9880", patch_cmi9880),
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:13f68888");
|
||||
MODULE_ALIAS("snd-hda-codec-id:13f69880");
|
||||
MODULE_ALIAS("snd-hda-codec-id:434d4980");
|
||||
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("C-Media HD-audio codec");
|
||||
|
||||
static struct hda_codec_driver cmedia_driver = {
|
||||
.preset = snd_hda_preset_cmedia,
|
||||
.id = snd_hda_id_cmedia,
|
||||
};
|
||||
|
||||
module_hda_codec_driver(cmedia_driver);
|
||||
|
||||
@@ -954,100 +954,44 @@ static int patch_conexant_auto(struct hda_codec *codec)
|
||||
/*
|
||||
*/
|
||||
|
||||
static const struct hda_codec_preset snd_hda_preset_conexant[] = {
|
||||
{ .id = 0x14f15045, .name = "CX20549 (Venice)",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f15047, .name = "CX20551 (Waikiki)",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f15051, .name = "CX20561 (Hermosa)",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f15066, .name = "CX20582 (Pebble)",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f15068, .name = "CX20584",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f15069, .name = "CX20585",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f1506c, .name = "CX20588",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f1506e, .name = "CX20590",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f15097, .name = "CX20631",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f15098, .name = "CX20632",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f150a1, .name = "CX20641",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f150a2, .name = "CX20642",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f150ab, .name = "CX20651",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f150ac, .name = "CX20652",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f150b8, .name = "CX20664",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f150b9, .name = "CX20665",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f150f1, .name = "CX20721",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f150f2, .name = "CX20722",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f150f3, .name = "CX20723",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f150f4, .name = "CX20724",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f1510f, .name = "CX20751/2",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f15110, .name = "CX20751/2",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f15111, .name = "CX20753/4",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f15113, .name = "CX20755",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f15114, .name = "CX20756",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f15115, .name = "CX20757",
|
||||
.patch = patch_conexant_auto },
|
||||
{ .id = 0x14f151d7, .name = "CX20952",
|
||||
.patch = patch_conexant_auto },
|
||||
static const struct hda_device_id snd_hda_id_conexant[] = {
|
||||
HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f15051, "CX20561 (Hermosa)", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f15066, "CX20582 (Pebble)", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f15067, "CX20583 (Pebble HSF)", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f15068, "CX20584", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f15069, "CX20585", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f1506c, "CX20588", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f1506e, "CX20590", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f15097, "CX20631", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f15098, "CX20632", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f150a1, "CX20641", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f150a2, "CX20642", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f150ab, "CX20651", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f150ac, "CX20652", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f150b8, "CX20664", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f150b9, "CX20665", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f150f1, "CX20721", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f150f2, "CX20722", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f150f3, "CX20723", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f150f4, "CX20724", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f1510f, "CX20751/2", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f15110, "CX20751/2", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f15111, "CX20753/4", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f15113, "CX20755", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f15114, "CX20756", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f15115, "CX20757", patch_conexant_auto),
|
||||
HDA_CODEC_ENTRY(0x14f151d7, "CX20952", patch_conexant_auto),
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15045");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15047");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15051");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15066");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15067");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15068");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15069");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f1506c");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f1506e");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15097");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15098");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f150a1");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f150a2");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f150ab");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f150ac");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f150b8");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f150b9");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f150f1");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f150f2");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f150f3");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f150f4");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f1510f");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15110");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15111");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15113");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15114");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15115");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f151d7");
|
||||
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_conexant);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Conexant HD-audio codec");
|
||||
|
||||
static struct hda_codec_driver conexant_driver = {
|
||||
.preset = snd_hda_preset_conexant,
|
||||
.id = snd_hda_id_conexant,
|
||||
};
|
||||
|
||||
module_hda_codec_driver(conexant_driver);
|
||||
|
||||
+89
-131
@@ -1775,6 +1775,16 @@ static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
|
||||
return non_pcm;
|
||||
}
|
||||
|
||||
/* There is a fixed mapping between audio pin node and display port
|
||||
* on current Intel platforms:
|
||||
* Pin Widget 5 - PORT B (port = 1 in i915 driver)
|
||||
* Pin Widget 6 - PORT C (port = 2 in i915 driver)
|
||||
* Pin Widget 7 - PORT D (port = 3 in i915 driver)
|
||||
*/
|
||||
static int intel_pin2port(hda_nid_t pin_nid)
|
||||
{
|
||||
return pin_nid - 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* HDMI callbacks
|
||||
@@ -1791,6 +1801,8 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
int pin_idx = hinfo_to_pin_index(codec, hinfo);
|
||||
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
||||
hda_nid_t pin_nid = per_pin->pin_nid;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct i915_audio_component *acomp = codec->bus->core.audio_component;
|
||||
bool non_pcm;
|
||||
int pinctl;
|
||||
|
||||
@@ -1807,6 +1819,13 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx);
|
||||
}
|
||||
|
||||
/* Call sync_audio_rate to set the N/CTS/M manually if necessary */
|
||||
/* Todo: add DP1.2 MST audio support later */
|
||||
if (acomp && acomp->ops && acomp->ops->sync_audio_rate)
|
||||
acomp->ops->sync_audio_rate(acomp->dev,
|
||||
intel_pin2port(pin_nid),
|
||||
runtime->rate);
|
||||
|
||||
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
|
||||
mutex_lock(&per_pin->lock);
|
||||
per_pin->channels = substream->runtime->channels;
|
||||
@@ -2561,7 +2580,7 @@ static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo,
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct snd_pcm_hw_constraint_list *hw_constraints_channels = NULL;
|
||||
|
||||
switch (codec->preset->id) {
|
||||
switch (codec->preset->vendor_id) {
|
||||
case 0x10de0002:
|
||||
case 0x10de0003:
|
||||
case 0x10de0005:
|
||||
@@ -2879,7 +2898,7 @@ static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec)
|
||||
snd_pcm_alt_chmaps, 8, 0, &chmap);
|
||||
if (err < 0)
|
||||
return err;
|
||||
switch (codec->preset->id) {
|
||||
switch (codec->preset->vendor_id) {
|
||||
case 0x10de0002:
|
||||
case 0x10de0003:
|
||||
case 0x10de0005:
|
||||
@@ -3487,138 +3506,77 @@ static int patch_via_hdmi(struct hda_codec *codec)
|
||||
/*
|
||||
* patch entries
|
||||
*/
|
||||
static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
|
||||
{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
|
||||
{ .id = 0x10de0003, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
|
||||
{ .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
|
||||
{ .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
|
||||
{ .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x },
|
||||
{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_nvhdmi },
|
||||
static const struct hda_device_id snd_hda_id_hdmi[] = {
|
||||
HDA_CODEC_ENTRY(0x1002793c, "RS600 HDMI", patch_atihdmi),
|
||||
HDA_CODEC_ENTRY(0x10027919, "RS600 HDMI", patch_atihdmi),
|
||||
HDA_CODEC_ENTRY(0x1002791a, "RS690/780 HDMI", patch_atihdmi),
|
||||
HDA_CODEC_ENTRY(0x1002aa01, "R6xx HDMI", patch_atihdmi),
|
||||
HDA_CODEC_ENTRY(0x10951390, "SiI1390 HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x10951392, "SiI1392 HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x17e80047, "Chrontel HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0002, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
|
||||
HDA_CODEC_ENTRY(0x10de0003, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
|
||||
HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
|
||||
HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x),
|
||||
HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI", patch_nvhdmi_8ch_7x),
|
||||
HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP", patch_nvhdmi),
|
||||
/* 17 is known to be absent */
|
||||
{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0020, .name = "Tegra30 HDMI", .patch = patch_tegra_hdmi },
|
||||
{ .id = 0x10de0022, .name = "Tegra114 HDMI", .patch = patch_tegra_hdmi },
|
||||
{ .id = 0x10de0028, .name = "Tegra124 HDMI", .patch = patch_tegra_hdmi },
|
||||
{ .id = 0x10de0029, .name = "Tegra210 HDMI/DP", .patch = patch_tegra_hdmi },
|
||||
{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0060, .name = "GPU 60 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
|
||||
{ .id = 0x10de0070, .name = "GPU 70 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0071, .name = "GPU 71 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0072, .name = "GPU 72 HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de007d, .name = "GPU 7d HDMI/DP", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
|
||||
{ .id = 0x11069f80, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
|
||||
{ .id = 0x11069f81, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
|
||||
{ .id = 0x11069f84, .name = "VX11 HDMI/DP", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x11069f85, .name = "VX11 HDMI/DP", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862807, .name = "Haswell HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862808, .name = "Broadwell HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862809, .name = "Skylake HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x8086280a, .name = "Broxton HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862882, .name = "Valleyview2 HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862883, .name = "Braswell HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
|
||||
HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI", patch_tegra_hdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI", patch_tegra_hdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI", patch_tegra_hdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0029, "Tegra210 HDMI/DP", patch_tegra_hdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0043, "GPU 43 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0044, "GPU 44 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0051, "GPU 51 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0060, "GPU 60 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0067, "MCP67 HDMI", patch_nvhdmi_2ch),
|
||||
HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP", patch_nvhdmi),
|
||||
HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch),
|
||||
HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi),
|
||||
HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi),
|
||||
HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_generic_hdmi),
|
||||
HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi),
|
||||
/* special ID for generic HDMI */
|
||||
{ .id = HDA_CODEC_ID_GENERIC_HDMI, .patch = patch_generic_hdmi },
|
||||
HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi),
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:1002793c");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10027919");
|
||||
MODULE_ALIAS("snd-hda-codec-id:1002791a");
|
||||
MODULE_ALIAS("snd-hda-codec-id:1002aa01");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10951390");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10951392");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0002");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0003");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0005");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0006");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0007");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de000a");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de000b");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de000c");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de000d");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0010");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0011");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0012");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0013");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0014");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0015");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0016");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0018");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0019");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de001a");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de001b");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de001c");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0028");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0040");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0041");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0042");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0043");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0044");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0051");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0060");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0067");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0070");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0071");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0072");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de007d");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de8001");
|
||||
MODULE_ALIAS("snd-hda-codec-id:11069f80");
|
||||
MODULE_ALIAS("snd-hda-codec-id:11069f81");
|
||||
MODULE_ALIAS("snd-hda-codec-id:11069f84");
|
||||
MODULE_ALIAS("snd-hda-codec-id:11069f85");
|
||||
MODULE_ALIAS("snd-hda-codec-id:17e80047");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80860054");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862801");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862802");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862803");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862804");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862805");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862806");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862807");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862808");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862809");
|
||||
MODULE_ALIAS("snd-hda-codec-id:8086280a");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862880");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862882");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862883");
|
||||
MODULE_ALIAS("snd-hda-codec-id:808629fb");
|
||||
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_hdmi);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("HDMI HD-audio codec");
|
||||
@@ -3627,7 +3585,7 @@ MODULE_ALIAS("snd-hda-codec-nvhdmi");
|
||||
MODULE_ALIAS("snd-hda-codec-atihdmi");
|
||||
|
||||
static struct hda_codec_driver hdmi_driver = {
|
||||
.preset = snd_hda_preset_hdmi,
|
||||
.id = snd_hda_id_hdmi,
|
||||
};
|
||||
|
||||
module_hda_codec_driver(hdmi_driver);
|
||||
|
||||
@@ -822,17 +822,7 @@ static const struct hda_codec_ops alc_patch_ops = {
|
||||
};
|
||||
|
||||
|
||||
/* replace the codec chip_name with the given string */
|
||||
static int alc_codec_rename(struct hda_codec *codec, const char *name)
|
||||
{
|
||||
kfree(codec->core.chip_name);
|
||||
codec->core.chip_name = kstrdup(name, GFP_KERNEL);
|
||||
if (!codec->core.chip_name) {
|
||||
alc_free(codec);
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name)
|
||||
|
||||
/*
|
||||
* Rename codecs appropriately from COEF value or subvendor id
|
||||
@@ -4596,6 +4586,7 @@ enum {
|
||||
ALC292_FIXUP_DELL_E7X,
|
||||
ALC292_FIXUP_DISABLE_AAMIX,
|
||||
ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
|
||||
ALC275_FIXUP_DELL_XPS,
|
||||
};
|
||||
|
||||
static const struct hda_fixup alc269_fixups[] = {
|
||||
@@ -5165,6 +5156,17 @@ static const struct hda_fixup alc269_fixups[] = {
|
||||
.chained = true,
|
||||
.chain_id = ALC269_FIXUP_HEADSET_MODE
|
||||
},
|
||||
[ALC275_FIXUP_DELL_XPS] = {
|
||||
.type = HDA_FIXUP_VERBS,
|
||||
.v.verbs = (const struct hda_verb[]) {
|
||||
/* Enables internal speaker */
|
||||
{0x20, AC_VERB_SET_COEF_INDEX, 0x1f},
|
||||
{0x20, AC_VERB_SET_PROC_COEF, 0x00c0},
|
||||
{0x20, AC_VERB_SET_COEF_INDEX, 0x30},
|
||||
{0x20, AC_VERB_SET_PROC_COEF, 0x00b1},
|
||||
{}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
||||
@@ -5179,6 +5181,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
|
||||
SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS),
|
||||
SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
|
||||
SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS),
|
||||
SND_PCI_QUIRK(0x1028, 0x05ca, "Dell Latitude E7240", ALC292_FIXUP_DELL_E7X),
|
||||
SND_PCI_QUIRK(0x1028, 0x05cb, "Dell Latitude E7440", ALC292_FIXUP_DELL_E7X),
|
||||
SND_PCI_QUIRK(0x1028, 0x05da, "Dell Vostro 5460", ALC290_FIXUP_SUBWOOFER),
|
||||
@@ -6627,78 +6630,70 @@ static int patch_alc680(struct hda_codec *codec)
|
||||
/*
|
||||
* patch entries
|
||||
*/
|
||||
static const struct hda_codec_preset snd_hda_preset_realtek[] = {
|
||||
{ .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0231, .name = "ALC231", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0233, .name = "ALC233", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0235, .name = "ALC233", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0255, .name = "ALC255", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0256, .name = "ALC256", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
|
||||
{ .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
|
||||
{ .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
|
||||
{ .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
|
||||
{ .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0270, .name = "ALC270", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 },
|
||||
{ .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0276, .name = "ALC276", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0280, .name = "ALC280", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0284, .name = "ALC284", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0285, .name = "ALC285", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0286, .name = "ALC286", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0288, .name = "ALC288", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0292, .name = "ALC292", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0293, .name = "ALC293", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0298, .name = "ALC298", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
|
||||
.patch = patch_alc861 },
|
||||
{ .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
|
||||
{ .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
|
||||
{ .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd },
|
||||
{ .id = 0x10ec0662, .rev = 0x100002, .name = "ALC662 rev2",
|
||||
.patch = patch_alc882 },
|
||||
{ .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1",
|
||||
.patch = patch_alc662 },
|
||||
{ .id = 0x10ec0662, .rev = 0x100300, .name = "ALC662 rev3",
|
||||
.patch = patch_alc662 },
|
||||
{ .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
|
||||
{ .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 },
|
||||
{ .id = 0x10ec0667, .name = "ALC667", .patch = patch_alc662 },
|
||||
{ .id = 0x10ec0668, .name = "ALC668", .patch = patch_alc662 },
|
||||
{ .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 },
|
||||
{ .id = 0x10ec0671, .name = "ALC671", .patch = patch_alc662 },
|
||||
{ .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 },
|
||||
{ .id = 0x10ec0867, .name = "ALC891", .patch = patch_alc882 },
|
||||
{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
|
||||
{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
|
||||
{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
|
||||
{ .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A",
|
||||
.patch = patch_alc882 },
|
||||
{ .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A",
|
||||
.patch = patch_alc882 },
|
||||
{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
|
||||
{ .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 },
|
||||
{ .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
|
||||
.patch = patch_alc882 },
|
||||
{ .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 },
|
||||
{ .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 },
|
||||
{ .id = 0x10ec0892, .name = "ALC892", .patch = patch_alc662 },
|
||||
{ .id = 0x10ec0899, .name = "ALC898", .patch = patch_alc882 },
|
||||
{ .id = 0x10ec0900, .name = "ALC1150", .patch = patch_alc882 },
|
||||
static const struct hda_device_id snd_hda_id_realtek[] = {
|
||||
HDA_CODEC_ENTRY(0x10ec0221, "ALC221", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0260, "ALC260", patch_alc260),
|
||||
HDA_CODEC_ENTRY(0x10ec0262, "ALC262", patch_alc262),
|
||||
HDA_CODEC_ENTRY(0x10ec0267, "ALC267", patch_alc268),
|
||||
HDA_CODEC_ENTRY(0x10ec0268, "ALC268", patch_alc268),
|
||||
HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662),
|
||||
HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0282, "ALC282", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0283, "ALC283", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0284, "ALC284", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0285, "ALC285", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0286, "ALC286", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0288, "ALC288", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269),
|
||||
HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861),
|
||||
HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd),
|
||||
HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861),
|
||||
HDA_CODEC_ENTRY(0x10ec0862, "ALC861-VD", patch_alc861vd),
|
||||
HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100002, "ALC662 rev2", patch_alc882),
|
||||
HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100101, "ALC662 rev1", patch_alc662),
|
||||
HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100300, "ALC662 rev3", patch_alc662),
|
||||
HDA_CODEC_ENTRY(0x10ec0663, "ALC663", patch_alc662),
|
||||
HDA_CODEC_ENTRY(0x10ec0665, "ALC665", patch_alc662),
|
||||
HDA_CODEC_ENTRY(0x10ec0667, "ALC667", patch_alc662),
|
||||
HDA_CODEC_ENTRY(0x10ec0668, "ALC668", patch_alc662),
|
||||
HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662),
|
||||
HDA_CODEC_ENTRY(0x10ec0671, "ALC671", patch_alc662),
|
||||
HDA_CODEC_ENTRY(0x10ec0680, "ALC680", patch_alc680),
|
||||
HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc882),
|
||||
HDA_CODEC_ENTRY(0x10ec0880, "ALC880", patch_alc880),
|
||||
HDA_CODEC_ENTRY(0x10ec0882, "ALC882", patch_alc882),
|
||||
HDA_CODEC_ENTRY(0x10ec0883, "ALC883", patch_alc882),
|
||||
HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100101, "ALC889A", patch_alc882),
|
||||
HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100103, "ALC889A", patch_alc882),
|
||||
HDA_CODEC_ENTRY(0x10ec0885, "ALC885", patch_alc882),
|
||||
HDA_CODEC_ENTRY(0x10ec0887, "ALC887", patch_alc882),
|
||||
HDA_CODEC_REV_ENTRY(0x10ec0888, 0x100101, "ALC1200", patch_alc882),
|
||||
HDA_CODEC_ENTRY(0x10ec0888, "ALC888", patch_alc882),
|
||||
HDA_CODEC_ENTRY(0x10ec0889, "ALC889", patch_alc882),
|
||||
HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662),
|
||||
HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882),
|
||||
HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882),
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:10ec*");
|
||||
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Realtek HD-audio codec");
|
||||
|
||||
static struct hda_codec_driver realtek_driver = {
|
||||
.preset = snd_hda_preset_realtek,
|
||||
.id = snd_hda_id_realtek,
|
||||
};
|
||||
|
||||
module_hda_codec_driver(realtek_driver);
|
||||
|
||||
@@ -289,41 +289,30 @@ static int patch_si3054(struct hda_codec *codec)
|
||||
/*
|
||||
* patch entries
|
||||
*/
|
||||
static const struct hda_codec_preset snd_hda_preset_si3054[] = {
|
||||
{ .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
|
||||
{ .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
|
||||
{ .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
|
||||
{ .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 },
|
||||
{ .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 },
|
||||
{ .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 },
|
||||
{ .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 },
|
||||
{ .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 },
|
||||
static const struct hda_device_id snd_hda_id_si3054[] = {
|
||||
HDA_CODEC_ENTRY(0x163c3055, "Si3054", patch_si3054),
|
||||
HDA_CODEC_ENTRY(0x163c3155, "Si3054", patch_si3054),
|
||||
HDA_CODEC_ENTRY(0x11c13026, "Si3054", patch_si3054),
|
||||
HDA_CODEC_ENTRY(0x11c13055, "Si3054", patch_si3054),
|
||||
HDA_CODEC_ENTRY(0x11c13155, "Si3054", patch_si3054),
|
||||
HDA_CODEC_ENTRY(0x10573055, "Si3054", patch_si3054),
|
||||
HDA_CODEC_ENTRY(0x10573057, "Si3054", patch_si3054),
|
||||
HDA_CODEC_ENTRY(0x10573155, "Si3054", patch_si3054),
|
||||
/* VIA HDA on Clevo m540 */
|
||||
{ .id = 0x11063288, .name = "Si3054", .patch = patch_si3054 },
|
||||
HDA_CODEC_ENTRY(0x11063288, "Si3054", patch_si3054),
|
||||
/* Asus A8J Modem (SM56) */
|
||||
{ .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 },
|
||||
HDA_CODEC_ENTRY(0x15433155, "Si3054", patch_si3054),
|
||||
/* LG LW20 modem */
|
||||
{ .id = 0x18540018, .name = "Si3054", .patch = patch_si3054 },
|
||||
HDA_CODEC_ENTRY(0x18540018, "Si3054", patch_si3054),
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:163c3055");
|
||||
MODULE_ALIAS("snd-hda-codec-id:163c3155");
|
||||
MODULE_ALIAS("snd-hda-codec-id:11c13026");
|
||||
MODULE_ALIAS("snd-hda-codec-id:11c13055");
|
||||
MODULE_ALIAS("snd-hda-codec-id:11c13155");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10573055");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10573057");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10573155");
|
||||
MODULE_ALIAS("snd-hda-codec-id:11063288");
|
||||
MODULE_ALIAS("snd-hda-codec-id:15433155");
|
||||
MODULE_ALIAS("snd-hda-codec-id:18540018");
|
||||
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_si3054);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Si3054 HD-audio modem codec");
|
||||
|
||||
static struct hda_codec_driver si3054_driver = {
|
||||
.preset = snd_hda_preset_si3054,
|
||||
.id = snd_hda_id_si3054,
|
||||
};
|
||||
|
||||
module_hda_codec_driver(si3054_driver);
|
||||
|
||||
+107
-108
@@ -702,6 +702,7 @@ static bool hp_bnb2011_with_dock(struct hda_codec *codec)
|
||||
static bool hp_blike_system(u32 subsystem_id)
|
||||
{
|
||||
switch (subsystem_id) {
|
||||
case 0x103c1473: /* HP ProBook 6550b */
|
||||
case 0x103c1520:
|
||||
case 0x103c1521:
|
||||
case 0x103c1523:
|
||||
@@ -5012,121 +5013,119 @@ static int patch_stac9872(struct hda_codec *codec)
|
||||
/*
|
||||
* patch entries
|
||||
*/
|
||||
static const struct hda_codec_preset snd_hda_preset_sigmatel[] = {
|
||||
{ .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
|
||||
{ .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
|
||||
{ .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
|
||||
{ .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x },
|
||||
{ .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
|
||||
{ .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
|
||||
{ .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
|
||||
{ .id = 0x83847618, .name = "STAC9227", .patch = patch_stac927x },
|
||||
{ .id = 0x83847619, .name = "STAC9227", .patch = patch_stac927x },
|
||||
{ .id = 0x83847616, .name = "STAC9228", .patch = patch_stac927x },
|
||||
{ .id = 0x83847617, .name = "STAC9228", .patch = patch_stac927x },
|
||||
{ .id = 0x83847614, .name = "STAC9229", .patch = patch_stac927x },
|
||||
{ .id = 0x83847615, .name = "STAC9229", .patch = patch_stac927x },
|
||||
{ .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x },
|
||||
{ .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x },
|
||||
{ .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x },
|
||||
{ .id = 0x83847623, .name = "STAC9273D", .patch = patch_stac927x },
|
||||
{ .id = 0x83847624, .name = "STAC9272X", .patch = patch_stac927x },
|
||||
{ .id = 0x83847625, .name = "STAC9272D", .patch = patch_stac927x },
|
||||
{ .id = 0x83847626, .name = "STAC9271X", .patch = patch_stac927x },
|
||||
{ .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x },
|
||||
{ .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x },
|
||||
{ .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x },
|
||||
{ .id = 0x83847632, .name = "STAC9202", .patch = patch_stac925x },
|
||||
{ .id = 0x83847633, .name = "STAC9202D", .patch = patch_stac925x },
|
||||
{ .id = 0x83847634, .name = "STAC9250", .patch = patch_stac925x },
|
||||
{ .id = 0x83847635, .name = "STAC9250D", .patch = patch_stac925x },
|
||||
{ .id = 0x83847636, .name = "STAC9251", .patch = patch_stac925x },
|
||||
{ .id = 0x83847637, .name = "STAC9250D", .patch = patch_stac925x },
|
||||
{ .id = 0x83847645, .name = "92HD206X", .patch = patch_stac927x },
|
||||
{ .id = 0x83847646, .name = "92HD206D", .patch = patch_stac927x },
|
||||
/* The following does not take into account .id=0x83847661 when subsys =
|
||||
* 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
|
||||
* currently not fully supported.
|
||||
*/
|
||||
{ .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
|
||||
{ .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
|
||||
{ .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
|
||||
{ .id = 0x83847698, .name = "STAC9205", .patch = patch_stac9205 },
|
||||
{ .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
|
||||
{ .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
|
||||
{ .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
|
||||
{ .id = 0x838476a3, .name = "STAC9204D", .patch = patch_stac9205 },
|
||||
{ .id = 0x838476a4, .name = "STAC9255", .patch = patch_stac9205 },
|
||||
{ .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
|
||||
{ .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
|
||||
{ .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
|
||||
{ .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx},
|
||||
{ .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76d4, .name = "92HD83C1C5", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76d1, .name = "92HD87B1/3", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76d9, .name = "92HD87B2/4", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d7666, .name = "92HD88B3", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d7667, .name = "92HD88B1", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d7668, .name = "92HD88B2", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d7669, .name = "92HD88B4", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},
|
||||
{ .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d7695, .name = "92HD95", .patch = patch_stac92hd95 },
|
||||
{ .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
|
||||
{ .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
|
||||
{ .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
|
||||
{ .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
|
||||
{ .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
|
||||
{ .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
|
||||
{ .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
|
||||
{ .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
|
||||
{ .id = 0x111d76c0, .name = "92HD89C3", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d76c1, .name = "92HD89C2", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d76c2, .name = "92HD89C1", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d76c3, .name = "92HD89B3", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d76c4, .name = "92HD89B2", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d76c5, .name = "92HD89B1", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d76c6, .name = "92HD89E3", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d76c7, .name = "92HD89E2", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d76c8, .name = "92HD89E1", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d76c9, .name = "92HD89D3", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d76ca, .name = "92HD89D2", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d76cb, .name = "92HD89D1", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx },
|
||||
{ .id = 0x111d76df, .name = "92HD93BXX", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76e3, .name = "92HD98BXX", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76e5, .name = "92HD99BXX", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76e8, .name = "92HD66B1X5", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76e9, .name = "92HD66B2X5", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76ea, .name = "92HD66B3X5", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76eb, .name = "92HD66C1X5", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76ec, .name = "92HD66C2X5", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76ed, .name = "92HD66C3X5", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76ee, .name = "92HD66B1X3", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76ef, .name = "92HD66B2X3", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76f0, .name = "92HD66B3X3", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76f1, .name = "92HD66C1X3", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76f2, .name = "92HD66C2X3", .patch = patch_stac92hd83xxx},
|
||||
{ .id = 0x111d76f3, .name = "92HD66C3/65", .patch = patch_stac92hd83xxx},
|
||||
static const struct hda_device_id snd_hda_id_sigmatel[] = {
|
||||
HDA_CODEC_ENTRY(0x83847690, "STAC9200", patch_stac9200),
|
||||
HDA_CODEC_ENTRY(0x83847882, "STAC9220 A1", patch_stac922x),
|
||||
HDA_CODEC_ENTRY(0x83847680, "STAC9221 A1", patch_stac922x),
|
||||
HDA_CODEC_ENTRY(0x83847880, "STAC9220 A2", patch_stac922x),
|
||||
HDA_CODEC_ENTRY(0x83847681, "STAC9220D/9223D A2", patch_stac922x),
|
||||
HDA_CODEC_ENTRY(0x83847682, "STAC9221 A2", patch_stac922x),
|
||||
HDA_CODEC_ENTRY(0x83847683, "STAC9221D A2", patch_stac922x),
|
||||
HDA_CODEC_ENTRY(0x83847618, "STAC9227", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847619, "STAC9227", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847616, "STAC9228", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847617, "STAC9228", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847614, "STAC9229", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847615, "STAC9229", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847620, "STAC9274", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847621, "STAC9274D", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847622, "STAC9273X", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847623, "STAC9273D", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847624, "STAC9272X", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847625, "STAC9272D", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847626, "STAC9271X", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847627, "STAC9271D", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847628, "STAC9274X5NH", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847629, "STAC9274D5NH", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847632, "STAC9202", patch_stac925x),
|
||||
HDA_CODEC_ENTRY(0x83847633, "STAC9202D", patch_stac925x),
|
||||
HDA_CODEC_ENTRY(0x83847634, "STAC9250", patch_stac925x),
|
||||
HDA_CODEC_ENTRY(0x83847635, "STAC9250D", patch_stac925x),
|
||||
HDA_CODEC_ENTRY(0x83847636, "STAC9251", patch_stac925x),
|
||||
HDA_CODEC_ENTRY(0x83847637, "STAC9250D", patch_stac925x),
|
||||
HDA_CODEC_ENTRY(0x83847645, "92HD206X", patch_stac927x),
|
||||
HDA_CODEC_ENTRY(0x83847646, "92HD206D", patch_stac927x),
|
||||
/* The following does not take into account .id=0x83847661 when subsys =
|
||||
* 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
|
||||
* currently not fully supported.
|
||||
*/
|
||||
HDA_CODEC_ENTRY(0x83847661, "CXD9872RD/K", patch_stac9872),
|
||||
HDA_CODEC_ENTRY(0x83847662, "STAC9872AK", patch_stac9872),
|
||||
HDA_CODEC_ENTRY(0x83847664, "CXD9872AKD", patch_stac9872),
|
||||
HDA_CODEC_ENTRY(0x83847698, "STAC9205", patch_stac9205),
|
||||
HDA_CODEC_ENTRY(0x838476a0, "STAC9205", patch_stac9205),
|
||||
HDA_CODEC_ENTRY(0x838476a1, "STAC9205D", patch_stac9205),
|
||||
HDA_CODEC_ENTRY(0x838476a2, "STAC9204", patch_stac9205),
|
||||
HDA_CODEC_ENTRY(0x838476a3, "STAC9204D", patch_stac9205),
|
||||
HDA_CODEC_ENTRY(0x838476a4, "STAC9255", patch_stac9205),
|
||||
HDA_CODEC_ENTRY(0x838476a5, "STAC9255D", patch_stac9205),
|
||||
HDA_CODEC_ENTRY(0x838476a6, "STAC9254", patch_stac9205),
|
||||
HDA_CODEC_ENTRY(0x838476a7, "STAC9254D", patch_stac9205),
|
||||
HDA_CODEC_ENTRY(0x111d7603, "92HD75B3X5", patch_stac92hd71bxx),
|
||||
HDA_CODEC_ENTRY(0x111d7604, "92HD83C1X5", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76d4, "92HD83C1C5", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d7605, "92HD81B1X5", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76d5, "92HD81B1C5", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76d1, "92HD87B1/3", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76d9, "92HD87B2/4", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d7666, "92HD88B3", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d7667, "92HD88B1", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d7668, "92HD88B2", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d7669, "92HD88B4", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d7608, "92HD75B2X5", patch_stac92hd71bxx),
|
||||
HDA_CODEC_ENTRY(0x111d7674, "92HD73D1X5", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d7675, "92HD73C1X5", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d7676, "92HD73E1X5", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d7695, "92HD95", patch_stac92hd95),
|
||||
HDA_CODEC_ENTRY(0x111d76b0, "92HD71B8X", patch_stac92hd71bxx),
|
||||
HDA_CODEC_ENTRY(0x111d76b1, "92HD71B8X", patch_stac92hd71bxx),
|
||||
HDA_CODEC_ENTRY(0x111d76b2, "92HD71B7X", patch_stac92hd71bxx),
|
||||
HDA_CODEC_ENTRY(0x111d76b3, "92HD71B7X", patch_stac92hd71bxx),
|
||||
HDA_CODEC_ENTRY(0x111d76b4, "92HD71B6X", patch_stac92hd71bxx),
|
||||
HDA_CODEC_ENTRY(0x111d76b5, "92HD71B6X", patch_stac92hd71bxx),
|
||||
HDA_CODEC_ENTRY(0x111d76b6, "92HD71B5X", patch_stac92hd71bxx),
|
||||
HDA_CODEC_ENTRY(0x111d76b7, "92HD71B5X", patch_stac92hd71bxx),
|
||||
HDA_CODEC_ENTRY(0x111d76c0, "92HD89C3", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d76c1, "92HD89C2", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d76c2, "92HD89C1", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d76c3, "92HD89B3", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d76c4, "92HD89B2", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d76c5, "92HD89B1", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d76c6, "92HD89E3", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d76c7, "92HD89E2", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d76c8, "92HD89E1", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d76c9, "92HD89D3", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d76ca, "92HD89D2", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d76cb, "92HD89D1", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d76cc, "92HD89F3", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d76cd, "92HD89F2", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d76ce, "92HD89F1", patch_stac92hd73xx),
|
||||
HDA_CODEC_ENTRY(0x111d76df, "92HD93BXX", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76e0, "92HD91BXX", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76e3, "92HD98BXX", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76e5, "92HD99BXX", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76e7, "92HD90BXX", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76e8, "92HD66B1X5", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76e9, "92HD66B2X5", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76ea, "92HD66B3X5", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76eb, "92HD66C1X5", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76ec, "92HD66C2X5", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76ed, "92HD66C3X5", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76ee, "92HD66B1X3", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76ef, "92HD66B2X3", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76f0, "92HD66B3X3", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76f1, "92HD66C1X3", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76f2, "92HD66C2X3", patch_stac92hd83xxx),
|
||||
HDA_CODEC_ENTRY(0x111d76f3, "92HD66C3/65", patch_stac92hd83xxx),
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:8384*");
|
||||
MODULE_ALIAS("snd-hda-codec-id:111d*");
|
||||
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_sigmatel);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
|
||||
|
||||
static struct hda_codec_driver sigmatel_driver = {
|
||||
.preset = snd_hda_preset_sigmatel,
|
||||
.id = snd_hda_id_sigmatel,
|
||||
};
|
||||
|
||||
module_hda_codec_driver(sigmatel_driver);
|
||||
|
||||
+58
-113
@@ -785,21 +785,11 @@ static int patch_vt1708S(struct hda_codec *codec)
|
||||
override_mic_boost(codec, 0x1e, 0, 3, 40);
|
||||
|
||||
/* correct names for VT1708BCE */
|
||||
if (get_codec_type(codec) == VT1708BCE) {
|
||||
kfree(codec->core.chip_name);
|
||||
codec->core.chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
|
||||
snprintf(codec->card->mixername,
|
||||
sizeof(codec->card->mixername),
|
||||
"%s %s", codec->core.vendor_name, codec->core.chip_name);
|
||||
}
|
||||
if (get_codec_type(codec) == VT1708BCE)
|
||||
snd_hda_codec_set_name(codec, "VT1708BCE");
|
||||
/* correct names for VT1705 */
|
||||
if (codec->core.vendor_id == 0x11064397) {
|
||||
kfree(codec->core.chip_name);
|
||||
codec->core.chip_name = kstrdup("VT1705", GFP_KERNEL);
|
||||
snprintf(codec->card->mixername,
|
||||
sizeof(codec->card->mixername),
|
||||
"%s %s", codec->core.vendor_name, codec->core.chip_name);
|
||||
}
|
||||
if (codec->core.vendor_id == 0x11064397)
|
||||
snd_hda_codec_set_name(codec, "VT1705");
|
||||
|
||||
/* automatic parse from the BIOS config */
|
||||
err = via_parse_auto_config(codec);
|
||||
@@ -1210,109 +1200,64 @@ static int patch_vt3476(struct hda_codec *codec)
|
||||
/*
|
||||
* patch entries
|
||||
*/
|
||||
static const struct hda_codec_preset snd_hda_preset_via[] = {
|
||||
{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
|
||||
{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
|
||||
{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
|
||||
{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
|
||||
{ .id = 0x1106e710, .name = "VT1709 10-Ch",
|
||||
.patch = patch_vt1709},
|
||||
{ .id = 0x1106e711, .name = "VT1709 10-Ch",
|
||||
.patch = patch_vt1709},
|
||||
{ .id = 0x1106e712, .name = "VT1709 10-Ch",
|
||||
.patch = patch_vt1709},
|
||||
{ .id = 0x1106e713, .name = "VT1709 10-Ch",
|
||||
.patch = patch_vt1709},
|
||||
{ .id = 0x1106e714, .name = "VT1709 6-Ch",
|
||||
.patch = patch_vt1709},
|
||||
{ .id = 0x1106e715, .name = "VT1709 6-Ch",
|
||||
.patch = patch_vt1709},
|
||||
{ .id = 0x1106e716, .name = "VT1709 6-Ch",
|
||||
.patch = patch_vt1709},
|
||||
{ .id = 0x1106e717, .name = "VT1709 6-Ch",
|
||||
.patch = patch_vt1709},
|
||||
{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
|
||||
.patch = patch_vt1708B},
|
||||
{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
|
||||
.patch = patch_vt1708B},
|
||||
{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
|
||||
.patch = patch_vt1708B},
|
||||
{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
|
||||
.patch = patch_vt1708B},
|
||||
{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
|
||||
.patch = patch_vt1708B},
|
||||
{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
|
||||
.patch = patch_vt1708B},
|
||||
{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
|
||||
.patch = patch_vt1708B},
|
||||
{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
|
||||
.patch = patch_vt1708B},
|
||||
{ .id = 0x11060397, .name = "VT1708S",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11061397, .name = "VT1708S",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11062397, .name = "VT1708S",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11063397, .name = "VT1708S",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11064397, .name = "VT1705",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11065397, .name = "VT1708S",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11066397, .name = "VT1708S",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11067397, .name = "VT1708S",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11060398, .name = "VT1702",
|
||||
.patch = patch_vt1702},
|
||||
{ .id = 0x11061398, .name = "VT1702",
|
||||
.patch = patch_vt1702},
|
||||
{ .id = 0x11062398, .name = "VT1702",
|
||||
.patch = patch_vt1702},
|
||||
{ .id = 0x11063398, .name = "VT1702",
|
||||
.patch = patch_vt1702},
|
||||
{ .id = 0x11064398, .name = "VT1702",
|
||||
.patch = patch_vt1702},
|
||||
{ .id = 0x11065398, .name = "VT1702",
|
||||
.patch = patch_vt1702},
|
||||
{ .id = 0x11066398, .name = "VT1702",
|
||||
.patch = patch_vt1702},
|
||||
{ .id = 0x11067398, .name = "VT1702",
|
||||
.patch = patch_vt1702},
|
||||
{ .id = 0x11060428, .name = "VT1718S",
|
||||
.patch = patch_vt1718S},
|
||||
{ .id = 0x11064428, .name = "VT1718S",
|
||||
.patch = patch_vt1718S},
|
||||
{ .id = 0x11060441, .name = "VT2020",
|
||||
.patch = patch_vt1718S},
|
||||
{ .id = 0x11064441, .name = "VT1828S",
|
||||
.patch = patch_vt1718S},
|
||||
{ .id = 0x11060433, .name = "VT1716S",
|
||||
.patch = patch_vt1716S},
|
||||
{ .id = 0x1106a721, .name = "VT1716S",
|
||||
.patch = patch_vt1716S},
|
||||
{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
|
||||
{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
|
||||
{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
|
||||
{ .id = 0x11060440, .name = "VT1818S",
|
||||
.patch = patch_vt1708S},
|
||||
{ .id = 0x11060446, .name = "VT1802",
|
||||
.patch = patch_vt2002P},
|
||||
{ .id = 0x11068446, .name = "VT1802",
|
||||
.patch = patch_vt2002P},
|
||||
{ .id = 0x11064760, .name = "VT1705CF",
|
||||
.patch = patch_vt3476},
|
||||
{ .id = 0x11064761, .name = "VT1708SCE",
|
||||
.patch = patch_vt3476},
|
||||
{ .id = 0x11064762, .name = "VT1808",
|
||||
.patch = patch_vt3476},
|
||||
static const struct hda_device_id snd_hda_id_via[] = {
|
||||
HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708),
|
||||
HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708),
|
||||
HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708),
|
||||
HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708),
|
||||
HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709),
|
||||
HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709),
|
||||
HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709),
|
||||
HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709),
|
||||
HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709),
|
||||
HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709),
|
||||
HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709),
|
||||
HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709),
|
||||
HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B),
|
||||
HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B),
|
||||
HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B),
|
||||
HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B),
|
||||
HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B),
|
||||
HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B),
|
||||
HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B),
|
||||
HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B),
|
||||
HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S),
|
||||
HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S),
|
||||
HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S),
|
||||
HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S),
|
||||
HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S),
|
||||
HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S),
|
||||
HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S),
|
||||
HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S),
|
||||
HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702),
|
||||
HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702),
|
||||
HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702),
|
||||
HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702),
|
||||
HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702),
|
||||
HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702),
|
||||
HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702),
|
||||
HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702),
|
||||
HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S),
|
||||
HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S),
|
||||
HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S),
|
||||
HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S),
|
||||
HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S),
|
||||
HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S),
|
||||
HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P),
|
||||
HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P),
|
||||
HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812),
|
||||
HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S),
|
||||
HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P),
|
||||
HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P),
|
||||
HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476),
|
||||
HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476),
|
||||
HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476),
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:1106*");
|
||||
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via);
|
||||
|
||||
static struct hda_codec_driver via_driver = {
|
||||
.preset = snd_hda_preset_via,
|
||||
.id = snd_hda_id_via,
|
||||
};
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -1394,7 +1394,9 @@ static int snd_korg1212_playback_open(struct snd_pcm_substream *substream)
|
||||
|
||||
spin_unlock_irqrestore(&korg1212->lock, flags);
|
||||
|
||||
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, kPlayBufferFrames, kPlayBufferFrames);
|
||||
snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
kPlayBufferFrames);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1422,8 +1424,8 @@ static int snd_korg1212_capture_open(struct snd_pcm_substream *substream)
|
||||
|
||||
spin_unlock_irqrestore(&korg1212->lock, flags);
|
||||
|
||||
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
kPlayBufferFrames, kPlayBufferFrames);
|
||||
snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
kPlayBufferFrames);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user