Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (36 commits)
  ALSA: hda - Add VREF powerdown sequence for another board
  ALSA: oss - volume control for CSWITCH and CROUTE
  ALSA: hda - add missing comma in ad1884_slave_vols
  sound: usb-audio: allow period sizes less than 1 ms
  sound: usb-audio: save data packet interval in audioformat structure
  sound: usb-audio: remove check_hw_params_convention()
  sound: usb-audio: show sample format width in proc file
  ASoC: fsl_dma: Pass the proper device for dma mapping routines
  ASoC: Fix null dereference in ak4535_remove()
  ALSA: hda - enable SPDIF output for Intel DX58SO board
  ALSA: snd-atmel-abdac: increase periods_min to 6 instead of 4
  ALSA: snd-atmel-abdac: replace bus_id with dev_name()
  ALSA: snd-atmel-ac97c: replace bus_id with dev_name()
  ALSA: snd-atmel-ac97c: cleanup registers when removing driver
  ALSA: snd-atmel-ac97c: do a proper reset of the external codec
  ALSA: snd-atmel-ac97c: enable interrupts to catch events for error reporting
  ALSA: snd-atmel-ac97c: set correct size for buffer hardware parameter
  ALSA: snd-atmel-ac97c: do not overwrite OCA and ICA when assigning channels
  ALSA: snd-atmel-ac97c: remove dead break statements after return in switch case
  ALSA: snd-atmel-ac97c: cleanup register definitions
  ...
This commit is contained in:
Linus Torvalds 2009-04-07 08:53:38 -07:00
commit 81d91acf8c
24 changed files with 1131 additions and 233 deletions

View File

@ -0,0 +1,71 @@
ASoC jack detection
===================
ALSA has a standard API for representing physical jacks to user space,
the kernel side of which can be seen in include/sound/jack.h. ASoC
provides a version of this API adding two additional features:
- It allows more than one jack detection method to work together on one
user visible jack. In embedded systems it is common for multiple
to be present on a single jack but handled by separate bits of
hardware.
- Integration with DAPM, allowing DAPM endpoints to be updated
automatically based on the detected jack status (eg, turning off the
headphone outputs if no headphones are present).
This is done by splitting the jacks up into three things working
together: the jack itself represented by a struct snd_soc_jack, sets of
snd_soc_jack_pins representing DAPM endpoints to update and blocks of
code providing jack reporting mechanisms.
For example, a system may have a stereo headset jack with two reporting
mechanisms, one for the headphone and one for the microphone. Some
systems won't be able to use their speaker output while a headphone is
connected and so will want to make sure to update both speaker and
headphone when the headphone jack status changes.
The jack - struct snd_soc_jack
==============================
This represents a physical jack on the system and is what is visible to
user space. The jack itself is completely passive, it is set up by the
machine driver and updated by jack detection methods.
Jacks are created by the machine driver calling snd_soc_jack_new().
snd_soc_jack_pin
================
These represent a DAPM pin to update depending on some of the status
bits supported by the jack. Each snd_soc_jack has zero or more of these
which are updated automatically. They are created by the machine driver
and associated with the jack using snd_soc_jack_add_pins(). The status
of the endpoint may configured to be the opposite of the jack status if
required (eg, enabling a built in microphone if a microphone is not
connected via a jack).
Jack detection methods
======================
Actual jack detection is done by code which is able to monitor some
input to the system and update a jack by calling snd_soc_jack_report(),
specifying a subset of bits to update. The jack detection code should
be set up by the machine driver, taking configuration for the jack to
update and the set of things to report when the jack is connected.
Often this is done based on the status of a GPIO - a handler for this is
provided by the snd_soc_jack_add_gpio() function. Other methods are
also available, for example integrated into CODECs. One example of
CODEC integrated jack detection can be see in the WM8350 driver.
Each jack may have multiple reporting mechanisms, though it will need at
least one to be useful.
Machine drivers
===============
These are all hooked together by the machine driver depending on the
system hardware. The machine driver will set up the snd_soc_jack and
the list of pins to update then set up one or more jack detection
mechanisms to update that jack based on their current status.

View File

@ -238,6 +238,8 @@ static inline void pxa_ac97_cold_pxa3xx(void)
bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
{
unsigned long gsr;
#ifdef CONFIG_PXA25x
if (cpu_is_pxa25x())
pxa_ac97_warm_pxa25x();
@ -254,10 +256,10 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
else
#endif
BUG();
if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) {
gsr = GSR | gsr_bits;
if (!(gsr & (GSR_PCR | GSR_SCR))) {
printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
__func__, gsr_bits);
__func__, gsr);
return false;
}
@ -268,6 +270,8 @@ EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset);
bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
{
unsigned long gsr;
#ifdef CONFIG_PXA25x
if (cpu_is_pxa25x())
pxa_ac97_cold_pxa25x();
@ -285,9 +289,10 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
#endif
BUG();
if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) {
gsr = GSR | gsr_bits;
if (!(gsr & (GSR_PCR | GSR_SCR))) {
printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
__func__, gsr_bits);
__func__, gsr);
return false;
}

View File

@ -165,7 +165,7 @@ static struct snd_pcm_hardware atmel_abdac_hw = {
.buffer_bytes_max = 64 * 4096,
.period_bytes_min = 4096,
.period_bytes_max = 4096,
.periods_min = 4,
.periods_min = 6,
.periods_max = 64,
};
@ -502,7 +502,7 @@ static int __devinit atmel_abdac_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, card);
dev_info(&pdev->dev, "Atmel ABDAC at 0x%p using %s\n",
dac->regs, dac->dma.chan->dev->device.bus_id);
dac->regs, dev_name(&dac->dma.chan->dev->device));
return retval;

View File

@ -1,5 +1,5 @@
/*
* Driver for the Atmel AC97C controller
* Driver for Atmel AC97C
*
* Copyright (C) 2005-2009 Atmel Corporation
*
@ -10,6 +10,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/bitmap.h>
#include <linux/device.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
@ -65,6 +66,7 @@ struct atmel_ac97c {
/* Serialize access to opened variable */
spinlock_t lock;
void __iomem *regs;
int irq;
int opened;
int reset_pin;
};
@ -150,10 +152,10 @@ static struct snd_pcm_hardware atmel_ac97c_hw = {
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = 64 * 4096,
.buffer_bytes_max = 2 * 2 * 64 * 2048,
.period_bytes_min = 4096,
.period_bytes_max = 4096,
.periods_min = 4,
.periods_min = 6,
.periods_max = 64,
};
@ -297,9 +299,11 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long word = 0;
unsigned long word = ac97c_readl(chip, OCA);
int retval;
word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
/* assign channels to AC97C channel A */
switch (runtime->channels) {
case 1:
@ -312,7 +316,6 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
default:
/* TODO: support more than two channels */
return -EINVAL;
break;
}
ac97c_writel(chip, OCA, word);
@ -324,13 +327,25 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
word |= AC97C_CMR_CEM_LITTLE;
break;
case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
default:
word &= ~(AC97C_CMR_CEM_LITTLE);
break;
default:
word = ac97c_readl(chip, OCA);
word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
ac97c_writel(chip, OCA, word);
return -EINVAL;
}
/* Enable underrun interrupt on channel A */
word |= AC97C_CSR_UNRUN;
ac97c_writel(chip, CAMR, word);
/* Enable channel A event interrupt */
word = ac97c_readl(chip, IMR);
word |= AC97C_SR_CAEVT;
ac97c_writel(chip, IER, word);
/* set variable rate if needed */
if (runtime->rate != 48000) {
word = ac97c_readl(chip, MR);
@ -359,9 +374,11 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long word = 0;
unsigned long word = ac97c_readl(chip, ICA);
int retval;
word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
/* assign channels to AC97C channel A */
switch (runtime->channels) {
case 1:
@ -374,7 +391,6 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
default:
/* TODO: support more than two channels */
return -EINVAL;
break;
}
ac97c_writel(chip, ICA, word);
@ -386,13 +402,25 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
word |= AC97C_CMR_CEM_LITTLE;
break;
case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
default:
word &= ~(AC97C_CMR_CEM_LITTLE);
break;
default:
word = ac97c_readl(chip, ICA);
word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
ac97c_writel(chip, ICA, word);
return -EINVAL;
}
/* Enable overrun interrupt on channel A */
word |= AC97C_CSR_OVRUN;
ac97c_writel(chip, CAMR, word);
/* Enable channel A event interrupt */
word = ac97c_readl(chip, IMR);
word |= AC97C_SR_CAEVT;
ac97c_writel(chip, IER, word);
/* set variable rate if needed */
if (runtime->rate != 48000) {
word = ac97c_readl(chip, MR);
@ -543,6 +571,43 @@ static struct snd_pcm_ops atmel_ac97_capture_ops = {
.pointer = atmel_ac97c_capture_pointer,
};
static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev)
{
struct atmel_ac97c *chip = (struct atmel_ac97c *)dev;
irqreturn_t retval = IRQ_NONE;
u32 sr = ac97c_readl(chip, SR);
u32 casr = ac97c_readl(chip, CASR);
u32 cosr = ac97c_readl(chip, COSR);
if (sr & AC97C_SR_CAEVT) {
dev_info(&chip->pdev->dev, "channel A event%s%s%s%s%s%s\n",
casr & AC97C_CSR_OVRUN ? " OVRUN" : "",
casr & AC97C_CSR_RXRDY ? " RXRDY" : "",
casr & AC97C_CSR_UNRUN ? " UNRUN" : "",
casr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "",
casr & AC97C_CSR_TXRDY ? " TXRDY" : "",
!casr ? " NONE" : "");
retval = IRQ_HANDLED;
}
if (sr & AC97C_SR_COEVT) {
dev_info(&chip->pdev->dev, "codec channel event%s%s%s%s%s\n",
cosr & AC97C_CSR_OVRUN ? " OVRUN" : "",
cosr & AC97C_CSR_RXRDY ? " RXRDY" : "",
cosr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "",
cosr & AC97C_CSR_TXRDY ? " TXRDY" : "",
!cosr ? " NONE" : "");
retval = IRQ_HANDLED;
}
if (retval == IRQ_NONE) {
dev_err(&chip->pdev->dev, "spurious interrupt sr 0x%08x "
"casr 0x%08x cosr 0x%08x\n", sr, casr, cosr);
}
return retval;
}
static int __devinit atmel_ac97c_pcm_new(struct atmel_ac97c *chip)
{
struct snd_pcm *pcm;
@ -665,17 +730,17 @@ static bool filter(struct dma_chan *chan, void *slave)
static void atmel_ac97c_reset(struct atmel_ac97c *chip)
{
ac97c_writel(chip, MR, AC97C_MR_WRST);
ac97c_writel(chip, MR, 0);
ac97c_writel(chip, MR, AC97C_MR_ENA);
ac97c_writel(chip, CAMR, 0);
ac97c_writel(chip, COMR, 0);
if (gpio_is_valid(chip->reset_pin)) {
gpio_set_value(chip->reset_pin, 0);
/* AC97 v2.2 specifications says minimum 1 us. */
udelay(10);
udelay(2);
gpio_set_value(chip->reset_pin, 1);
}
udelay(1);
ac97c_writel(chip, MR, AC97C_MR_ENA);
}
static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
@ -690,6 +755,7 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
.read = atmel_ac97c_read,
};
int retval;
int irq;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs) {
@ -703,6 +769,12 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
return -ENXIO;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_dbg(&pdev->dev, "could not get irq\n");
return -ENXIO;
}
pclk = clk_get(&pdev->dev, "pclk");
if (IS_ERR(pclk)) {
dev_dbg(&pdev->dev, "no peripheral clock\n");
@ -719,6 +791,13 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
chip = get_chip(card);
retval = request_irq(irq, atmel_ac97c_interrupt, 0, "AC97C", chip);
if (retval) {
dev_dbg(&pdev->dev, "unable to request irq %d\n", irq);
goto err_request_irq;
}
chip->irq = irq;
spin_lock_init(&chip->lock);
strcpy(card->driver, "Atmel AC97C");
@ -747,14 +826,18 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
snd_card_set_dev(card, &pdev->dev);
atmel_ac97c_reset(chip);
/* Enable overrun interrupt from codec channel */
ac97c_writel(chip, COMR, AC97C_CSR_OVRUN);
ac97c_writel(chip, IER, ac97c_readl(chip, IMR) | AC97C_SR_COEVT);
retval = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus);
if (retval) {
dev_dbg(&pdev->dev, "could not register on ac97 bus\n");
goto err_ac97_bus;
}
atmel_ac97c_reset(chip);
retval = atmel_ac97c_mixer_new(chip);
if (retval) {
dev_dbg(&pdev->dev, "could not register ac97 mixer\n");
@ -773,7 +856,7 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
chip->dma.rx_chan = dma_request_channel(mask, filter, dws);
dev_info(&chip->pdev->dev, "using %s for DMA RX\n",
chip->dma.rx_chan->dev->device.bus_id);
dev_name(&chip->dma.rx_chan->dev->device));
set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
}
@ -789,7 +872,7 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
chip->dma.tx_chan = dma_request_channel(mask, filter, dws);
dev_info(&chip->pdev->dev, "using %s for DMA TX\n",
chip->dma.tx_chan->dev->device.bus_id);
dev_name(&chip->dma.tx_chan->dev->device));
set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
}
@ -809,7 +892,7 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
retval = snd_card_register(card);
if (retval) {
dev_dbg(&pdev->dev, "could not register sound card\n");
goto err_ac97_bus;
goto err_dma;
}
platform_set_drvdata(pdev, card);
@ -836,6 +919,8 @@ err_ac97_bus:
iounmap(chip->regs);
err_ioremap:
free_irq(irq, chip);
err_request_irq:
snd_card_free(card);
err_snd_card_new:
clk_disable(pclk);
@ -884,9 +969,14 @@ static int __devexit atmel_ac97c_remove(struct platform_device *pdev)
if (gpio_is_valid(chip->reset_pin))
gpio_free(chip->reset_pin);
ac97c_writel(chip, CAMR, 0);
ac97c_writel(chip, COMR, 0);
ac97c_writel(chip, MR, 0);
clk_disable(chip->pclk);
clk_put(chip->pclk);
iounmap(chip->regs);
free_irq(chip->irq, chip);
if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
dma_release_channel(chip->dma.rx_chan);

View File

@ -1,5 +1,5 @@
/*
* Register definitions for the Atmel AC97C controller
* Register definitions for Atmel AC97C
*
* Copyright (C) 2005-2009 Atmel Corporation
*
@ -17,10 +17,6 @@
#define AC97C_CATHR 0x24
#define AC97C_CASR 0x28
#define AC97C_CAMR 0x2c
#define AC97C_CBRHR 0x30
#define AC97C_CBTHR 0x34
#define AC97C_CBSR 0x38
#define AC97C_CBMR 0x3c
#define AC97C_CORHR 0x40
#define AC97C_COTHR 0x44
#define AC97C_COSR 0x48
@ -46,8 +42,10 @@
#define AC97C_MR_VRA (1 << 2)
#define AC97C_CSR_TXRDY (1 << 0)
#define AC97C_CSR_TXEMPTY (1 << 1)
#define AC97C_CSR_UNRUN (1 << 2)
#define AC97C_CSR_RXRDY (1 << 4)
#define AC97C_CSR_OVRUN (1 << 5)
#define AC97C_CSR_ENDTX (1 << 10)
#define AC97C_CSR_ENDRX (1 << 14)
@ -61,11 +59,15 @@
#define AC97C_CMR_DMAEN (1 << 22)
#define AC97C_SR_CAEVT (1 << 3)
#define AC97C_SR_COEVT (1 << 2)
#define AC97C_SR_WKUP (1 << 1)
#define AC97C_SR_SOF (1 << 0)
#define AC97C_CH_MASK(slot) \
(0x7 << (3 * (AC97_SLOT_##slot - 3)))
#define AC97C_CH_ASSIGN(slot, channel) \
(AC97C_CHANNEL_##channel << (3 * (AC97_SLOT_##slot - 3)))
#define AC97C_CHANNEL_NONE 0x0
#define AC97C_CHANNEL_A 0x1
#define AC97C_CHANNEL_B 0x2
#endif /* __SOUND_ATMEL_AC97C_H */

View File

@ -703,19 +703,27 @@ static int snd_mixer_oss_put_volume1(struct snd_mixer_oss_file *fmixer,
if (left || right) {
if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH)
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
if (slot->present & SNDRV_MIXER_OSS_PRESENT_CSWITCH)
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], left, right, 0);
if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH)
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE)
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
if (slot->present & SNDRV_MIXER_OSS_PRESENT_CROUTE)
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], left, right, 1);
if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE)
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
} else {
if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) {
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) {
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], left, right, 0);
} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) {
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) {
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CROUTE) {
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], left, right, 1);
} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) {
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
}

View File

@ -481,6 +481,7 @@ OPL3SA2_DOUBLE_TLV("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1,
OPL3SA2_SINGLE("Mic Playback Switch", 0, 0x09, 7, 1, 1),
OPL3SA2_SINGLE_TLV("Mic Playback Volume", 0, 0x09, 0, 31, 1,
db_scale_5bit_12db_max),
OPL3SA2_SINGLE("ZV Port Switch", 0, 0x02, 0, 1, 0),
};
static struct snd_kcontrol_new snd_opl3sa2_tone_controls[] = {

View File

@ -3256,7 +3256,7 @@ static const char *ad1884_slave_vols[] = {
"Mic Playback Volume",
"CD Playback Volume",
"Internal Mic Playback Volume",
"Docking Mic Playback Volume"
"Docking Mic Playback Volume",
/* "Beep Playback Volume", */
"IEC958 Playback Volume",
NULL

View File

@ -8764,6 +8764,10 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
{}
};
static hda_nid_t alc883_slave_dig_outs[] = {
ALC1200_DIGOUT_NID, 0,
};
static hda_nid_t alc1200_slave_dig_outs[] = {
ALC883_DIGOUT_NID, 0,
};
@ -8809,6 +8813,7 @@ static struct alc_config_preset alc883_presets[] = {
.dac_nids = alc883_dac_nids,
.dig_out_nid = ALC883_DIGOUT_NID,
.dig_in_nid = ALC883_DIGIN_NID,
.slave_dig_outs = alc883_slave_dig_outs,
.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_intel_modes),
.channel_mode = alc883_3ST_6ch_intel_modes,
.need_dac_fix = 1,

View File

@ -4413,6 +4413,24 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
if (spec->num_pwrs > 0)
stac92xx_pin_sense(codec, event->nid);
stac92xx_report_jack(codec, event->nid);
switch (codec->subsystem_id) {
case 0x103c308f:
if (event->nid == 0xb) {
int pin = AC_PINCTL_IN_EN;
if (get_pin_presence(codec, 0xa)
&& get_pin_presence(codec, 0xb))
pin |= AC_PINCTL_VREF_80;
if (!get_pin_presence(codec, 0xb))
pin |= AC_PINCTL_VREF_80;
/* toggle VREF state based on mic + hp pin
* status
*/
stac92xx_auto_set_pinctl(codec, 0x0a, pin);
}
}
break;
case STAC_VREF_EVENT:
data = snd_hda_codec_read(codec, codec->afg, 0,
@ -4895,6 +4913,7 @@ again:
switch (codec->vendor_id) {
case 0x111d7604:
case 0x111d7605:
case 0x111d76d5:
if (spec->board_config == STAC_92HD83XXX_PWR_REF)
break;
spec->num_pwrs = 0;
@ -5707,6 +5726,7 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
{ .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx},
{ .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx},
{ .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx},
{ .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx},
{ .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},
{ .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
{ .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },

View File

@ -51,7 +51,7 @@ static struct platform_device *device;
/*
*/
static int __init snd_pmac_probe(struct platform_device *devptr)
static int __devinit snd_pmac_probe(struct platform_device *devptr)
{
struct snd_card *card;
struct snd_pmac *chip;

View File

@ -659,7 +659,8 @@ static int ak4535_remove(struct platform_device *pdev)
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_unregister_device(codec->control_data);
if (codec->control_data)
i2c_unregister_device(codec->control_data);
i2c_del_driver(&ak4535_i2c_driver);
#endif
kfree(codec->private_data);

View File

@ -122,6 +122,9 @@ struct twl4030_priv {
unsigned int bypass_state;
unsigned int codec_powered;
unsigned int codec_muted;
struct snd_pcm_substream *master_substream;
struct snd_pcm_substream *slave_substream;
};
/*
@ -1217,6 +1220,50 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
static int twl4030_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct twl4030_priv *twl4030 = codec->private_data;
/* If we already have a playback or capture going then constrain
* this substream to match it.
*/
if (twl4030->master_substream) {
struct snd_pcm_runtime *master_runtime;
master_runtime = twl4030->master_substream->runtime;
snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
master_runtime->rate,
master_runtime->rate);
snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
master_runtime->sample_bits,
master_runtime->sample_bits);
twl4030->slave_substream = substream;
} else
twl4030->master_substream = substream;
return 0;
}
static void twl4030_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct twl4030_priv *twl4030 = codec->private_data;
if (twl4030->master_substream == substream)
twl4030->master_substream = twl4030->slave_substream;
twl4030->slave_substream = NULL;
}
static int twl4030_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@ -1224,8 +1271,13 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
struct twl4030_priv *twl4030 = codec->private_data;
u8 mode, old_mode, format, old_format;
if (substream == twl4030->slave_substream)
/* Ignoring hw_params for slave substream */
return 0;
/* bit rate */
old_mode = twl4030_read_reg_cache(codec,
TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
@ -1259,6 +1311,9 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
case 48000:
mode |= TWL4030_APLL_RATE_48000;
break;
case 96000:
mode |= TWL4030_APLL_RATE_96000;
break;
default:
printk(KERN_ERR "TWL4030 hw params: unknown rate %d\n",
params_rate(params));
@ -1384,6 +1439,8 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE)
static struct snd_soc_dai_ops twl4030_dai_ops = {
.startup = twl4030_startup,
.shutdown = twl4030_shutdown,
.hw_params = twl4030_hw_params,
.set_sysclk = twl4030_set_dai_sysclk,
.set_fmt = twl4030_set_dai_fmt,
@ -1395,7 +1452,7 @@ struct snd_soc_dai twl4030_dai = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = TWL4030_RATES,
.rates = TWL4030_RATES | SNDRV_PCM_RATE_96000,
.formats = TWL4030_FORMATS,},
.capture = {
.stream_name = "Capture",

View File

@ -109,6 +109,7 @@
#define TWL4030_APLL_RATE_32000 0x80
#define TWL4030_APLL_RATE_44100 0x90
#define TWL4030_APLL_RATE_48000 0xA0
#define TWL4030_APLL_RATE_96000 0xE0
#define TWL4030_SEL_16K 0x04
#define TWL4030_CODECPDZ 0x02
#define TWL4030_OPT_MODE 0x01

View File

@ -317,6 +317,41 @@ static int wm9705_reset(struct snd_soc_codec *codec)
return -EIO;
}
#ifdef CONFIG_PM
static int wm9705_soc_suspend(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
soc_ac97_ops.write(codec->ac97, AC97_POWERDOWN, 0xffff);
return 0;
}
static int wm9705_soc_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
int i, ret;
u16 *cache = codec->reg_cache;
ret = wm9705_reset(codec);
if (ret < 0) {
printk(KERN_ERR "could not reset AC97 codec\n");
return ret;
}
for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) {
soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
}
return 0;
}
#else
#define wm9705_soc_suspend NULL
#define wm9705_soc_resume NULL
#endif
static int wm9705_soc_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@ -407,6 +442,8 @@ static int wm9705_soc_remove(struct platform_device *pdev)
struct snd_soc_codec_device soc_codec_dev_wm9705 = {
.probe = wm9705_soc_probe,
.remove = wm9705_soc_remove,
.suspend = wm9705_soc_suspend,
.resume = wm9705_soc_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm9705);

View File

@ -300,7 +300,7 @@ static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = fsl_dma_dmamask;
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev,
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev,
fsl_dma_hardware.buffer_bytes_max,
&pcm->streams[0].substream->dma_buffer);
if (ret) {
@ -310,7 +310,7 @@ static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
return -ENOMEM;
}
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev,
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev,
fsl_dma_hardware.buffer_bytes_max,
&pcm->streams[1].substream->dma_buffer);
if (ret) {
@ -418,7 +418,7 @@ static int fsl_dma_open(struct snd_pcm_substream *substream)
return -EBUSY;
}
dma_private = dma_alloc_coherent(substream->pcm->dev,
dma_private = dma_alloc_coherent(substream->pcm->card->dev,
sizeof(struct fsl_dma_private), &ld_buf_phys, GFP_KERNEL);
if (!dma_private) {
dev_err(substream->pcm->card->dev,
@ -445,7 +445,7 @@ static int fsl_dma_open(struct snd_pcm_substream *substream)
dev_err(substream->pcm->card->dev,
"can't register ISR for IRQ %u (ret=%i)\n",
dma_private->irq, ret);
dma_free_coherent(substream->pcm->dev,
dma_free_coherent(substream->pcm->card->dev,
sizeof(struct fsl_dma_private),
dma_private, dma_private->ld_buf_phys);
return ret;
@ -697,6 +697,23 @@ static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream)
else
position = in_be32(&dma_channel->dar);
/*
* When capture is started, the SSI immediately starts to fill its FIFO.
* This means that the DMA controller is not started until the FIFO is
* full. However, ALSA calls this function before that happens, when
* MR.DAR is still zero. In this case, just return zero to indicate
* that nothing has been received yet.
*/
if (!position)
return 0;
if ((position < dma_private->dma_buf_phys) ||
(position > dma_private->dma_buf_end)) {
dev_err(substream->pcm->card->dev,
"dma pointer is out of range, halting stream\n");
return SNDRV_PCM_POS_XRUN;
}
frames = bytes_to_frames(runtime, position - dma_private->dma_buf_phys);
/*
@ -761,13 +778,13 @@ static int fsl_dma_close(struct snd_pcm_substream *substream)
free_irq(dma_private->irq, dma_private);
if (dma_private->ld_buf_phys) {
dma_unmap_single(substream->pcm->dev,
dma_unmap_single(substream->pcm->card->dev,
dma_private->ld_buf_phys,
sizeof(dma_private->link), DMA_TO_DEVICE);
}
/* Deallocate the fsl_dma_private structure */
dma_free_coherent(substream->pcm->dev,
dma_free_coherent(substream->pcm->card->dev,
sizeof(struct fsl_dma_private),
dma_private, dma_private->ld_buf_phys);
substream->runtime->private_data = NULL;

View File

@ -60,6 +60,13 @@
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
#endif
/* SIER bitflag of interrupts to enable */
#define SIER_FLAGS (CCSR_SSI_SIER_TFRC_EN | CCSR_SSI_SIER_TDMAE | \
CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TUE0_EN | \
CCSR_SSI_SIER_TUE1_EN | CCSR_SSI_SIER_RFRC_EN | \
CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE | \
CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN)
/**
* fsl_ssi_private: per-SSI private data
*
@ -140,7 +147,7 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
were interrupted for. We mask it with the Interrupt Enable register
so that we only check for events that we're interested in.
*/
sisr = in_be32(&ssi->sisr) & in_be32(&ssi->sier);
sisr = in_be32(&ssi->sisr) & SIER_FLAGS;
if (sisr & CCSR_SSI_SISR_RFRC) {
ssi_private->stats.rfrc++;
@ -324,12 +331,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
*/
/* 4. Enable the interrupts and DMA requests */
out_be32(&ssi->sier,
CCSR_SSI_SIER_TFRC_EN | CCSR_SSI_SIER_TDMAE |
CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TUE0_EN |
CCSR_SSI_SIER_TUE1_EN | CCSR_SSI_SIER_RFRC_EN |
CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE |
CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN);
out_be32(&ssi->sier, SIER_FLAGS);
/*
* Set the watermark for transmit FIFI 0 and receive FIFO 0. We
@ -466,28 +468,12 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_START:
clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
setbits32(&ssi->scr,
CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
} else {
long timeout = jiffies + 10;
else
setbits32(&ssi->scr,
CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
/* Wait until the SSI has filled its FIFO. Without this
* delay, ALSA complains about overruns. When the FIFO
* is full, the DMA controller initiates its first
* transfer. Until then, however, the DMA's DAR
* register is zero, which translates to an
* out-of-bounds pointer. This makes ALSA think an
* overrun has occurred.
*/
while (!(in_be32(&ssi->sisr) & CCSR_SSI_SISR_RFF0) &&
(jiffies < timeout));
if (!(in_be32(&ssi->sisr) & CCSR_SSI_SISR_RFF0))
return -EIO;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
@ -606,39 +592,52 @@ static struct snd_soc_dai fsl_ssi_dai_template = {
.ops = &fsl_ssi_dai_ops,
};
/* Show the statistics of a flag only if its interrupt is enabled. The
* compiler will optimze this code to a no-op if the interrupt is not
* enabled.
*/
#define SIER_SHOW(flag, name) \
do { \
if (SIER_FLAGS & CCSR_SSI_SIER_##flag) \
length += sprintf(buf + length, #name "=%u\n", \
ssi_private->stats.name); \
} while (0)
/**
* fsl_sysfs_ssi_show: display SSI statistics
*
* Display the statistics for the current SSI device.
* Display the statistics for the current SSI device. To avoid confusion,
* we only show those counts that are enabled.
*/
static ssize_t fsl_sysfs_ssi_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fsl_ssi_private *ssi_private =
container_of(attr, struct fsl_ssi_private, dev_attr);
ssize_t length;
container_of(attr, struct fsl_ssi_private, dev_attr);
ssize_t length = 0;
length = sprintf(buf, "rfrc=%u", ssi_private->stats.rfrc);
length += sprintf(buf + length, "\ttfrc=%u", ssi_private->stats.tfrc);
length += sprintf(buf + length, "\tcmdau=%u", ssi_private->stats.cmdau);
length += sprintf(buf + length, "\tcmddu=%u", ssi_private->stats.cmddu);
length += sprintf(buf + length, "\trxt=%u", ssi_private->stats.rxt);
length += sprintf(buf + length, "\trdr1=%u", ssi_private->stats.rdr1);
length += sprintf(buf + length, "\trdr0=%u", ssi_private->stats.rdr0);
length += sprintf(buf + length, "\ttde1=%u", ssi_private->stats.tde1);
length += sprintf(buf + length, "\ttde0=%u", ssi_private->stats.tde0);
length += sprintf(buf + length, "\troe1=%u", ssi_private->stats.roe1);
length += sprintf(buf + length, "\troe0=%u", ssi_private->stats.roe0);
length += sprintf(buf + length, "\ttue1=%u", ssi_private->stats.tue1);
length += sprintf(buf + length, "\ttue0=%u", ssi_private->stats.tue0);
length += sprintf(buf + length, "\ttfs=%u", ssi_private->stats.tfs);
length += sprintf(buf + length, "\trfs=%u", ssi_private->stats.rfs);
length += sprintf(buf + length, "\ttls=%u", ssi_private->stats.tls);
length += sprintf(buf + length, "\trls=%u", ssi_private->stats.rls);
length += sprintf(buf + length, "\trff1=%u", ssi_private->stats.rff1);
length += sprintf(buf + length, "\trff0=%u", ssi_private->stats.rff0);
length += sprintf(buf + length, "\ttfe1=%u", ssi_private->stats.tfe1);
length += sprintf(buf + length, "\ttfe0=%u\n", ssi_private->stats.tfe0);
SIER_SHOW(RFRC_EN, rfrc);
SIER_SHOW(TFRC_EN, tfrc);
SIER_SHOW(CMDAU_EN, cmdau);
SIER_SHOW(CMDDU_EN, cmddu);
SIER_SHOW(RXT_EN, rxt);
SIER_SHOW(RDR1_EN, rdr1);
SIER_SHOW(RDR0_EN, rdr0);
SIER_SHOW(TDE1_EN, tde1);
SIER_SHOW(TDE0_EN, tde0);
SIER_SHOW(ROE1_EN, roe1);
SIER_SHOW(ROE0_EN, roe0);
SIER_SHOW(TUE1_EN, tue1);
SIER_SHOW(TUE0_EN, tue0);
SIER_SHOW(TFS_EN, tfs);
SIER_SHOW(RFS_EN, rfs);
SIER_SHOW(TLS_EN, tls);
SIER_SHOW(RLS_EN, rls);
SIER_SHOW(RFF1_EN, rff1);
SIER_SHOW(RFF0_EN, rff0);
SIER_SHOW(TFE1_EN, tfe1);
SIER_SHOW(TFE0_EN, tfe0);
return length;
}

View File

@ -146,6 +146,17 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
int err = 0;
if (cpu_is_omap343x() && mcbsp_data->bus_id == 1) {
/*
* McBSP2 in OMAP3 has 1024 * 32-bit internal audio buffer.
* Set constraint for minimum buffer size to the same than FIFO
* size in order to avoid underruns in playback startup because
* HW is keeping the DMA request active until FIFO is filled.
*/
snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 4096, UINT_MAX);
}
if (!cpu_dai->active)
err = omap_mcbsp_request(mcbsp_data->bus_id);

View File

@ -116,6 +116,16 @@ config SND_SOC_ZYLONITE
Say Y if you want to add support for SoC audio on the
Marvell Zylonite reference platform.
config SND_PXA2XX_SOC_MAGICIAN
tristate "SoC Audio support for HTC Magician"
depends on SND_PXA2XX_SOC && MACH_MAGICIAN
select SND_PXA2XX_SOC_I2S
select SND_PXA_SOC_SSP
select SND_SOC_UDA1380
help
Say Y if you want to add support for SoC audio on the
HTC Magician.
config SND_PXA2XX_SOC_MIOA701
tristate "SoC Audio support for MIO A701"
depends on SND_PXA2XX_SOC && MACH_MIOA701

View File

@ -20,6 +20,7 @@ snd-soc-spitz-objs := spitz.o
snd-soc-em-x270-objs := em-x270.o
snd-soc-palm27x-objs := palm27x.o
snd-soc-zylonite-objs := zylonite.o
snd-soc-magician-objs := magician.o
snd-soc-mioa701-objs := mioa701_wm9713.o
obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
@ -31,5 +32,6 @@ obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o
obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o
obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o
obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o
obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o

560
sound/soc/pxa/magician.c Normal file
View File

@ -0,0 +1,560 @@
/*
* SoC audio for HTC Magician
*
* Copyright (c) 2006 Philipp Zabel <philipp.zabel@gmail.com>
*
* based on spitz.c,
* Authors: Liam Girdwood <lrg@slimlogic.co.uk>
* Richard Purdie <richard@openedhand.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <mach/pxa-regs.h>
#include <mach/hardware.h>
#include <mach/magician.h>
#include <asm/mach-types.h>
#include "../codecs/uda1380.h"
#include "pxa2xx-pcm.h"
#include "pxa2xx-i2s.h"
#include "pxa-ssp.h"
#define MAGICIAN_MIC 0
#define MAGICIAN_MIC_EXT 1
static int magician_hp_switch;
static int magician_spk_switch = 1;
static int magician_in_sel = MAGICIAN_MIC;
static void magician_ext_control(struct snd_soc_codec *codec)
{
if (magician_spk_switch)
snd_soc_dapm_enable_pin(codec, "Speaker");
else
snd_soc_dapm_disable_pin(codec, "Speaker");
if (magician_hp_switch)
snd_soc_dapm_enable_pin(codec, "Headphone Jack");
else
snd_soc_dapm_disable_pin(codec, "Headphone Jack");
switch (magician_in_sel) {
case MAGICIAN_MIC:
snd_soc_dapm_disable_pin(codec, "Headset Mic");
snd_soc_dapm_enable_pin(codec, "Call Mic");
break;
case MAGICIAN_MIC_EXT:
snd_soc_dapm_disable_pin(codec, "Call Mic");
snd_soc_dapm_enable_pin(codec, "Headset Mic");
break;
}
snd_soc_dapm_sync(codec);
}
static int magician_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->socdev->card->codec;
/* check the jack status at stream startup */
magician_ext_control(codec);
return 0;
}
/*
* Magician uses SSP port for playback.
*/
static int magician_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
unsigned int acps, acds, width, rate;
unsigned int div4 = PXA_SSP_CLK_SCDB_4;
int ret = 0;
rate = params_rate(params);
width = snd_pcm_format_physical_width(params_format(params));
/*
* rate = SSPSCLK / (2 * width(16 or 32))
* SSPSCLK = (ACPS / ACDS) / SSPSCLKDIV(div4 or div1)
*/
switch (params_rate(params)) {
case 8000:
/* off by a factor of 2: bug in the PXA27x audio clock? */
acps = 32842000;
switch (width) {
case 16:
/* 513156 Hz ~= _2_ * 8000 Hz * 32 (+0.23%) */
acds = PXA_SSP_CLK_AUDIO_DIV_16;
break;
case 32:
/* 1026312 Hz ~= _2_ * 8000 Hz * 64 (+0.23%) */
acds = PXA_SSP_CLK_AUDIO_DIV_8;
}
break;
case 11025:
acps = 5622000;
switch (width) {
case 16:
/* 351375 Hz ~= 11025 Hz * 32 (-0.41%) */
acds = PXA_SSP_CLK_AUDIO_DIV_4;
break;
case 32:
/* 702750 Hz ~= 11025 Hz * 64 (-0.41%) */
acds = PXA_SSP_CLK_AUDIO_DIV_2;
}
break;
case 22050:
acps = 5622000;
switch (width) {
case 16:
/* 702750 Hz ~= 22050 Hz * 32 (-0.41%) */
acds = PXA_SSP_CLK_AUDIO_DIV_2;
break;
case 32:
/* 1405500 Hz ~= 22050 Hz * 64 (-0.41%) */
acds = PXA_SSP_CLK_AUDIO_DIV_1;
}
break;
case 44100:
acps = 5622000;
switch (width) {
case 16:
/* 1405500 Hz ~= 44100 Hz * 32 (-0.41%) */
acds = PXA_SSP_CLK_AUDIO_DIV_2;
break;
case 32:
/* 2811000 Hz ~= 44100 Hz * 64 (-0.41%) */
acds = PXA_SSP_CLK_AUDIO_DIV_1;
}
break;
case 48000:
acps = 12235000;
switch (width) {
case 16:
/* 1529375 Hz ~= 48000 Hz * 32 (-0.44%) */
acds = PXA_SSP_CLK_AUDIO_DIV_2;
break;
case 32:
/* 3058750 Hz ~= 48000 Hz * 64 (-0.44%) */
acds = PXA_SSP_CLK_AUDIO_DIV_1;
}
break;
case 96000:
acps = 12235000;
switch (width) {
case 16:
/* 3058750 Hz ~= 96000 Hz * 32 (-0.44%) */
acds = PXA_SSP_CLK_AUDIO_DIV_1;
break;
case 32:
/* 6117500 Hz ~= 96000 Hz * 64 (-0.44%) */
acds = PXA_SSP_CLK_AUDIO_DIV_2;
div4 = PXA_SSP_CLK_SCDB_1;
break;
}
break;
}
/* set codec DAI configuration */
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_MSB |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 1);
if (ret < 0)
return ret;
/* set audio clock as clock source */
ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0,
SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;
/* set the SSP audio system clock ACDS divider */
ret = snd_soc_dai_set_clkdiv(cpu_dai,
PXA_SSP_AUDIO_DIV_ACDS, acds);
if (ret < 0)
return ret;
/* set the SSP audio system clock SCDB divider4 */
ret = snd_soc_dai_set_clkdiv(cpu_dai,
PXA_SSP_AUDIO_DIV_SCDB, div4);
if (ret < 0)
return ret;
/* set SSP audio pll clock */
ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, acps);
if (ret < 0)
return ret;
return 0;
}
/*
* Magician uses I2S for capture.
*/
static int magician_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
int ret = 0;
/* set codec DAI configuration */
ret = snd_soc_dai_set_fmt(codec_dai,
SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai,
SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set the I2S system clock as output */
ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_ops magician_capture_ops = {
.startup = magician_startup,
.hw_params = magician_capture_hw_params,
};
static struct snd_soc_ops magician_playback_ops = {
.startup = magician_startup,
.hw_params = magician_playback_hw_params,
};
static int magician_get_hp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = magician_hp_switch;
return 0;
}
static int magician_set_hp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
if (magician_hp_switch == ucontrol->value.integer.value[0])
return 0;
magician_hp_switch = ucontrol->value.integer.value[0];
magician_ext_control(codec);
return 1;
}
static int magician_get_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = magician_spk_switch;
return 0;
}
static int magician_set_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
if (magician_spk_switch == ucontrol->value.integer.value[0])
return 0;
magician_spk_switch = ucontrol->value.integer.value[0];
magician_ext_control(codec);
return 1;
}
static int magician_get_input(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = magician_in_sel;
return 0;
}
static int magician_set_input(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
if (magician_in_sel == ucontrol->value.integer.value[0])
return 0;
magician_in_sel = ucontrol->value.integer.value[0];
switch (magician_in_sel) {
case MAGICIAN_MIC:
gpio_set_value(EGPIO_MAGICIAN_IN_SEL1, 1);
break;
case MAGICIAN_MIC_EXT:
gpio_set_value(EGPIO_MAGICIAN_IN_SEL1, 0);
}
return 1;
}
static int magician_spk_power(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
static int magician_hp_power(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
gpio_set_value(EGPIO_MAGICIAN_EP_POWER, SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
static int magician_mic_bias(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
/* magician machine dapm widgets */
static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", magician_hp_power),
SND_SOC_DAPM_SPK("Speaker", magician_spk_power),
SND_SOC_DAPM_MIC("Call Mic", magician_mic_bias),
SND_SOC_DAPM_MIC("Headset Mic", magician_mic_bias),
};
/* magician machine audio_map */
static const struct snd_soc_dapm_route audio_map[] = {
/* Headphone connected to VOUTL, VOUTR */
{"Headphone Jack", NULL, "VOUTL"},
{"Headphone Jack", NULL, "VOUTR"},
/* Speaker connected to VOUTL, VOUTR */
{"Speaker", NULL, "VOUTL"},
{"Speaker", NULL, "VOUTR"},
/* Mics are connected to VINM */
{"VINM", NULL, "Headset Mic"},
{"VINM", NULL, "Call Mic"},
};
static const char *input_select[] = {"Call Mic", "Headset Mic"};
static const struct soc_enum magician_in_sel_enum =
SOC_ENUM_SINGLE_EXT(2, input_select);
static const struct snd_kcontrol_new uda1380_magician_controls[] = {
SOC_SINGLE_BOOL_EXT("Headphone Switch",
(unsigned long)&magician_hp_switch,
magician_get_hp, magician_set_hp),
SOC_SINGLE_BOOL_EXT("Speaker Switch",
(unsigned long)&magician_spk_switch,
magician_get_spk, magician_set_spk),
SOC_ENUM_EXT("Input Select", magician_in_sel_enum,
magician_get_input, magician_set_input),
};
/*
* Logic for a uda1380 as connected on a HTC Magician
*/
static int magician_uda1380_init(struct snd_soc_codec *codec)
{
int err;
/* NC codec pins */
snd_soc_dapm_nc_pin(codec, "VOUTLHP");
snd_soc_dapm_nc_pin(codec, "VOUTRHP");
/* FIXME: is anything connected here? */
snd_soc_dapm_nc_pin(codec, "VINL");
snd_soc_dapm_nc_pin(codec, "VINR");
/* Add magician specific controls */
err = snd_soc_add_controls(codec, uda1380_magician_controls,
ARRAY_SIZE(uda1380_magician_controls));
if (err < 0)
return err;
/* Add magician specific widgets */
snd_soc_dapm_new_controls(codec, uda1380_dapm_widgets,
ARRAY_SIZE(uda1380_dapm_widgets));
/* Set up magician specific audio path interconnects */
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_sync(codec);
return 0;
}
/* magician digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link magician_dai[] = {
{
.name = "uda1380",
.stream_name = "UDA1380 Playback",
.cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP1],
.codec_dai = &uda1380_dai[UDA1380_DAI_PLAYBACK],
.init = magician_uda1380_init,
.ops = &magician_playback_ops,
},
{
.name = "uda1380",
.stream_name = "UDA1380 Capture",
.cpu_dai = &pxa_i2s_dai,
.codec_dai = &uda1380_dai[UDA1380_DAI_CAPTURE],
.ops = &magician_capture_ops,
}
};
/* magician audio machine driver */
static struct snd_soc_card snd_soc_card_magician = {
.name = "Magician",
.dai_link = magician_dai,
.num_links = ARRAY_SIZE(magician_dai),
.platform = &pxa2xx_soc_platform,
};
/* magician audio private data */
static struct uda1380_setup_data magician_uda1380_setup = {
.i2c_address = 0x18,
.dac_clk = UDA1380_DAC_CLK_WSPLL,
};
/* magician audio subsystem */
static struct snd_soc_device magician_snd_devdata = {
.card = &snd_soc_card_magician,
.codec_dev = &soc_codec_dev_uda1380,
.codec_data = &magician_uda1380_setup,
};
static struct platform_device *magician_snd_device;
static int __init magician_init(void)
{
int ret;
if (!machine_is_magician())
return -ENODEV;
ret = gpio_request(EGPIO_MAGICIAN_CODEC_POWER, "CODEC_POWER");
if (ret)
goto err_request_power;
ret = gpio_request(EGPIO_MAGICIAN_CODEC_RESET, "CODEC_RESET");
if (ret)
goto err_request_reset;
ret = gpio_request(EGPIO_MAGICIAN_SPK_POWER, "SPK_POWER");
if (ret)
goto err_request_spk;
ret = gpio_request(EGPIO_MAGICIAN_EP_POWER, "EP_POWER");
if (ret)
goto err_request_ep;
ret = gpio_request(EGPIO_MAGICIAN_MIC_POWER, "MIC_POWER");
if (ret)
goto err_request_mic;
ret = gpio_request(EGPIO_MAGICIAN_IN_SEL0, "IN_SEL0");
if (ret)
goto err_request_in_sel0;
ret = gpio_request(EGPIO_MAGICIAN_IN_SEL1, "IN_SEL1");
if (ret)
goto err_request_in_sel1;
gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 1);
gpio_set_value(EGPIO_MAGICIAN_IN_SEL0, 0);
/* we may need to have the clock running here - pH5 */
gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 1);
udelay(5);
gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 0);
magician_snd_device = platform_device_alloc("soc-audio", -1);
if (!magician_snd_device) {
ret = -ENOMEM;
goto err_pdev;
}
platform_set_drvdata(magician_snd_device, &magician_snd_devdata);
magician_snd_devdata.dev = &magician_snd_device->dev;
ret = platform_device_add(magician_snd_device);
if (ret) {
platform_device_put(magician_snd_device);
goto err_pdev;
}
return 0;
err_pdev:
gpio_free(EGPIO_MAGICIAN_IN_SEL1);
err_request_in_sel1:
gpio_free(EGPIO_MAGICIAN_IN_SEL0);
err_request_in_sel0:
gpio_free(EGPIO_MAGICIAN_MIC_POWER);
err_request_mic:
gpio_free(EGPIO_MAGICIAN_EP_POWER);
err_request_ep:
gpio_free(EGPIO_MAGICIAN_SPK_POWER);
err_request_spk:
gpio_free(EGPIO_MAGICIAN_CODEC_RESET);
err_request_reset:
gpio_free(EGPIO_MAGICIAN_CODEC_POWER);
err_request_power:
return ret;
}
static void __exit magician_exit(void)
{
platform_device_unregister(magician_snd_device);
gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, 0);
gpio_set_value(EGPIO_MAGICIAN_EP_POWER, 0);
gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, 0);
gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 0);
gpio_free(EGPIO_MAGICIAN_IN_SEL1);
gpio_free(EGPIO_MAGICIAN_IN_SEL0);
gpio_free(EGPIO_MAGICIAN_MIC_POWER);
gpio_free(EGPIO_MAGICIAN_EP_POWER);
gpio_free(EGPIO_MAGICIAN_SPK_POWER);
gpio_free(EGPIO_MAGICIAN_CODEC_RESET);
gpio_free(EGPIO_MAGICIAN_CODEC_POWER);
}
module_init(magician_init);
module_exit(magician_exit);
MODULE_AUTHOR("Philipp Zabel");
MODULE_DESCRIPTION("ALSA SoC Magician");
MODULE_LICENSE("GPL");

View File

@ -627,12 +627,18 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
u32 sscr0;
u32 sspsp;
int width = snd_pcm_format_physical_width(params_format(params));
int ttsa = ssp_read_reg(ssp, SSTSA) & 0xf;
/* select correct DMA params */
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
dma = 1; /* capture DMA offset is 1,3 */
if (chn == 2)
dma += 2; /* stereo DMA offset is 2, mono is 0 */
/* Network mode with one active slot (ttsa == 1) can be used
* to force 16-bit frame width on the wire (for S16_LE), even
* with two channels. Use 16-bit DMA transfers for this case.
*/
if (((chn == 2) && (ttsa != 1)) || (width == 32))
dma += 2; /* 32-bit DMA offset is 2, 16-bit is 0 */
cpu_dai->dma_data = ssp_dma_params[cpu_dai->id][dma];
dev_dbg(&ssp->pdev->dev, "pxa_ssp_hw_params: dma %d\n", dma);
@ -712,7 +718,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
/* When we use a network mode, we always require TDM slots
* - complain loudly and fail if they've not been set up yet.
*/
if ((sscr0 & SSCR0_MOD) && !(ssp_read_reg(ssp, SSTSA) & 0xf)) {
if ((sscr0 & SSCR0_MOD) && !ttsa) {
dev_err(&ssp->pdev->dev, "No TDM timeslot configured\n");
return -EINVAL;
}

View File

@ -98,7 +98,7 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
int err;
codec->ac97->dev.bus = &ac97_bus_type;
codec->ac97->dev.parent = NULL;
codec->ac97->dev.parent = codec->card->dev;
codec->ac97->dev.release = soc_ac97_device_release;
dev_set_name(&codec->ac97->dev, "%d-%d:%s",
@ -767,11 +767,21 @@ static int soc_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_card *card = socdev->card;
struct snd_soc_dai *cpu_dai = card->dai_link[0].cpu_dai;
dev_dbg(socdev->dev, "scheduling resume work\n");
if (!schedule_work(&card->deferred_resume_work))
dev_err(socdev->dev, "resume work item may be lost\n");
/* AC97 devices might have other drivers hanging off them so
* need to resume immediately. Other drivers don't have that
* problem and may take a substantial amount of time to resume
* due to I/O costs and anti-pop so handle them out of line.
*/
if (cpu_dai->ac97_control) {
dev_dbg(socdev->dev, "Resuming AC97 immediately\n");
soc_resume_deferred(&card->deferred_resume_work);
} else {
dev_dbg(socdev->dev, "Scheduling resume work\n");
if (!schedule_work(&card->deferred_resume_work))
dev_err(socdev->dev, "resume work item may be lost\n");
}
return 0;
}

View File

@ -121,6 +121,7 @@ struct audioformat {
unsigned char attributes; /* corresponding attributes of cs endpoint */
unsigned char endpoint; /* endpoint */
unsigned char ep_attr; /* endpoint attributes */
unsigned char datainterval; /* log_2 of data packet interval */
unsigned int maxpacksize; /* max. packet size */
unsigned int rates; /* rate bitmasks */
unsigned int rate_min, rate_max; /* min/max rates */
@ -170,7 +171,6 @@ struct snd_usb_substream {
unsigned int curframesize; /* current packet size in frames (for capture) */
unsigned int fill_max: 1; /* fill max packet size always */
unsigned int fmt_type; /* USB audio format type (1-3) */
unsigned int packs_per_ms; /* packets per millisecond (for playback) */
unsigned int running: 1; /* running status */
@ -607,9 +607,7 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
break;
}
}
/* finish at the frame boundary at/after the period boundary */
if (period_elapsed &&
(i & (subs->packs_per_ms - 1)) == subs->packs_per_ms - 1)
if (period_elapsed) /* finish at the period boundary */
break;
}
if (subs->hwptr_done + offs > runtime->buffer_size) {
@ -1067,7 +1065,6 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri
packs_per_ms = 8 >> subs->datainterval;
else
packs_per_ms = 1;
subs->packs_per_ms = packs_per_ms;
if (is_playback) {
urb_packs = max(nrpacks, 1);
@ -1087,18 +1084,17 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri
minsize -= minsize >> 3;
minsize = max(minsize, 1u);
total_packs = (period_bytes + minsize - 1) / minsize;
/* round up to multiple of packs_per_ms */
total_packs = (total_packs + packs_per_ms - 1)
& ~(packs_per_ms - 1);
/* we need at least two URBs for queueing */
if (total_packs < 2 * packs_per_ms) {
total_packs = 2 * packs_per_ms;
if (total_packs < 2) {
total_packs = 2;
} else {
/* and we don't want too long a queue either */
maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
total_packs = min(total_packs, maxpacks);
}
} else {
while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
urb_packs >>= 1;
total_packs = MAX_URBS * urb_packs;
}
subs->nurbs = (total_packs + urb_packs - 1) / urb_packs;
@ -1350,12 +1346,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
subs->datapipe = usb_sndisocpipe(dev, ep);
else
subs->datapipe = usb_rcvisocpipe(dev, ep);
if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH &&
get_endpoint(alts, 0)->bInterval >= 1 &&
get_endpoint(alts, 0)->bInterval <= 4)
subs->datainterval = get_endpoint(alts, 0)->bInterval - 1;
else
subs->datainterval = 0;
subs->datainterval = fmt->datainterval;
subs->syncpipe = subs->syncinterval = 0;
subs->maxpacksize = fmt->maxpacksize;
subs->fill_max = 0;
@ -1568,11 +1559,15 @@ static struct snd_pcm_hardware snd_usb_hardware =
#define hwc_debug(fmt, args...) /**/
#endif
static int hw_check_valid_format(struct snd_pcm_hw_params *params, struct audioformat *fp)
static int hw_check_valid_format(struct snd_usb_substream *subs,
struct snd_pcm_hw_params *params,
struct audioformat *fp)
{
struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
struct snd_mask *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
struct snd_interval *pt = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME);
unsigned int ptime;
/* check the format */
if (!snd_mask_test(fmts, fp->format)) {
@ -1593,6 +1588,14 @@ static int hw_check_valid_format(struct snd_pcm_hw_params *params, struct audiof
hwc_debug(" > check: rate_max %d < min %d\n", fp->rate_max, it->min);
return 0;
}
/* check whether the period time is >= the data packet interval */
if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) {
ptime = 125 * (1 << fp->datainterval);
if (ptime > pt->max || (ptime == pt->max && pt->openmax)) {
hwc_debug(" > check: ptime %u > max %u\n", ptime, pt->max);
return 0;
}
}
return 1;
}
@ -1611,7 +1614,7 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params,
list_for_each(p, &subs->fmt_list) {
struct audioformat *fp;
fp = list_entry(p, struct audioformat, list);
if (!hw_check_valid_format(params, fp))
if (!hw_check_valid_format(subs, params, fp))
continue;
if (changed++) {
if (rmin > fp->rate_min)
@ -1665,7 +1668,7 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params,
list_for_each(p, &subs->fmt_list) {
struct audioformat *fp;
fp = list_entry(p, struct audioformat, list);
if (!hw_check_valid_format(params, fp))
if (!hw_check_valid_format(subs, params, fp))
continue;
if (changed++) {
if (rmin > fp->channels)
@ -1718,7 +1721,7 @@ static int hw_rule_format(struct snd_pcm_hw_params *params,
list_for_each(p, &subs->fmt_list) {
struct audioformat *fp;
fp = list_entry(p, struct audioformat, list);
if (!hw_check_valid_format(params, fp))
if (!hw_check_valid_format(subs, params, fp))
continue;
fbits |= (1ULL << fp->format);
}
@ -1736,95 +1739,42 @@ static int hw_rule_format(struct snd_pcm_hw_params *params,
return changed;
}
#define MAX_MASK 64
/*
* check whether the registered audio formats need special hw-constraints
*/
static int check_hw_params_convention(struct snd_usb_substream *subs)
static int hw_rule_period_time(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
int i;
u32 *channels;
u32 *rates;
u32 cmaster, rmaster;
u32 rate_min = 0, rate_max = 0;
struct list_head *p;
int err = 1;
struct snd_usb_substream *subs = rule->private;
struct audioformat *fp;
struct snd_interval *it;
unsigned char min_datainterval;
unsigned int pmin;
int changed;
channels = kcalloc(MAX_MASK, sizeof(u32), GFP_KERNEL);
rates = kcalloc(MAX_MASK, sizeof(u32), GFP_KERNEL);
if (!channels || !rates) {
err = -ENOMEM;
goto __out;
}
list_for_each(p, &subs->fmt_list) {
struct audioformat *f;
f = list_entry(p, struct audioformat, list);
/* unconventional channels? */
if (f->channels > 32)
goto __out;
/* continuous rate min/max matches? */
if (f->rates & SNDRV_PCM_RATE_CONTINUOUS) {
if (rate_min && f->rate_min != rate_min)
goto __out;
if (rate_max && f->rate_max != rate_max)
goto __out;
rate_min = f->rate_min;
rate_max = f->rate_max;
}
/* combination of continuous rates and fixed rates? */
if (rates[f->format] & SNDRV_PCM_RATE_CONTINUOUS) {
if (f->rates != rates[f->format])
goto __out;
}
if (f->rates & SNDRV_PCM_RATE_CONTINUOUS) {
if (rates[f->format] && rates[f->format] != f->rates)
goto __out;
}
channels[f->format] |= 1 << (f->channels - 1);
rates[f->format] |= f->rates;
/* needs knot? */
if (f->rates & SNDRV_PCM_RATE_KNOT)
goto __out;
}
/* check whether channels and rates match for all formats */
cmaster = rmaster = 0;
for (i = 0; i < MAX_MASK; i++) {
if (cmaster != channels[i] && cmaster && channels[i])
goto __out;
if (rmaster != rates[i] && rmaster && rates[i])
goto __out;
if (channels[i])
cmaster = channels[i];
if (rates[i])
rmaster = rates[i];
}
/* check whether channels match for all distinct rates */
memset(channels, 0, MAX_MASK * sizeof(u32));
list_for_each(p, &subs->fmt_list) {
struct audioformat *f;
f = list_entry(p, struct audioformat, list);
if (f->rates & SNDRV_PCM_RATE_CONTINUOUS)
it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME);
hwc_debug("hw_rule_period_time: (%u,%u)\n", it->min, it->max);
min_datainterval = 0xff;
list_for_each_entry(fp, &subs->fmt_list, list) {
if (!hw_check_valid_format(subs, params, fp))
continue;
for (i = 0; i < 32; i++) {
if (f->rates & (1 << i))
channels[i] |= 1 << (f->channels - 1);
}
min_datainterval = min(min_datainterval, fp->datainterval);
}
cmaster = 0;
for (i = 0; i < 32; i++) {
if (cmaster != channels[i] && cmaster && channels[i])
goto __out;
if (channels[i])
cmaster = channels[i];
if (min_datainterval == 0xff) {
hwc_debug(" --> get emtpy\n");
it->empty = 1;
return -EINVAL;
}
err = 0;
__out:
kfree(channels);
kfree(rates);
return err;
pmin = 125 * (1 << min_datainterval);
changed = 0;
if (it->min < pmin) {
it->min = pmin;
it->openmin = 0;
changed = 1;
}
if (snd_interval_checkempty(it)) {
it->empty = 1;
return -EINVAL;
}
hwc_debug(" --> (%u,%u) (changed = %d)\n", it->min, it->max, changed);
return changed;
}
/*
@ -1872,6 +1822,8 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs)
{
struct list_head *p;
unsigned int pt, ptmin;
int param_period_time_if_needed;
int err;
runtime->hw.formats = subs->formats;
@ -1881,6 +1833,7 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
runtime->hw.channels_min = 256;
runtime->hw.channels_max = 0;
runtime->hw.rates = 0;
ptmin = UINT_MAX;
/* check min/max rates and channels */
list_for_each(p, &subs->fmt_list) {
struct audioformat *fp;
@ -1899,42 +1852,54 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
runtime->hw.period_bytes_min = runtime->hw.period_bytes_max =
fp->frame_size;
}
pt = 125 * (1 << fp->datainterval);
ptmin = min(ptmin, pt);
}
/* set the period time minimum 1ms */
/* FIXME: high-speed mode allows 125us minimum period, but many parts
* in the current code assume the 1ms period.
*/
param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME;
if (snd_usb_get_speed(subs->dev) != USB_SPEED_HIGH)
/* full speed devices have fixed data packet interval */
ptmin = 1000;
if (ptmin == 1000)
/* if period time doesn't go below 1 ms, no rules needed */
param_period_time_if_needed = -1;
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
1000,
/*(nrpacks * MAX_URBS) * 1000*/ UINT_MAX);
ptmin, UINT_MAX);
err = check_hw_params_convention(subs);
if (err < 0)
if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
hw_rule_rate, subs,
SNDRV_PCM_HW_PARAM_FORMAT,
SNDRV_PCM_HW_PARAM_CHANNELS,
param_period_time_if_needed,
-1)) < 0)
return err;
else if (err) {
hwc_debug("setting extra hw constraints...\n");
if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
hw_rule_rate, subs,
SNDRV_PCM_HW_PARAM_FORMAT,
SNDRV_PCM_HW_PARAM_CHANNELS,
-1)) < 0)
return err;
if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
hw_rule_channels, subs,
SNDRV_PCM_HW_PARAM_FORMAT,
SNDRV_PCM_HW_PARAM_RATE,
-1)) < 0)
return err;
if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
hw_rule_format, subs,
SNDRV_PCM_HW_PARAM_RATE,
SNDRV_PCM_HW_PARAM_CHANNELS,
-1)) < 0)
return err;
if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0)
if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
hw_rule_channels, subs,
SNDRV_PCM_HW_PARAM_FORMAT,
SNDRV_PCM_HW_PARAM_RATE,
param_period_time_if_needed,
-1)) < 0)
return err;
if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
hw_rule_format, subs,
SNDRV_PCM_HW_PARAM_RATE,
SNDRV_PCM_HW_PARAM_CHANNELS,
param_period_time_if_needed,
-1)) < 0)
return err;
if (param_period_time_if_needed >= 0) {
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
hw_rule_period_time, subs,
SNDRV_PCM_HW_PARAM_FORMAT,
SNDRV_PCM_HW_PARAM_CHANNELS,
SNDRV_PCM_HW_PARAM_RATE,
-1);
if (err < 0)
return err;
}
if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0)
return err;
return 0;
}
@ -2147,7 +2112,8 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
fp = list_entry(p, struct audioformat, list);
snd_iprintf(buffer, " Interface %d\n", fp->iface);
snd_iprintf(buffer, " Altset %d\n", fp->altsetting);
snd_iprintf(buffer, " Format: %#x\n", fp->format);
snd_iprintf(buffer, " Format: %#x (%d bits)\n",
fp->format, snd_pcm_format_width(fp->format));
snd_iprintf(buffer, " Channels: %d\n", fp->channels);
snd_iprintf(buffer, " Endpoint: %d %s (%s)\n",
fp->endpoint & USB_ENDPOINT_NUMBER_MASK,
@ -2166,6 +2132,9 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
}
snd_iprintf(buffer, "\n");
}
if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
snd_iprintf(buffer, " Data packet interval: %d us\n",
125 * (1 << fp->datainterval));
// snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize);
// snd_iprintf(buffer, " EP Attribute = %#x\n", fp->attributes);
}
@ -2659,6 +2628,17 @@ static int parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp
return 0;
}
static unsigned char parse_datainterval(struct snd_usb_audio *chip,
struct usb_host_interface *alts)
{
if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH &&
get_endpoint(alts, 0)->bInterval >= 1 &&
get_endpoint(alts, 0)->bInterval <= 4)
return get_endpoint(alts, 0)->bInterval - 1;
else
return 0;
}
static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
int iface, int altno);
static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
@ -2764,6 +2744,7 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
fp->altset_idx = i;
fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
fp->datainterval = parse_datainterval(chip, alts);
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
@ -2955,6 +2936,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
return -EINVAL;
}
alts = &iface->altsetting[fp->altset_idx];
fp->datainterval = parse_datainterval(chip, alts);
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
usb_set_interface(chip->dev, fp->iface, 0);
init_usb_pitch(chip->dev, fp->iface, alts, fp);
@ -3049,6 +3031,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
fp->iface = altsd->bInterfaceNumber;
fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
fp->datainterval = 0;
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
switch (fp->maxpacksize) {
@ -3116,6 +3099,7 @@ static int create_ua1000_quirk(struct snd_usb_audio *chip,
fp->iface = altsd->bInterfaceNumber;
fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
fp->datainterval = parse_datainterval(chip, alts);
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
fp->rate_max = fp->rate_min = combine_triple(&alts->extra[8]);
@ -3168,6 +3152,7 @@ static int create_ua101_quirk(struct snd_usb_audio *chip,
fp->iface = altsd->bInterfaceNumber;
fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
fp->datainterval = parse_datainterval(chip, alts);
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
fp->rate_max = fp->rate_min = combine_triple(&alts->extra[15]);