Commit eb7e7cd8 authored by Jaroslav Kysela's avatar Jaroslav Kysela Committed by Linus Torvalds

[PATCH] ALSA update [6/10] - 2002/07/20

  - added vfree_nocheck()
  - PCM midlevel & EMU10K1 - added support for SG buffer
  - CS4236 - added new ISA PnP ID
  - HDSP - fixed rate rules (OSS emulation works)
parent 81ff6ee4
......@@ -243,8 +243,10 @@ void snd_hidden_vfree(void *obj);
#define kfree_nocheck(obj) snd_wrapper_kfree(obj)
#define vmalloc(size) snd_hidden_vmalloc(size)
#define vfree(obj) snd_hidden_vfree(obj)
#define vfree_nocheck(obj) snd_wrapper_vfree(obj)
#else
#define kfree_nocheck(obj) kfree(obj)
#define vfree_nocheck(obj) vfree(obj)
#endif
void *snd_kcalloc(size_t size, int flags);
char *snd_kmalloc_strdup(const char *string, int flags);
......
......@@ -26,6 +26,7 @@
#ifdef __KERNEL__
#include "pcm.h"
#include "pcm_sgbuf.h"
#include "rawmidi.h"
#include "hwdep.h"
#include "ac97_codec.h"
......@@ -1043,7 +1044,7 @@ unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate);
unsigned char snd_emu10k1_sum_vol_attn(unsigned int value);
/* memory allocation */
snd_util_memblk_t *snd_emu10k1_alloc_pages(emu10k1_t *emu, dma_addr_t addr, unsigned long size);
snd_util_memblk_t *snd_emu10k1_alloc_pages(emu10k1_t *emu, struct snd_sg_buf *sgbuf);
int snd_emu10k1_free_pages(emu10k1_t *emu, snd_util_memblk_t *blk);
snd_util_memblk_t *snd_emu10k1_synth_alloc(emu10k1_t *emu, unsigned int size);
int snd_emu10k1_synth_free(emu10k1_t *emu, snd_util_memblk_t *blk);
......
......@@ -96,6 +96,7 @@ typedef struct _snd_pcm_ops {
void *buf, snd_pcm_uframes_t count);
int (*silence)(snd_pcm_substream_t *substream, int channel,
snd_pcm_uframes_t pos, snd_pcm_uframes_t count);
void *(*page)(snd_pcm_substream_t *substream, unsigned long offset);
} snd_pcm_ops_t;
/*
......@@ -771,6 +772,9 @@ int snd_pcm_hw_constraint_step(snd_pcm_runtime_t *runtime,
unsigned int cond,
snd_pcm_hw_param_t var,
unsigned long step);
int snd_pcm_hw_constraint_pow2(snd_pcm_runtime_t *runtime,
unsigned int cond,
snd_pcm_hw_param_t var);
int snd_pcm_hw_rule_add(snd_pcm_runtime_t *runtime,
unsigned int cond,
int var,
......
#ifndef __SOUND_PCM_SGBUF_H
#define __SOUND_PCM_SGBUF_H
/*
* Scatter-Gather PCM access
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
struct snd_sg_page {
void *buf;
dma_addr_t addr;
};
struct snd_sg_buf {
int size; /* allocated byte size (= runtime->dma_bytes) */
int pages; /* allocated pages */
int tblsize; /* allocated table size */
struct snd_sg_page *table;
struct pci_dev *pci;
};
typedef struct snd_sg_buf snd_pcm_sgbuf_t; /* for magic cast */
/*
* return the pages matching with the given byte size
*/
static inline unsigned int snd_pcm_sgbuf_pages(size_t size)
{
return (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
}
int snd_pcm_sgbuf_init(snd_pcm_substream_t *substream, struct pci_dev *pci, int tblsize);
int snd_pcm_sgbuf_delete(snd_pcm_substream_t *substream);
int snd_pcm_sgbuf_alloc(snd_pcm_substream_t *substream, size_t size);
int snd_pcm_sgbuf_free(snd_pcm_substream_t *substream);
int snd_pcm_sgbuf_ops_copy_playback(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t hwoff, void *buf, snd_pcm_uframes_t count);
int snd_pcm_sgbuf_ops_copy_capture(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t hwoff, void *buf, snd_pcm_uframes_t count);
int snd_pcm_sgbuf_ops_silence(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t hwoff, snd_pcm_uframes_t count);
void *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset);
#endif /* __SOUND_PCM_SGBUF_H */
......@@ -61,6 +61,7 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic)
#define snd_pcm_proc_private_t_magic 0xa15a0104
#define snd_pcm_oss_file_t_magic 0xa15a0105
#define snd_mixer_oss_t_magic 0xa15a0106
#define snd_pcm_sgbuf_t_magic 0xa15a0107
#define snd_info_private_data_t_magic 0xa15a0201
#define snd_ctl_file_t_magic 0xa15a0301
......
/* include/version.h. Generated automatically by configure. */
#define CONFIG_SND_VERSION "0.9.0rc2"
#define CONFIG_SND_DATE " (Wed Jul 17 10:56:41 2002 UTC)"
#define CONFIG_SND_DATE " (Sat Jul 20 07:16:41 2002 UTC)"
......@@ -3,7 +3,8 @@
# Copyright (c) 1999,2001 by Jaroslav Kysela <perex@suse.cz>
#
export-objs := sound.o pcm.o pcm_lib.o rawmidi.o timer.o hwdep.o
export-objs := sound.o pcm.o pcm_lib.o rawmidi.o timer.o hwdep.o \
pcm_sgbuf.o
snd-objs := sound.o init.o isadma.o memory.o info.o control.o misc.o \
device.o wrappers.o
......@@ -12,7 +13,7 @@ snd-objs += sound_oss.o info_oss.o
endif
snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
pcm_memory.o
pcm_memory.o pcm_sgbuf.o
snd-rawmidi-objs := rawmidi.o
snd-timer-objs := timer.o
......
......@@ -21,6 +21,7 @@
#define __NO_VERSION__
#include <sound/driver.h>
#include <linux/threads.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
......
......@@ -960,6 +960,26 @@ int snd_pcm_hw_constraint_step(snd_pcm_runtime_t *runtime,
var, -1);
}
static int snd_pcm_hw_rule_pow2(snd_pcm_hw_params_t *params, snd_pcm_hw_rule_t *rule)
{
static int pow2_sizes[] = {
1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7,
1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15,
1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23,
1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30
};
return snd_interval_list(hw_param_interval(params, rule->var),
sizeof(pow2_sizes)/sizeof(int), pow2_sizes, 0);
}
int snd_pcm_hw_constraint_pow2(snd_pcm_runtime_t *runtime,
unsigned int cond,
snd_pcm_hw_param_t var)
{
return snd_pcm_hw_rule_add(runtime, cond, var,
snd_pcm_hw_rule_pow2, NULL,
var, -1);
}
/* To use the same code we have in alsa-lib */
#define snd_pcm_t snd_pcm_substream_t
......@@ -2364,6 +2384,7 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens);
EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits);
EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax);
EXPORT_SYMBOL(snd_pcm_hw_constraint_integer);
EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2);
EXPORT_SYMBOL(snd_pcm_hw_rule_add);
EXPORT_SYMBOL(snd_pcm_set_ops);
EXPORT_SYMBOL(snd_pcm_set_sync);
......
......@@ -2631,6 +2631,7 @@ static unsigned long snd_pcm_mmap_data_nopage(struct vm_area_struct *area, unsig
snd_pcm_runtime_t *runtime;
unsigned long offset;
struct page * page;
void *vaddr;
size_t dma_bytes;
if (substream == NULL)
......@@ -2646,7 +2647,13 @@ static unsigned long snd_pcm_mmap_data_nopage(struct vm_area_struct *area, unsig
dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
if (offset > dma_bytes - PAGE_SIZE)
return NOPAGE_SIGBUS;
page = virt_to_page(runtime->dma_area + offset);
if (substream->ops->page) {
vaddr = substream->ops->page(substream, offset);
if (! vaddr)
return NOPAGE_OOM;
} else
vaddr = runtime->dma_area + offset;
page = virt_to_page(vaddr);
get_page(page);
#ifndef LINUX_2_2
return page;
......
/*
* Scatter-Gather PCM access
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#define __NO_VERSION__
#include <sound/driver.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_sgbuf.h>
/* table entries are align to 32 */
#define SGBUF_TBL_ALIGN 32
#define sgbuf_align_table(tbl) ((((tbl) + SGBUF_TBL_ALIGN - 1) / SGBUF_TBL_ALIGN) * SGBUF_TBL_ALIGN)
/*
* shrink to the given pages.
* free the unused pages
*/
static void sgbuf_shrink(struct snd_sg_buf *sgbuf, int pages)
{
snd_assert(sgbuf, return);
if (! sgbuf->table)
return;
while (sgbuf->pages > pages) {
sgbuf->pages--;
snd_free_pci_pages(sgbuf->pci, PAGE_SIZE,
sgbuf->table[sgbuf->pages].buf,
sgbuf->table[sgbuf->pages].addr);
}
}
/*
* initialize the sg buffer
* assigned to substream->dma_private.
* initialize the table with the given size.
*/
int snd_pcm_sgbuf_init(snd_pcm_substream_t *substream, struct pci_dev *pci, int tblsize)
{
struct snd_sg_buf *sgbuf;
tblsize = sgbuf_align_table(tblsize);
sgbuf = snd_magic_kcalloc(snd_pcm_sgbuf_t, 0, GFP_KERNEL);
if (! sgbuf)
return -ENOMEM;
substream->dma_private = sgbuf;
sgbuf->pci = pci;
sgbuf->pages = 0;
sgbuf->tblsize = tblsize;
sgbuf->table = kmalloc(sizeof(struct snd_sg_page) * tblsize, GFP_KERNEL);
if (! sgbuf->table) {
snd_pcm_sgbuf_free(substream);
return -ENOMEM;
}
memset(sgbuf->table, 0, sizeof(struct snd_sg_page) * tblsize);
return 0;
}
/*
* release all pages and free the sgbuf instance
*/
int snd_pcm_sgbuf_delete(snd_pcm_substream_t *substream)
{
struct snd_sg_buf *sgbuf;
sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL);
sgbuf_shrink(sgbuf, 0);
if (sgbuf->table)
kfree(sgbuf->table);
snd_magic_kfree(sgbuf);
substream->dma_private = NULL;
return 0;
}
/*
* allocate sg buffer table with the given byte size.
* if the buffer table already exists, try to resize it.
* call this from hw_params callback.
*/
int snd_pcm_sgbuf_alloc(snd_pcm_substream_t *substream, size_t size)
{
struct snd_sg_buf *sgbuf;
unsigned int pages;
unsigned int tblsize;
int changed = 0;
sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL);
pages = snd_pcm_sgbuf_pages(size);
tblsize = sgbuf_align_table(pages);
if (pages < sgbuf->pages) {
/* release unsed pages */
sgbuf_shrink(sgbuf, pages);
substream->runtime->dma_bytes = size;
return 1; /* changed */
} else if (pages > sgbuf->tblsize) {
/* bigger than existing one. reallocate the table. */
struct snd_sg_page *table;
table = kmalloc(sizeof(*table) * tblsize, GFP_KERNEL);
if (! table)
return -ENOMEM;
memcpy(table, sgbuf->table, sizeof(*table) * sgbuf->tblsize);
kfree(sgbuf->table);
sgbuf->table = table;
sgbuf->tblsize = tblsize;
}
/* allocate each page */
while (sgbuf->pages < pages) {
void *ptr;
dma_addr_t addr;
ptr = snd_malloc_pci_pages(sgbuf->pci, PAGE_SIZE, &addr);
if (! ptr)
return -ENOMEM;
sgbuf->table[sgbuf->pages].buf = ptr;
sgbuf->table[sgbuf->pages].addr = addr;
sgbuf->pages++;
changed = 1;
}
sgbuf->size = size;
substream->runtime->dma_bytes = size;
return changed;
}
/*
* free the sg buffer
* the table is kept.
* call this from hw_free callback.
*/
int snd_pcm_sgbuf_free(snd_pcm_substream_t *substream)
{
struct snd_sg_buf *sgbuf;
sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL);
sgbuf_shrink(sgbuf, 0);
return 0;
}
/*
* get the page pointer on the given offset
* used as the page callback of pcm ops
*/
void *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset)
{
struct snd_sg_buf *sgbuf;
unsigned int idx;
sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return NULL);
idx = offset >> PAGE_SHIFT;
if (idx >= sgbuf->pages)
return 0;
return sgbuf->table[idx].buf;
}
/*
* do copy_from_user to the sg buffer
*/
static int copy_from_user_sg_buf(snd_pcm_substream_t *substream,
char *buf, size_t hwoff, ssize_t bytes)
{
int len;
char *addr;
size_t p = (hwoff >> PAGE_SHIFT) << PAGE_SHIFT;
hwoff -= p;
len = PAGE_SIZE - hwoff;
for (;;) {
addr = snd_pcm_sgbuf_ops_page(substream, p);
if (! addr)
return -EFAULT;
if (len > bytes)
len = bytes;
if (copy_from_user(addr + hwoff, buf, len))
return -EFAULT;
bytes -= len;
if (bytes <= 0)
break;
buf += len;
p += PAGE_SIZE;
len = PAGE_SIZE;
hwoff = 0;
}
return 0;
}
/*
* do copy_to_user from the sg buffer
*/
static int copy_to_user_sg_buf(snd_pcm_substream_t *substream,
char *buf, size_t hwoff, ssize_t bytes)
{
int len;
char *addr;
size_t p = (hwoff >> PAGE_SHIFT) << PAGE_SHIFT;
hwoff -= p;
len = PAGE_SIZE - hwoff;
for (;;) {
addr = snd_pcm_sgbuf_ops_page(substream, p);
if (! addr)
return -EFAULT;
if (len > bytes)
len = bytes;
if (copy_to_user(buf, addr + hwoff, len))
return -EFAULT;
bytes -= len;
if (bytes <= 0)
break;
buf += len;
p += PAGE_SIZE;
len = PAGE_SIZE;
hwoff = 0;
}
return 0;
}
/*
* set silence on the sg buffer
*/
static int set_silence_sg_buf(snd_pcm_substream_t *substream,
size_t hwoff, ssize_t samples)
{
snd_pcm_runtime_t *runtime = substream->runtime;
int len, page_len;
char *addr;
size_t p = (hwoff >> PAGE_SHIFT) << PAGE_SHIFT;
hwoff -= p;
len = bytes_to_samples(substream->runtime, PAGE_SIZE - hwoff);
page_len = bytes_to_samples(substream->runtime, PAGE_SIZE);
for (;;) {
addr = snd_pcm_sgbuf_ops_page(substream, p);
if (! addr)
return -EFAULT;
if (len > samples)
len = samples;
snd_pcm_format_set_silence(runtime->format, addr + hwoff, len);
samples -= len;
if (samples <= 0)
break;
p += PAGE_SIZE;
len = page_len;
hwoff = 0;
}
return 0;
}
/*
* copy callback for playback pcm ops
*/
int snd_pcm_sgbuf_ops_copy_playback(snd_pcm_substream_t *substream, int channel,
snd_pcm_uframes_t hwoff, void *buf, snd_pcm_uframes_t count)
{
snd_pcm_runtime_t *runtime = substream->runtime;
if (channel < 0) {
return copy_from_user_sg_buf(substream, buf, frames_to_bytes(runtime, hwoff), frames_to_bytes(runtime, count));
} else {
size_t dma_csize = runtime->dma_bytes / runtime->channels;
size_t c_ofs = (channel * dma_csize) + samples_to_bytes(runtime, hwoff);
return copy_from_user_sg_buf(substream, buf, c_ofs, samples_to_bytes(runtime, count));
}
}
/*
* copy callback for capture pcm ops
*/
int snd_pcm_sgbuf_ops_copy_capture(snd_pcm_substream_t *substream, int channel,
snd_pcm_uframes_t hwoff, void *buf, snd_pcm_uframes_t count)
{
snd_pcm_runtime_t *runtime = substream->runtime;
if (channel < 0) {
return copy_to_user_sg_buf(substream, buf, frames_to_bytes(runtime, hwoff), frames_to_bytes(runtime, count));
} else {
size_t dma_csize = runtime->dma_bytes / runtime->channels;
size_t c_ofs = (channel * dma_csize) + samples_to_bytes(runtime, hwoff);
return copy_to_user_sg_buf(substream, buf, c_ofs, samples_to_bytes(runtime, count));
}
}
/*
* silence callback for pcm ops
*/
int snd_pcm_sgbuf_ops_silence(snd_pcm_substream_t *substream, int channel,
snd_pcm_uframes_t hwoff, snd_pcm_uframes_t count)
{
snd_pcm_runtime_t *runtime = substream->runtime;
if (channel < 0) {
return set_silence_sg_buf(substream, frames_to_bytes(runtime, hwoff),
frames_to_bytes(runtime, count));
} else {
size_t dma_csize = runtime->dma_bytes / runtime->channels;
size_t c_ofs = (channel * dma_csize) + samples_to_bytes(runtime, hwoff);
return set_silence_sg_buf(substream, c_ofs, samples_to_bytes(runtime, count));
}
}
/*
* Exported symbols
*/
EXPORT_SYMBOL(snd_pcm_sgbuf_init);
EXPORT_SYMBOL(snd_pcm_sgbuf_delete);
EXPORT_SYMBOL(snd_pcm_sgbuf_alloc);
EXPORT_SYMBOL(snd_pcm_sgbuf_free);
EXPORT_SYMBOL(snd_pcm_sgbuf_ops_copy_playback);
EXPORT_SYMBOL(snd_pcm_sgbuf_ops_copy_capture);
EXPORT_SYMBOL(snd_pcm_sgbuf_ops_silence);
EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
......@@ -23,6 +23,7 @@
#include <sound/driver.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/threads.h>
#include <linux/interrupt.h>
#include <sound/core.h>
#include <sound/timer.h>
......
......@@ -501,6 +501,8 @@ EXPORT_SYMBOL(snd_printd);
#ifdef CONFIG_SND_DEBUG_MEMORY
EXPORT_SYMBOL(snd_wrapper_kmalloc);
EXPORT_SYMBOL(snd_wrapper_kfree);
EXPORT_SYMBOL(snd_wrapper_vmalloc);
EXPORT_SYMBOL(snd_wrapper_vfree);
#endif
#ifdef HACK_PCI_ALLOC_CONSISTENT
EXPORT_SYMBOL(snd_pci_hack_alloc_consistent);
......
......@@ -227,6 +227,8 @@ static struct isapnp_card_id snd_card_pnpids[] __devinitdata = {
ISAPNP_CS4232('C','S','C',0x4336,0x0000,0x0010,0x0003),
/* Typhoon Soundsystem PnP - CS4236B */
ISAPNP_CS4232('C','S','C',0x4536,0x0000,0x0010,0x0003),
/* Crystal CX4235-XQ3 EP - CS4235 */
ISAPNP_CS4232('C','S','C',0x4625,0x0100,0x0110,0x0103),
/* TerraTec AudioSystem EWS64XL - CS4236B */
ISAPNP_CS4232('C','S','C',0xa836,0xa800,0xa810,0xa803),
/* TerraTec AudioSystem EWS64XL - CS4236B */
......
......@@ -7,6 +7,10 @@ CONFIG_SND_CS46XX
CONFIG_SND_CS46XX_ACCEPT_VALID
Say 'Y' to allow sample resolution for mmap() transfers.
Note: This can be also specified via module option snd_mmap_valid.
CONFIG_SND_CS4281
Say 'Y' or 'M' to include support for Cirrus Logic CS4281.
CONFIG_SND_EMU10K1
Say 'Y' or 'M' to include support for Sound Blaster PCI 512, Live!,
......
......@@ -8,7 +8,7 @@ dep_tristate 'Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x' CONFIG_SN
if [ "$CONFIG_SND_CS46XX" != "n" ]; then
bool ' Cirrus Logic (Sound Fusion) MMAP support for OSS' CONFIG_SND_CS46XX_ACCEPT_VALID
fi
dep_tristate 'Cirrus Logic (Sound Fusion) CS4281' CONFIG_SND_CS4281 $CONFIG_SND
dep_tristate 'Cirrus Logic CS4281' CONFIG_SND_CS4281 $CONFIG_SND
dep_tristate 'EMU10K1 (SB Live!, E-mu APS)' CONFIG_SND_EMU10K1 $CONFIG_SND
dep_tristate 'Korg 1212 IO' CONFIG_SND_KORG1212 $CONFIG_SND
dep_tristate 'NeoMagic NM256AV/ZX' CONFIG_SND_NM256 $CONFIG_SND
......
......@@ -32,6 +32,7 @@
#include <linux/init.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
#include <sound/pcm_sgbuf.h>
#define chip_t emu10k1_t
......@@ -358,13 +359,13 @@ static int snd_emu10k1_playback_hw_params(snd_pcm_substream_t * substream,
if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0)
return err;
if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
if ((err = snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params))) < 0)
return err;
if (err > 0) { /* change */
snd_util_memblk_t *memblk;
if (epcm->memblk != NULL)
snd_emu10k1_free_pages(emu, epcm->memblk);
memblk = snd_emu10k1_alloc_pages(emu, runtime->dma_addr, runtime->dma_bytes);
memblk = snd_emu10k1_alloc_pages(emu, (struct snd_sg_buf *)substream->dma_private);
if ((epcm->memblk = memblk) == NULL || ((emu10k1_memblk_t *)memblk)->mapped_page < 0) {
epcm->start_addr = 0;
return -ENOMEM;
......@@ -400,7 +401,7 @@ static int snd_emu10k1_playback_hw_free(snd_pcm_substream_t * substream)
epcm->memblk = NULL;
epcm->start_addr = 0;
}
snd_pcm_lib_free_pages(substream);
snd_pcm_sgbuf_free(substream);
return 0;
}
......@@ -778,6 +779,10 @@ static int snd_emu10k1_playback_open(snd_pcm_substream_t * substream)
runtime->private_data = epcm;
runtime->private_free = snd_emu10k1_pcm_free_substream;
runtime->hw = snd_emu10k1_playback;
if ((err = snd_pcm_sgbuf_init(substream, emu->pci, 32)) < 0) {
snd_magic_kfree(epcm);
return err;
}
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) {
snd_magic_kfree(epcm);
return err;
......@@ -804,6 +809,7 @@ static int snd_emu10k1_playback_close(snd_pcm_substream_t * substream)
emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[substream->number];
mix->epcm = NULL;
snd_pcm_sgbuf_delete(substream);
snd_emu10k1_pcm_mixer_notify(emu->card, mix, 0);
return 0;
}
......@@ -942,6 +948,9 @@ static snd_pcm_ops_t snd_emu10k1_playback_ops = {
prepare: snd_emu10k1_playback_prepare,
trigger: snd_emu10k1_playback_trigger,
pointer: snd_emu10k1_playback_pointer,
copy: snd_pcm_sgbuf_ops_copy_playback,
silence: snd_pcm_sgbuf_ops_silence,
page: snd_pcm_sgbuf_ops_page,
};
static snd_pcm_ops_t snd_emu10k1_capture_ops = {
......@@ -959,7 +968,6 @@ static void snd_emu10k1_pcm_free(snd_pcm_t *pcm)
{
emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return);
emu->pcm = NULL;
snd_pcm_lib_preallocate_free_for_all(pcm);
}
int __devinit snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm)
......@@ -984,8 +992,6 @@ int __devinit snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm)
strcpy(pcm->name, "EMU10K1");
emu->pcm = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 128*1024);
if (rpcm)
*rpcm = pcm;
......
......@@ -289,22 +289,19 @@ int snd_emu10k1_memblk_map(emu10k1_t *emu, emu10k1_memblk_t *blk)
* page allocation for DMA
*/
snd_util_memblk_t *
snd_emu10k1_alloc_pages(emu10k1_t *emu, dma_addr_t addr, unsigned long size)
snd_emu10k1_alloc_pages(emu10k1_t *emu, struct snd_sg_buf *sgbuf)
{
snd_util_memhdr_t *hdr;
emu10k1_memblk_t *blk;
int page, err;
int page, err, idx;
snd_assert(emu, return NULL);
snd_assert(size > 0 && size < MAXPAGES * EMUPAGESIZE, return NULL);
snd_assert(sgbuf->size > 0 && sgbuf->size < MAXPAGES * EMUPAGESIZE, return NULL);
hdr = emu->memhdr;
snd_assert(hdr, return NULL);
if (!is_valid_page(addr))
return NULL;
down(&hdr->block_mutex);
blk = search_empty(emu, size);
blk = search_empty(emu, sgbuf->size);
if (blk == NULL) {
up(&hdr->block_mutex);
return NULL;
......@@ -312,10 +309,15 @@ snd_emu10k1_alloc_pages(emu10k1_t *emu, dma_addr_t addr, unsigned long size)
/* fill buffer addresses but pointers are not stored so that
* snd_free_pci_pages() is not called in in synth_free()
*/
for (page = blk->first_page; page <= blk->last_page; page++) {
idx = 0;
for (page = blk->first_page; page <= blk->last_page; page++, idx++) {
dma_addr_t addr = sgbuf->table[idx].addr;
if (! is_valid_page(addr)) {
up(&hdr->block_mutex);
return NULL;
}
emu->page_addr_table[page] = addr;
emu->page_ptr_table[page] = NULL;
addr += PAGE_SIZE;
}
/* set PTB entries */
......
......@@ -482,7 +482,6 @@ static inline int hdsp_write_gain(hdsp_t *hdsp, unsigned int addr, unsigned shor
if (hdsp_fifo_wait(hdsp, 127, HDSP_LONG_WAIT)) {
return -1;
}
hdsp_write (hdsp, HDSP_fifoData, ad);
hdsp->mixer_matrix[addr] = data;
......@@ -854,10 +853,7 @@ static int snd_hdsp_midi_output_write (hdsp_midi_t *hmidi)
}
if (clear_timer && hmidi->istimer && --hmidi->istimer <= 0) {
printk ("removing timer because there is nothing to do\n");
if (del_timer(&hmidi->timer)) {
printk ("not removed\n");
}
del_timer(&hmidi->timer);
}
}
......@@ -956,16 +952,12 @@ static void snd_hdsp_midi_output_trigger(snd_rawmidi_substream_t * substream, in
hmidi->timer.function = snd_hdsp_midi_output_timer;
hmidi->timer.data = (unsigned long) hmidi;
hmidi->timer.expires = 1 + jiffies;
printk ("add timer from output trigger\n");
add_timer(&hmidi->timer);
hmidi->istimer++;
}
} else {
if (hmidi->istimer && --hmidi->istimer <= 0) {
printk ("remove timer in trigger off\n");
if (del_timer (&hmidi->timer)) {
printk ("not removed\n");
}
del_timer (&hmidi->timer);
}
}
spin_unlock_irqrestore (&hmidi->lock, flags);
......@@ -2560,7 +2552,7 @@ static snd_pcm_hardware_t snd_hdsp_playback_subinfo =
SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_DOUBLE),
formats: SNDRV_PCM_FMTBIT_S32_LE,
rates: (SNDRV_PCM_RATE_32000 |
rates: (SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_64000 |
......@@ -2635,7 +2627,7 @@ static int snd_hdsp_hw_rule_channels_rate(snd_pcm_hw_params_t *params,
integer: 1,
};
return snd_interval_refine(c, &t);
} else if (r->max < 88200) {
} else if (r->max < 64000) {
snd_interval_t t = {
min: hdsp->ss_channels,
max: hdsp->ss_channels,
......@@ -2654,14 +2646,14 @@ static int snd_hdsp_hw_rule_rate_channels(snd_pcm_hw_params_t *params,
snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
if (c->min >= hdsp->ss_channels) {
snd_interval_t t = {
min: 44100,
min: 32000,
max: 48000,
integer: 1,
};
return snd_interval_refine(r, &t);
} else if (c->max <= hdsp->ds_channels) {
snd_interval_t t = {
min: 88200,
min: 64000,
max: 96000,
integer: 1,
};
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment