ASoC: Intel fixes for v3.15

This is a relatively large batch of fixes for the newly added
 Haswell/Baytrail drivers from Intel.  It's a bit larger than is good for
 this point in the cycle but it's all for a newly added driver so not so
 worrying as it might otherwise be.  Some of it's integration problems,
 some of it's the sort of problem usually turned up in stress tests.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJTc1YrAAoJELSic+t+oim9losP/0I50EKB6hrEugvFrNT+vBkR
 OQxhcj+WGgIguVQNqaHPBr3rQ1iGG54C5Yf1KDSi2jNIjkHSTIJ1bT2QB40rmclT
 14V6LZJL481TadcKcjLux1jgmaMvNO5Fa1gqYdLLVZFUYP/yTCLhqYrVIDO7NC6M
 VCKtZ965l4u6TMJACTdz9MzCbAwlhb7OTpgxIiyVQlE4SPq2D7M8W9IceLjiDzGg
 rviGK7MejMxc2b4i2vJGi4msaqK8aazDmMhoqrI+HZr/6pZgWkvKJ2S/zZf8AdEH
 6KfEQ8vPU/ag8M4UoH72JJtn1gsjphkEY8GyNWCvP7fIXnpcYB34c/cyaaMQ6lGN
 fGPfwQpbNobx3sJsPMRj21kFgy61rXM2PcbA4QEhPniHd6UlVPUgjkxBNE2YVM3d
 0+tWtgzWVT10F10fcKIkw00/gDohBK+4rViAu0VK5Ml90F0eYLeITWYFVyjmU52f
 7Q+0Duwm65LsA4hqFKcH5lRbk6IM29yRte/wGYY8mLWODADO0+cU6WmVRDHTlRFo
 35HidtfY8EGcU+rS24XyGd0+GRoO+nghzKTckoD3z9OCPilePkMb/dD5vC7NvmMO
 5Q15VbxmJgsus0aTDOPD634XoVTlQ/ESBA6bj5APylZorNKyANorJBphn4rBfZV+
 GXtzzjFrllDCOpKtjDzB
 =ROtF
 -----END PGP SIGNATURE-----

Merge tag 'asoc-v3.15-rc5-intel' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus

ASoC: Intel fixes for v3.15

This is a relatively large batch of fixes for the newly added
Haswell/Baytrail drivers from Intel.  It's a bit larger than is good for
this point in the cycle but it's all for a newly added driver so not so
worrying as it might otherwise be.  Some of it's integration problems,
some of it's the sort of problem usually turned up in stress tests.
This commit is contained in:
Takashi Iwai 2014-05-14 14:27:12 +02:00
commit ff2354bc6e
11 changed files with 126 additions and 59 deletions

View File

@ -138,6 +138,7 @@ static int sst_acpi_probe(struct platform_device *pdev)
sst_pdata = &sst_acpi->sst_pdata;
sst_pdata->id = desc->sst_id;
sst_pdata->dma_dev = dev;
sst_acpi->desc = desc;
sst_acpi->mach = mach;

View File

@ -324,7 +324,7 @@ static int sst_byt_init(struct sst_dsp *sst, struct sst_pdata *pdata)
memcpy_toio(sst->addr.lpe + SST_BYT_MAILBOX_OFFSET,
&pdata->fw_base, sizeof(u32));
ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
ret = dma_coerce_mask_and_coherent(sst->dma_dev, DMA_BIT_MASK(32));
if (ret)
return ret;

View File

@ -542,16 +542,20 @@ struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id,
void *data)
{
struct sst_byt_stream *stream;
struct sst_dsp *sst = byt->dsp;
unsigned long flags;
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
if (stream == NULL)
return NULL;
spin_lock_irqsave(&sst->spinlock, flags);
list_add(&stream->node, &byt->stream_list);
stream->notify_position = notify_position;
stream->pdata = data;
stream->byt = byt;
stream->str_id = id;
spin_unlock_irqrestore(&sst->spinlock, flags);
return stream;
}
@ -630,6 +634,8 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
{
u64 header;
int ret = 0;
struct sst_dsp *sst = byt->dsp;
unsigned long flags;
if (!stream->commited)
goto out;
@ -644,8 +650,10 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
stream->commited = false;
out:
spin_lock_irqsave(&sst->spinlock, flags);
list_del(&stream->node);
kfree(stream);
spin_unlock_irqrestore(&sst->spinlock, flags);
return ret;
}

View File

@ -228,6 +228,7 @@ struct sst_dsp {
spinlock_t spinlock; /* IPC locking */
struct mutex mutex; /* DSP FW lock */
struct device *dev;
struct device *dma_dev;
void *thread_context;
int irq;
u32 id;

View File

@ -337,6 +337,7 @@ struct sst_dsp *sst_dsp_new(struct device *dev,
spin_lock_init(&sst->spinlock);
mutex_init(&sst->mutex);
sst->dev = dev;
sst->dma_dev = pdata->dma_dev;
sst->thread_context = sst_dev->thread_context;
sst->sst_dev = sst_dev;
sst->id = pdata->id;

View File

@ -169,6 +169,7 @@ struct sst_pdata {
u32 dma_base;
u32 dma_size;
int dma_engine;
struct device *dma_dev;
/* DSP */
u32 id;

View File

@ -57,14 +57,8 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
sst_fw->private = private;
sst_fw->size = fw->size;
err = dma_coerce_mask_and_coherent(dsp->dev, DMA_BIT_MASK(32));
if (err < 0) {
kfree(sst_fw);
return NULL;
}
/* allocate DMA buffer to store FW data */
sst_fw->dma_buf = dma_alloc_coherent(dsp->dev, sst_fw->size,
sst_fw->dma_buf = dma_alloc_coherent(dsp->dma_dev, sst_fw->size,
&sst_fw->dmable_fw_paddr, GFP_DMA | GFP_KERNEL);
if (!sst_fw->dma_buf) {
dev_err(dsp->dev, "error: DMA alloc failed\n");
@ -106,7 +100,7 @@ void sst_fw_free(struct sst_fw *sst_fw)
list_del(&sst_fw->list);
mutex_unlock(&dsp->mutex);
dma_free_coherent(dsp->dev, sst_fw->size, sst_fw->dma_buf,
dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf,
sst_fw->dmable_fw_paddr);
kfree(sst_fw);
}
@ -202,6 +196,9 @@ static int block_alloc_contiguous(struct sst_module *module,
size -= block->size;
}
list_for_each_entry(block, &tmp, list)
list_add(&block->module_list, &module->block_list);
list_splice(&tmp, &dsp->used_block_list);
return 0;
}
@ -247,8 +244,7 @@ static int block_alloc(struct sst_module *module,
/* do we span > 1 blocks */
if (data->size > block->size) {
ret = block_alloc_contiguous(module, data,
block->offset + block->size,
data->size - block->size);
block->offset, data->size);
if (ret == 0)
return ret;
}
@ -344,7 +340,7 @@ static int block_alloc_fixed(struct sst_module *module,
err = block_alloc_contiguous(module, data,
block->offset + block->size,
data->size - block->size + data->offset - block->offset);
data->size - block->size);
if (err < 0)
return -ENOMEM;
@ -371,15 +367,10 @@ static int block_alloc_fixed(struct sst_module *module,
if (data->offset >= block->offset && data->offset < block_end) {
err = block_alloc_contiguous(module, data,
block->offset + block->size,
data->size - block->size);
block->offset, data->size);
if (err < 0)
return -ENOMEM;
/* add block */
block->data_type = data->data_type;
list_move(&block->list, &dsp->used_block_list);
list_add(&block->module_list, &module->block_list);
return 0;
}

View File

@ -433,7 +433,7 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
int ret = -ENODEV, i, j, region_count;
u32 offset, size;
dev = sst->dev;
dev = sst->dma_dev;
switch (sst->id) {
case SST_DEV_ID_LYNX_POINT:
@ -466,7 +466,7 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
return ret;
}
ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31));
if (ret)
return ret;

View File

@ -1159,11 +1159,14 @@ struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
void *data)
{
struct sst_hsw_stream *stream;
struct sst_dsp *sst = hsw->dsp;
unsigned long flags;
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
if (stream == NULL)
return NULL;
spin_lock_irqsave(&sst->spinlock, flags);
list_add(&stream->node, &hsw->stream_list);
stream->notify_position = notify_position;
stream->pdata = data;
@ -1172,6 +1175,7 @@ struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
/* work to process notification messages */
INIT_WORK(&stream->notify_work, hsw_notification_work);
spin_unlock_irqrestore(&sst->spinlock, flags);
return stream;
}
@ -1180,6 +1184,8 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
{
u32 header;
int ret = 0;
struct sst_dsp *sst = hsw->dsp;
unsigned long flags;
/* dont free DSP streams that are not commited */
if (!stream->commited)
@ -1201,8 +1207,11 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
trace_hsw_stream_free_req(stream, &stream->free_req);
out:
cancel_work_sync(&stream->notify_work);
spin_lock_irqsave(&sst->spinlock, flags);
list_del(&stream->node);
kfree(stream);
spin_unlock_irqrestore(&sst->spinlock, flags);
return ret;
}
@ -1538,10 +1547,28 @@ int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
}
/* Stream pointer positions */
int sst_hsw_get_dsp_position(struct sst_hsw *hsw,
u32 sst_hsw_get_dsp_position(struct sst_hsw *hsw,
struct sst_hsw_stream *stream)
{
return stream->rpos.position;
u32 rpos;
sst_dsp_read(hsw->dsp, &rpos,
stream->reply.read_position_register_address, sizeof(rpos));
return rpos;
}
/* Stream presentation (monotonic) positions */
u64 sst_hsw_get_dsp_presentation_position(struct sst_hsw *hsw,
struct sst_hsw_stream *stream)
{
u64 ppos;
sst_dsp_read(hsw->dsp, &ppos,
stream->reply.presentation_position_register_address,
sizeof(ppos));
return ppos;
}
int sst_hsw_stream_set_write_position(struct sst_hsw *hsw,

View File

@ -464,7 +464,9 @@ int sst_hsw_stream_get_write_pos(struct sst_hsw *hsw,
struct sst_hsw_stream *stream, u32 *position);
int sst_hsw_stream_set_write_position(struct sst_hsw *hsw,
struct sst_hsw_stream *stream, u32 stage_id, u32 position);
int sst_hsw_get_dsp_position(struct sst_hsw *hsw,
u32 sst_hsw_get_dsp_position(struct sst_hsw *hsw,
struct sst_hsw_stream *stream);
u64 sst_hsw_get_dsp_presentation_position(struct sst_hsw *hsw,
struct sst_hsw_stream *stream);
/* HW port config */

View File

@ -99,6 +99,7 @@ struct hsw_pcm_data {
struct snd_compr_stream *cstream;
unsigned int wpos;
struct mutex mutex;
bool allocated;
};
/* private data for the driver */
@ -107,12 +108,14 @@ struct hsw_priv_data {
struct sst_hsw *hsw;
/* page tables */
unsigned char *pcm_pg[HSW_PCM_COUNT][2];
struct snd_dma_buffer dmab[HSW_PCM_COUNT][2];
/* DAI data */
struct hsw_pcm_data pcm[HSW_PCM_COUNT];
};
static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data);
static inline u32 hsw_mixer_to_ipc(unsigned int value)
{
if (value >= ARRAY_SIZE(volume_map))
@ -273,28 +276,26 @@ static const struct snd_kcontrol_new hsw_volume_controls[] = {
};
/* Create DMA buffer page table for DSP */
static int create_adsp_page_table(struct hsw_priv_data *pdata,
struct snd_soc_pcm_runtime *rtd,
unsigned char *dma_area, size_t size, int pcm, int stream)
static int create_adsp_page_table(struct snd_pcm_substream *substream,
struct hsw_priv_data *pdata, struct snd_soc_pcm_runtime *rtd,
unsigned char *dma_area, size_t size, int pcm)
{
int i, pages;
struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream);
int i, pages, stream = substream->stream;
if (size % PAGE_SIZE)
pages = (size / PAGE_SIZE) + 1;
else
pages = size / PAGE_SIZE;
pages = snd_sgbuf_aligned_pages(size);
dev_dbg(rtd->dev, "generating page table for %p size 0x%zu pages %d\n",
dma_area, size, pages);
for (i = 0; i < pages; i++) {
u32 idx = (((i << 2) + i)) >> 1;
u32 pfn = (virt_to_phys(dma_area + i * PAGE_SIZE)) >> PAGE_SHIFT;
u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT;
u32 *pg_table;
dev_dbg(rtd->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn);
pg_table = (u32*)(pdata->pcm_pg[pcm][stream] + idx);
pg_table = (u32 *)(pdata->dmab[pcm][stream].area + idx);
if (i & 1)
*pg_table |= (pfn << 4);
@ -317,12 +318,36 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
struct sst_hsw *hsw = pdata->hsw;
struct sst_module *module_data;
struct sst_dsp *dsp;
struct snd_dma_buffer *dmab;
enum sst_hsw_stream_type stream_type;
enum sst_hsw_stream_path_id path_id;
u32 rate, bits, map, pages, module_id;
u8 channels;
int ret;
/* check if we are being called a subsequent time */
if (pcm_data->allocated) {
ret = sst_hsw_stream_reset(hsw, pcm_data->stream);
if (ret < 0)
dev_dbg(rtd->dev, "error: reset stream failed %d\n",
ret);
ret = sst_hsw_stream_free(hsw, pcm_data->stream);
if (ret < 0) {
dev_dbg(rtd->dev, "error: free stream failed %d\n",
ret);
return ret;
}
pcm_data->allocated = false;
pcm_data->stream = sst_hsw_stream_new(hsw, rtd->cpu_dai->id,
hsw_notify_pointer, pcm_data);
if (pcm_data->stream == NULL) {
dev_err(rtd->dev, "error: failed to create stream\n");
return -EINVAL;
}
}
/* stream direction */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
@ -416,8 +441,10 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
return ret;
}
ret = create_adsp_page_table(pdata, rtd, runtime->dma_area,
runtime->dma_bytes, rtd->cpu_dai->id, substream->stream);
dmab = snd_pcm_get_dma_buf(substream);
ret = create_adsp_page_table(substream, pdata, rtd, runtime->dma_area,
runtime->dma_bytes, rtd->cpu_dai->id);
if (ret < 0)
return ret;
@ -430,9 +457,9 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
pages = runtime->dma_bytes / PAGE_SIZE;
ret = sst_hsw_stream_buffer(hsw, pcm_data->stream,
virt_to_phys(pdata->pcm_pg[rtd->cpu_dai->id][substream->stream]),
pdata->dmab[rtd->cpu_dai->id][substream->stream].addr,
pages, runtime->dma_bytes, 0,
(u32)(virt_to_phys(runtime->dma_area) >> PAGE_SHIFT));
snd_sgbuf_get_addr(dmab, 0) >> PAGE_SHIFT);
if (ret < 0) {
dev_err(rtd->dev, "error: failed to set DMA buffer %d\n", ret);
return ret;
@ -474,6 +501,7 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
dev_err(rtd->dev, "error: failed to commit stream %d\n", ret);
return ret;
}
pcm_data->allocated = true;
ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1);
if (ret < 0)
@ -541,12 +569,14 @@ static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream)
struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
struct sst_hsw *hsw = pdata->hsw;
snd_pcm_uframes_t offset;
uint64_t ppos;
u32 position = sst_hsw_get_dsp_position(hsw, pcm_data->stream);
offset = bytes_to_frames(runtime,
sst_hsw_get_dsp_position(hsw, pcm_data->stream));
offset = bytes_to_frames(runtime, position);
ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream);
dev_dbg(rtd->dev, "PCM: DMA pointer %zu bytes\n",
frames_to_bytes(runtime, (u32)offset));
dev_dbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
position, ppos);
return offset;
}
@ -606,6 +636,7 @@ static int hsw_pcm_close(struct snd_pcm_substream *substream)
dev_dbg(rtd->dev, "error: free stream failed %d\n", ret);
goto out;
}
pcm_data->allocated = 0;
pcm_data->stream = NULL;
out:
@ -621,7 +652,7 @@ static struct snd_pcm_ops hsw_pcm_ops = {
.hw_free = hsw_pcm_hw_free,
.trigger = hsw_pcm_trigger,
.pointer = hsw_pcm_pointer,
.mmap = snd_pcm_lib_default_mmap,
.page = snd_pcm_sgbuf_ops_page,
};
static void hsw_pcm_free(struct snd_pcm *pcm)
@ -632,17 +663,16 @@ static void hsw_pcm_free(struct snd_pcm *pcm)
static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
struct snd_soc_platform *platform = rtd->platform;
struct sst_pdata *pdata = dev_get_platdata(platform->dev);
struct device *dev = pdata->dma_dev;
int ret = 0;
ret = dma_coerce_mask_and_coherent(rtd->card->dev, DMA_BIT_MASK(32));
if (ret)
return ret;
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_DEV,
rtd->card->dev,
SNDRV_DMA_TYPE_DEV_SG,
dev,
hsw_pcm_hardware.buffer_bytes_max,
hsw_pcm_hardware.buffer_bytes_max);
if (ret) {
@ -742,11 +772,14 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
{
struct sst_pdata *pdata = dev_get_platdata(platform->dev);
struct hsw_priv_data *priv_data;
int i;
struct device *dma_dev;
int i, ret = 0;
if (!pdata)
return -ENODEV;
dma_dev = pdata->dma_dev;
priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data), GFP_KERNEL);
priv_data->hsw = pdata->dsp;
snd_soc_platform_set_drvdata(platform, priv_data);
@ -758,15 +791,17 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
/* playback */
if (hsw_dais[i].playback.channels_min) {
priv_data->pcm_pg[i][0] = kzalloc(PAGE_SIZE, GFP_DMA);
if (priv_data->pcm_pg[i][0] == NULL)
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev,
PAGE_SIZE, &priv_data->dmab[i][0]);
if (ret < 0)
goto err;
}
/* capture */
if (hsw_dais[i].capture.channels_min) {
priv_data->pcm_pg[i][1] = kzalloc(PAGE_SIZE, GFP_DMA);
if (priv_data->pcm_pg[i][1] == NULL)
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev,
PAGE_SIZE, &priv_data->dmab[i][1]);
if (ret < 0)
goto err;
}
}
@ -776,11 +811,11 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
err:
for (;i >= 0; i--) {
if (hsw_dais[i].playback.channels_min)
kfree(priv_data->pcm_pg[i][0]);
snd_dma_free_pages(&priv_data->dmab[i][0]);
if (hsw_dais[i].capture.channels_min)
kfree(priv_data->pcm_pg[i][1]);
snd_dma_free_pages(&priv_data->dmab[i][1]);
}
return -ENOMEM;
return ret;
}
static int hsw_pcm_remove(struct snd_soc_platform *platform)
@ -791,9 +826,9 @@ static int hsw_pcm_remove(struct snd_soc_platform *platform)
for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
if (hsw_dais[i].playback.channels_min)
kfree(priv_data->pcm_pg[i][0]);
snd_dma_free_pages(&priv_data->dmab[i][0]);
if (hsw_dais[i].capture.channels_min)
kfree(priv_data->pcm_pg[i][1]);
snd_dma_free_pages(&priv_data->dmab[i][1]);
}
return 0;