Merge tag 'sound-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "This contains pretty many small commits covering fairly large range of files in sound/ directory. Partly because of additional API support and partly because of constantly developed ASoC and ARM stuff. Some highlights: - Introduced the helper function and documentation for exposing the channel map via control API, as discussed in Plumbers; most of PCI drivers are covered, will follow more drivers later - Most of drivers have been replaced with the new PM callbacks (if the bus is supported) - HD-audio controller got the support of runtime PM and the support of D3 clock-stop. Also changing the power_save option in sysfs kicks off immediately to enable / disable the power-save mode. - Another significant code change in HD-audio is the rewrite of firmware loading code. Other than that, most of changes in HD-audio are continued cleanups and standardization for the generic auto parser and bug fixes (HBR, device-specific fixups), in addition to the support of channel-map API. - Addition of ASoC bindings for the compressed API, used by the mid-x86 drivers. - Lots of cleanups and API refreshes for ASoC codec drivers and DaVinci. - Conversion of OMAP to dmaengine. - New machine driver for Wolfson Microelectronics Bells. - New CODEC driver for Wolfson Microelectronics WM0010. - Enhancements to the ux500 and wm2000 drivers - A new driver for DA9055 and the support for regulator bypass mode." Fix up various arm soc header file reorg conflicts. * tag 'sound-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (339 commits) ALSA: hda - Add new codec ALC283 ALC290 support ALSA: hda - avoid unneccesary indices on "Headphone Jack" controls ALSA: hda - fix indices on boost volume on Conexant ALSA: aloop - add locking to timer access ALSA: hda - Fix hang caused by race during suspend. sound: Remove unnecessary semicolon ALSA: hda/realtek - Fix detection of ALC271X codec ALSA: hda - Add inverted internal mic quirk for Lenovo IdeaPad U310 ALSA: hda - make Realtek/Sigmatel/Conexant use the generic unsol event ALSA: hda - make a generic unsol event handler ASoC: codecs: Add DA9055 codec driver ASoC: eukrea-tlv320: Convert it to platform driver ALSA: ASoC: add DT bindings for CS4271 ASoC: wm_hubs: Ensure volume updates are handled during class W startup ASoC: wm5110: Adding missing volume update bits ASoC: wm5110: Add OUT3R support ASoC: wm5110: Add AEC loopback support ASoC: wm5110: Rename EPOUT to HPOUT3 ASoC: arizona: Add more clock rates ASoC: arizona: Add more DSP options for mixer input muxes ...
此提交包含在:
@@ -25,13 +25,14 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/omap-dma.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <plat/cpu.h>
|
||||
#include <plat/dma.h>
|
||||
#include "omap-pcm.h"
|
||||
|
||||
static const struct snd_pcm_hardware omap_pcm_hardware = {
|
||||
@@ -50,61 +51,34 @@ static const struct snd_pcm_hardware omap_pcm_hardware = {
|
||||
.buffer_bytes_max = 128 * 1024,
|
||||
};
|
||||
|
||||
struct omap_runtime_data {
|
||||
spinlock_t lock;
|
||||
struct omap_pcm_dma_data *dma_data;
|
||||
int dma_ch;
|
||||
int period_index;
|
||||
};
|
||||
|
||||
static void omap_pcm_dma_irq(int ch, u16 stat, void *data)
|
||||
static int omap_pcm_get_dma_buswidth(int num_bits)
|
||||
{
|
||||
struct snd_pcm_substream *substream = data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct omap_runtime_data *prtd = runtime->private_data;
|
||||
unsigned long flags;
|
||||
int buswidth;
|
||||
|
||||
if ((cpu_is_omap1510())) {
|
||||
/*
|
||||
* OMAP1510 doesn't fully support DMA progress counter
|
||||
* and there is no software emulation implemented yet,
|
||||
* so have to maintain our own progress counters
|
||||
* that can be used by omap_pcm_pointer() instead.
|
||||
*/
|
||||
spin_lock_irqsave(&prtd->lock, flags);
|
||||
if ((stat == OMAP_DMA_LAST_IRQ) &&
|
||||
(prtd->period_index == runtime->periods - 1)) {
|
||||
/* we are in sync, do nothing */
|
||||
spin_unlock_irqrestore(&prtd->lock, flags);
|
||||
return;
|
||||
}
|
||||
if (prtd->period_index >= 0) {
|
||||
if (stat & OMAP_DMA_BLOCK_IRQ) {
|
||||
/* end of buffer reached, loop back */
|
||||
prtd->period_index = 0;
|
||||
} else if (stat & OMAP_DMA_LAST_IRQ) {
|
||||
/* update the counter for the last period */
|
||||
prtd->period_index = runtime->periods - 1;
|
||||
} else if (++prtd->period_index >= runtime->periods) {
|
||||
/* end of buffer missed? loop back */
|
||||
prtd->period_index = 0;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&prtd->lock, flags);
|
||||
switch (num_bits) {
|
||||
case 16:
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
break;
|
||||
case 32:
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
break;
|
||||
default:
|
||||
buswidth = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
snd_pcm_period_elapsed(substream);
|
||||
return buswidth;
|
||||
}
|
||||
|
||||
|
||||
/* this may get called several times by oss emulation */
|
||||
static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct omap_runtime_data *prtd = runtime->private_data;
|
||||
struct omap_pcm_dma_data *dma_data;
|
||||
|
||||
struct dma_slave_config config;
|
||||
struct dma_chan *chan;
|
||||
int err = 0;
|
||||
|
||||
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
||||
@@ -117,162 +91,78 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
runtime->dma_bytes = params_buffer_bytes(params);
|
||||
|
||||
if (prtd->dma_data)
|
||||
return 0;
|
||||
prtd->dma_data = dma_data;
|
||||
err = omap_request_dma(dma_data->dma_req, dma_data->name,
|
||||
omap_pcm_dma_irq, substream, &prtd->dma_ch);
|
||||
if (!err) {
|
||||
/*
|
||||
* Link channel with itself so DMA doesn't need any
|
||||
* reprogramming while looping the buffer
|
||||
*/
|
||||
omap_dma_link_lch(prtd->dma_ch, prtd->dma_ch);
|
||||
chan = snd_dmaengine_pcm_get_chan(substream);
|
||||
if (!chan)
|
||||
return -EINVAL;
|
||||
|
||||
/* fills in addr_width and direction */
|
||||
err = snd_hwparams_to_dma_slave_config(substream, params, &config);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Override the *_dma addr_width if requested by the DAI driver */
|
||||
if (dma_data->data_type) {
|
||||
int buswidth = omap_pcm_get_dma_buswidth(dma_data->data_type);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
config.dst_addr_width = buswidth;
|
||||
else
|
||||
config.src_addr_width = buswidth;
|
||||
}
|
||||
|
||||
return err;
|
||||
config.src_addr = dma_data->port_addr;
|
||||
config.dst_addr = dma_data->port_addr;
|
||||
config.src_maxburst = dma_data->packet_size;
|
||||
config.dst_maxburst = dma_data->packet_size;
|
||||
|
||||
return dmaengine_slave_config(chan, &config);
|
||||
}
|
||||
|
||||
static int omap_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct omap_runtime_data *prtd = runtime->private_data;
|
||||
|
||||
if (prtd->dma_data == NULL)
|
||||
return 0;
|
||||
|
||||
omap_dma_unlink_lch(prtd->dma_ch, prtd->dma_ch);
|
||||
omap_free_dma(prtd->dma_ch);
|
||||
prtd->dma_data = NULL;
|
||||
|
||||
snd_pcm_set_runtime_buffer(substream, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct omap_runtime_data *prtd = runtime->private_data;
|
||||
struct omap_pcm_dma_data *dma_data = prtd->dma_data;
|
||||
struct omap_dma_channel_params dma_params;
|
||||
int bytes;
|
||||
|
||||
/* return if this is a bufferless transfer e.g.
|
||||
* codec <--> BT codec or GSM modem -- lg FIXME */
|
||||
if (!prtd->dma_data)
|
||||
return 0;
|
||||
|
||||
memset(&dma_params, 0, sizeof(dma_params));
|
||||
dma_params.data_type = dma_data->data_type;
|
||||
dma_params.trigger = dma_data->dma_req;
|
||||
dma_params.sync_mode = dma_data->sync_mode;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
dma_params.src_amode = OMAP_DMA_AMODE_POST_INC;
|
||||
dma_params.dst_amode = OMAP_DMA_AMODE_CONSTANT;
|
||||
dma_params.src_or_dst_synch = OMAP_DMA_DST_SYNC;
|
||||
dma_params.src_start = runtime->dma_addr;
|
||||
dma_params.dst_start = dma_data->port_addr;
|
||||
dma_params.dst_port = OMAP_DMA_PORT_MPUI;
|
||||
dma_params.dst_fi = dma_data->packet_size;
|
||||
} else {
|
||||
dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT;
|
||||
dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC;
|
||||
dma_params.src_or_dst_synch = OMAP_DMA_SRC_SYNC;
|
||||
dma_params.src_start = dma_data->port_addr;
|
||||
dma_params.dst_start = runtime->dma_addr;
|
||||
dma_params.src_port = OMAP_DMA_PORT_MPUI;
|
||||
dma_params.src_fi = dma_data->packet_size;
|
||||
}
|
||||
/*
|
||||
* Set DMA transfer frame size equal to ALSA period size and frame
|
||||
* count as no. of ALSA periods. Then with DMA frame interrupt enabled,
|
||||
* we can transfer the whole ALSA buffer with single DMA transfer but
|
||||
* still can get an interrupt at each period bounary
|
||||
*/
|
||||
bytes = snd_pcm_lib_period_bytes(substream);
|
||||
dma_params.elem_count = bytes >> dma_data->data_type;
|
||||
dma_params.frame_count = runtime->periods;
|
||||
omap_set_dma_params(prtd->dma_ch, &dma_params);
|
||||
|
||||
if ((cpu_is_omap1510()))
|
||||
omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ |
|
||||
OMAP_DMA_LAST_IRQ | OMAP_DMA_BLOCK_IRQ);
|
||||
else if (!substream->runtime->no_period_wakeup)
|
||||
omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
|
||||
else {
|
||||
/*
|
||||
* No period wakeup:
|
||||
* we need to disable BLOCK_IRQ, which is enabled by the omap
|
||||
* dma core at request dma time.
|
||||
*/
|
||||
omap_disable_dma_irq(prtd->dma_ch, OMAP_DMA_BLOCK_IRQ);
|
||||
}
|
||||
|
||||
if (!(cpu_class_is_omap1())) {
|
||||
omap_set_dma_src_burst_mode(prtd->dma_ch,
|
||||
OMAP_DMA_DATA_BURST_16);
|
||||
omap_set_dma_dest_burst_mode(prtd->dma_ch,
|
||||
OMAP_DMA_DATA_BURST_16);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct omap_runtime_data *prtd = runtime->private_data;
|
||||
struct omap_pcm_dma_data *dma_data = prtd->dma_data;
|
||||
unsigned long flags;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct omap_pcm_dma_data *dma_data;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&prtd->lock, flags);
|
||||
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
prtd->period_index = 0;
|
||||
/* Configure McBSP internal buffer usage */
|
||||
if (dma_data->set_threshold)
|
||||
dma_data->set_threshold(substream);
|
||||
|
||||
omap_start_dma(prtd->dma_ch);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
prtd->period_index = -1;
|
||||
omap_stop_dma(prtd->dma_ch);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
spin_unlock_irqrestore(&prtd->lock, flags);
|
||||
|
||||
if (ret == 0)
|
||||
ret = snd_dmaengine_pcm_trigger(substream, cmd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct omap_runtime_data *prtd = runtime->private_data;
|
||||
dma_addr_t ptr;
|
||||
snd_pcm_uframes_t offset;
|
||||
|
||||
if (cpu_is_omap1510()) {
|
||||
offset = prtd->period_index * runtime->period_size;
|
||||
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
ptr = omap_get_dma_dst_pos(prtd->dma_ch);
|
||||
offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
|
||||
} else {
|
||||
ptr = omap_get_dma_src_pos(prtd->dma_ch);
|
||||
offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
|
||||
}
|
||||
|
||||
if (offset >= runtime->buffer_size)
|
||||
offset = 0;
|
||||
if (cpu_is_omap1510())
|
||||
offset = snd_dmaengine_pcm_pointer_no_residue(substream);
|
||||
else
|
||||
offset = snd_dmaengine_pcm_pointer(substream);
|
||||
|
||||
return offset;
|
||||
}
|
||||
@@ -280,7 +170,8 @@ static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
static int omap_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct omap_runtime_data *prtd;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct omap_pcm_dma_data *dma_data;
|
||||
int ret;
|
||||
|
||||
snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware);
|
||||
@@ -289,25 +180,17 @@ static int omap_pcm_open(struct snd_pcm_substream *substream)
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
|
||||
if (prtd == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
spin_lock_init(&prtd->lock);
|
||||
runtime->private_data = prtd;
|
||||
|
||||
out:
|
||||
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
||||
ret = snd_dmaengine_pcm_open(substream, omap_dma_filter_fn,
|
||||
&dma_data->dma_req);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int omap_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
kfree(runtime->private_data);
|
||||
snd_dmaengine_pcm_close(substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -328,7 +211,6 @@ static struct snd_pcm_ops omap_pcm_ops = {
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = omap_pcm_hw_params,
|
||||
.hw_free = omap_pcm_hw_free,
|
||||
.prepare = omap_pcm_prepare,
|
||||
.trigger = omap_pcm_trigger,
|
||||
.pointer = omap_pcm_pointer,
|
||||
.mmap = omap_pcm_mmap,
|
||||
|
新增問題並參考
封鎖使用者