Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 142718
b: refs/heads/master
c: 5f9c510
h: refs/heads/master
v: v3
  • Loading branch information
Takashi Iwai committed Apr 6, 2009
1 parent a8ab9e9 commit 2d449e0
Show file tree
Hide file tree
Showing 18 changed files with 860 additions and 66 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: fa075ed2dc80440bf3e9092d38a66c3227b174c1
refs/heads/master: 5f9c510e9e18cd029e15190d35dd4271f2ef393b
71 changes: 71 additions & 0 deletions trunk/Documentation/sound/alsa/soc/jack.txt
Original file line number Diff line number Diff line change
@@ -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.
15 changes: 10 additions & 5 deletions trunk/sound/arm/pxa2xx-ac97-lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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;
}
Expand All @@ -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();
Expand All @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions trunk/sound/isa/opl3sa2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = {
Expand Down
5 changes: 5 additions & 0 deletions trunk/sound/pci/hda/patch_realtek.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions trunk/sound/pci/hda/patch_sigmatel.c
Original file line number Diff line number Diff line change
Expand Up @@ -4895,6 +4895,7 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
switch (codec->vendor_id) {
case 0x111d7604:
case 0x111d7605:
case 0x111d76d5:
if (spec->board_config == STAC_92HD83XXX_PWR_REF)
break;
spec->num_pwrs = 0;
Expand Down Expand Up @@ -5707,6 +5708,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 },
Expand Down
2 changes: 1 addition & 1 deletion trunk/sound/ppc/powermac.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
59 changes: 58 additions & 1 deletion trunk/sound/soc/codecs/twl4030.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

/*
Expand Down Expand Up @@ -1217,15 +1220,64 @@ 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)
{
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;
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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,
Expand All @@ -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",
Expand Down
1 change: 1 addition & 0 deletions trunk/sound/soc/codecs/twl4030.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 37 additions & 0 deletions trunk/sound/soc/codecs/wm9705.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
17 changes: 17 additions & 0 deletions trunk/sound/soc/fsl/fsl_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/*
Expand Down
Loading

0 comments on commit 2d449e0

Please sign in to comment.