Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 81d91acf authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* '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
  ...
parents 132ea5e9 0dd7b0cb
Loading
Loading
Loading
Loading
+71 −0
Original line number Diff line number Diff line
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.
+10 −5
Original line number Diff line number Diff line
@@ -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;
	}
+2 −2
Original line number Diff line number Diff line
@@ -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;

+109 −19
Original line number Diff line number Diff line
/*
 * 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);
+8 −6
Original line number Diff line number Diff line
/*
 * 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 */
Loading