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);
......@@ -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,
};
......
......@@ -28,6 +28,8 @@
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_sgbuf.h>
#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/ac97_codec.h>
#include <sound/mpu401.h>
......@@ -135,29 +137,92 @@ MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:48000");
#define VIA_REG_AC97_DATA_MASK 0xffff
#define VIA_REG_SGD_SHADOW 0x84 /* dword */
/*
*
*/
#define VIA_MAX_FRAGS 32
#define VIA_TBL_BIT_FLAG 0x40000000
#define VIA_TBL_BIT_EOL 0x80000000
/*
*
* pcm stream
*/
typedef struct {
unsigned long reg_offset;
unsigned int *table;
dma_addr_t table_addr;
snd_pcm_substream_t *substream;
dma_addr_t physbuf;
int running;
unsigned int size;
unsigned int fragsize;
unsigned int frags;
unsigned int lastptr;
unsigned int lastcount;
unsigned int page_per_frag;
unsigned int curidx;
unsigned int tbl_entries; /* number of descriptor table entries */
unsigned int tbl_size; /* size of a table entry */
u32 *table; /* physical address + flag */
dma_addr_t table_addr;
} viadev_t;
static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
struct pci_dev *pci)
{
int i, pages, size;
struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL);
if (dev->table) {
pages = snd_pcm_sgbuf_pages(dev->tbl_entries * 8);
snd_free_pci_pages(pci, pages << PAGE_SHIFT, dev->table, dev->table_addr);
dev->table = NULL;
}
/* allocate buffer descriptor lists */
if (dev->fragsize < PAGE_SIZE) {
dev->tbl_size = dev->fragsize;
dev->tbl_entries = dev->frags;
dev->page_per_frag = 1;
} else {
dev->tbl_size = PAGE_SIZE;
dev->tbl_entries = sgbuf->pages;
dev->page_per_frag = dev->fragsize >> PAGE_SHIFT;
}
/* the start of each lists must be aligned to 8 bytes,
* but the kernel pages are much bigger, so we don't care
*/
pages = snd_pcm_sgbuf_pages(dev->tbl_entries * 8);
dev->table = (u32*)snd_malloc_pci_pages(pci, pages << PAGE_SHIFT, &dev->table_addr);
if (! dev->table)
return -ENOMEM;
if (dev->tbl_size < PAGE_SIZE) {
for (i = 0; i < dev->tbl_entries; i++)
dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[0].addr + dev->fragsize * i);
} else {
for (i = 0; i < dev->tbl_entries; i++)
dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[i].addr);
}
size = dev->size;
for (i = 0; i < dev->tbl_entries - 1; i++) {
dev->table[(i << 1) + 1] = cpu_to_le32(VIA_TBL_BIT_FLAG | dev->tbl_size);
size -= dev->tbl_size;
}
dev->table[(dev->tbl_entries << 1) - 1] = cpu_to_le32(VIA_TBL_BIT_EOL | size);
return 0;
}
static void clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
struct pci_dev *pci)
{
if (dev->table) {
snd_free_pci_pages(pci, snd_pcm_sgbuf_pages(dev->tbl_entries * 8) << PAGE_SHIFT, dev->table, dev->table_addr);
dev->table = NULL;
}
}
/*
*/
typedef struct _snd_via686a via686a_t;
#define chip_t via686a_t
......@@ -178,7 +243,7 @@ struct _snd_via686a {
snd_pcm_t *pcm_fm;
viadev_t playback;
viadev_t capture;
viadev_t playback_fm;
/*viadev_t playback_fm;*/
snd_rawmidi_t *rmidi;
......@@ -188,9 +253,6 @@ struct _snd_via686a {
spinlock_t reg_lock;
snd_info_entry_t *proc_entry;
void *tables;
dma_addr_t tables_addr;
};
static struct pci_device_id snd_via686a_ids[] __devinitdata = {
......@@ -336,15 +398,19 @@ static int snd_via686a_trigger(via686a_t *chip, viadev_t *viadev, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
val = VIA_REG_CTRL_START;
viadev->running = 1;
break;
case SNDRV_PCM_TRIGGER_STOP:
val = VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET;
viadev->running = 0;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
val = VIA_REG_CTRL_PAUSE;
viadev->running = 1;
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
val = 0;
viadev->running = 0;
break;
default:
return -EINVAL;
......@@ -355,44 +421,43 @@ static int snd_via686a_trigger(via686a_t *chip, viadev_t *viadev, int cmd)
return 0;
}
static void snd_via686a_setup_periods(via686a_t *chip, viadev_t *viadev,
static int snd_via686a_setup_periods(via686a_t *chip, viadev_t *viadev,
snd_pcm_substream_t *substream)
{
snd_pcm_runtime_t *runtime = substream->runtime;
int idx, frags;
unsigned int *table = viadev->table;
unsigned long port = chip->port + viadev->reg_offset;
int v, err;
viadev->physbuf = runtime->dma_addr;
viadev->size = snd_pcm_lib_buffer_bytes(substream);
viadev->fragsize = snd_pcm_lib_period_bytes(substream);
viadev->frags = runtime->periods;
viadev->lastptr = ~0;
viadev->lastcount = ~0;
viadev->curidx = 0;
/* the period size must be in power of 2 */
v = ld2(viadev->fragsize);
if (viadev->fragsize != (1 << v)) {
snd_printd(KERN_ERR "invalid fragment size %d\n", viadev->fragsize);
return -EINVAL;
}
snd_via686a_channel_reset(chip, viadev);
outl(viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR);
err = build_via_table(viadev, substream, chip->pci);
if (err < 0)
return err;
runtime->dma_bytes = viadev->size;
outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR);
outb(VIA_REG_TYPE_AUTOSTART |
(runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) |
(runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) |
((viadev->reg_offset & 0x10) == 0 ? VIA_REG_TYPE_INT_LSAMPLE : 0) |
VIA_REG_TYPE_INT_EOL |
VIA_REG_TYPE_INT_FLAG, port + VIA_REG_OFFSET_TYPE);
if (viadev->size == viadev->fragsize) {
table[0] = cpu_to_le32(viadev->physbuf);
table[1] = cpu_to_le32(0xc0000000 | /* EOL + flag */
viadev->fragsize);
} else {
frags = viadev->size / viadev->fragsize;
for (idx = 0; idx < frags - 1; idx++) {
table[(idx << 1) + 0] = cpu_to_le32(viadev->physbuf + (idx * viadev->fragsize));
table[(idx << 1) + 1] = cpu_to_le32(0x40000000 | /* flag */
viadev->fragsize);
}
table[((frags-1) << 1) + 0] = cpu_to_le32(viadev->physbuf + ((frags-1) * viadev->fragsize));
table[((frags-1) << 1) + 1] = cpu_to_le32(0x80000000 | /* EOL */
viadev->fragsize);
}
return 0;
}
/*
......@@ -401,8 +466,16 @@ static void snd_via686a_setup_periods(via686a_t *chip, viadev_t *viadev,
static inline void snd_via686a_update(via686a_t *chip, viadev_t *viadev)
{
snd_pcm_period_elapsed(viadev->substream);
outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset);
if (viadev->substream && viadev->running) {
viadev->curidx++;
if (viadev->curidx >= viadev->page_per_frag) {
viadev->curidx = 0;
spin_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(viadev->substream);
spin_lock(&chip->reg_lock);
}
}
}
static void snd_via686a_interrupt(int irq, void *dev_id, struct pt_regs *regs)
......@@ -410,8 +483,10 @@ static void snd_via686a_interrupt(int irq, void *dev_id, struct pt_regs *regs)
via686a_t *chip = snd_magic_cast(via686a_t, dev_id, return);
unsigned int status;
spin_lock(&chip->reg_lock);
status = inl(VIAREG(chip, SGD_SHADOW));
if ((status & 0x00000077) == 0) {
spin_unlock(&chip->reg_lock);
if (chip->rmidi != NULL) {
snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
}
......@@ -419,10 +494,11 @@ static void snd_via686a_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
if (inb(VIAREG(chip, PLAYBACK_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
snd_via686a_update(chip, &chip->playback);
if (inb(VIAREG(chip, FM_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
snd_via686a_update(chip, &chip->playback_fm);
if (inb(VIAREG(chip, CAPTURE_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
snd_via686a_update(chip, &chip->capture);
/*if (inb(VIAREG(chip, FM_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
snd_via686a_update(chip, &chip->playback_fm);*/
spin_unlock(&chip->reg_lock);
}
/*
......@@ -448,12 +524,12 @@ static int snd_via686a_capture_trigger(snd_pcm_substream_t * substream,
static int snd_via686a_hw_params(snd_pcm_substream_t * substream,
snd_pcm_hw_params_t * hw_params)
{
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
return snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params));
}
static int snd_via686a_hw_free(snd_pcm_substream_t * substream)
{
return snd_pcm_lib_free_pages(substream);
return 0;
}
static int snd_via686a_playback_prepare(snd_pcm_substream_t * substream)
......@@ -462,8 +538,7 @@ static int snd_via686a_playback_prepare(snd_pcm_substream_t * substream)
snd_pcm_runtime_t *runtime = substream->runtime;
snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
snd_via686a_setup_periods(chip, &chip->playback, substream);
return 0;
return snd_via686a_setup_periods(chip, &chip->playback, substream);
}
static int snd_via686a_capture_prepare(snd_pcm_substream_t * substream)
......@@ -472,8 +547,7 @@ static int snd_via686a_capture_prepare(snd_pcm_substream_t * substream)
snd_pcm_runtime_t *runtime = substream->runtime;
snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
snd_via686a_setup_periods(chip, &chip->capture, substream);
return 0;
return snd_via686a_setup_periods(chip, &chip->capture, substream);
}
static inline unsigned int snd_via686a_cur_ptr(via686a_t *chip, viadev_t *viadev)
......@@ -486,9 +560,14 @@ static inline unsigned int snd_via686a_cur_ptr(via686a_t *chip, viadev_t *viadev
ptr += 8;
if (!(inb(VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset) & VIA_REG_STAT_ACTIVE))
return 0;
val = (((unsigned int)(ptr - viadev->table_addr) / 8) - 1) % viadev->frags;
val *= viadev->fragsize;
val += viadev->fragsize - count;
snd_assert(viadev->tbl_entries, return 0);
/* get index */
if (ptr <= (unsigned int)viadev->table_addr)
val = 0;
else
val = ((ptr - (unsigned int)viadev->table_addr) / 8 - 1) % viadev->tbl_entries;
val *= viadev->tbl_size;
val += viadev->tbl_size - count;
viadev->lastptr = ptr;
viadev->lastcount = count;
// printk("pointer: ptr = 0x%x (%i), count = 0x%x, val = 0x%x\n", ptr, count, val);
......@@ -522,8 +601,8 @@ static snd_pcm_hardware_t snd_via686a_playback =
buffer_bytes_max: 128 * 1024,
period_bytes_min: 32,
period_bytes_max: 128 * 1024,
periods_min: 1,
periods_max: VIA_MAX_FRAGS,
periods_min: 2,
periods_max: 1024,
fifo_size: 0,
};
......@@ -541,8 +620,8 @@ static snd_pcm_hardware_t snd_via686a_capture =
buffer_bytes_max: 128 * 1024,
period_bytes_min: 32,
period_bytes_max: 128 * 1024,
periods_min: 1,
periods_max: VIA_MAX_FRAGS,
periods_min: 2,
periods_max: 1024,
fifo_size: 0,
};
......@@ -555,10 +634,20 @@ static int snd_via686a_playback_open(snd_pcm_substream_t * substream)
chip->playback.substream = substream;
runtime->hw = snd_via686a_playback;
runtime->hw.rates = chip->ac97->rates_front_dac;
if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
return err;
if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
runtime->hw.rate_min = 48000;
if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
return err;
#if 0
/* applying the following constraint together with the power-of-2 rule
* above may result in too narrow space.
* this one is not strictly necessary, so let's disable it.
*/
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
return err;
#endif
return 0;
}
......@@ -571,10 +660,16 @@ static int snd_via686a_capture_open(snd_pcm_substream_t * substream)
chip->capture.substream = substream;
runtime->hw = snd_via686a_capture;
runtime->hw.rates = chip->ac97->rates_adc;
if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
return err;
if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
runtime->hw.rate_min = 48000;
if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
return err;
#if 0
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
return err;
#endif
return 0;
}
......@@ -582,6 +677,8 @@ static int snd_via686a_playback_close(snd_pcm_substream_t * substream)
{
via686a_t *chip = snd_pcm_substream_chip(substream);
clean_via_table(&chip->playback, substream, chip->pci);
snd_pcm_sgbuf_delete(substream);
chip->playback.substream = NULL;
/* disable DAC power */
snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200);
......@@ -592,6 +689,8 @@ static int snd_via686a_capture_close(snd_pcm_substream_t * substream)
{
via686a_t *chip = snd_pcm_substream_chip(substream);
clean_via_table(&chip->capture, substream, chip->pci);
snd_pcm_sgbuf_delete(substream);
chip->capture.substream = NULL;
/* disable ADC power */
snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
......@@ -607,6 +706,9 @@ static snd_pcm_ops_t snd_via686a_playback_ops = {
prepare: snd_via686a_playback_prepare,
trigger: snd_via686a_playback_trigger,
pointer: snd_via686a_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_via686a_capture_ops = {
......@@ -618,13 +720,15 @@ static snd_pcm_ops_t snd_via686a_capture_ops = {
prepare: snd_via686a_capture_prepare,
trigger: snd_via686a_capture_trigger,
pointer: snd_via686a_capture_pointer,
copy: snd_pcm_sgbuf_ops_copy_capture,
silence: snd_pcm_sgbuf_ops_silence,
page: snd_pcm_sgbuf_ops_page,
};
static void snd_via686a_pcm_free(snd_pcm_t *pcm)
{
via686a_t *chip = snd_magic_cast(via686a_t, pcm->private_data, return);
chip->pcm = NULL;
snd_pcm_lib_preallocate_free_for_all(pcm);
}
static int __devinit snd_via686a_pcm(via686a_t *chip, int device, snd_pcm_t ** rpcm)
......@@ -647,146 +751,11 @@ static int __devinit snd_via686a_pcm(via686a_t *chip, int device, snd_pcm_t ** r
strcpy(pcm->name, "VIA 82C686A");
chip->pcm = pcm;
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 128*1024);
if (rpcm)
*rpcm = NULL;
return 0;
}
#if 0
/*
* PCM code - FM channel
*/
static int snd_via686a_playback_fm_ioctl(snd_pcm_substream_t * substream,
unsigned int cmd,
void *arg)
{
return snd_pcm_lib_ioctl(substream, cmd, arg);
}
static int snd_via686a_playback_fm_trigger(snd_pcm_substream_t * substream,
int cmd)
{
via686a_t *chip = snd_pcm_substream_chip(substream);
return snd_via686a_trigger(chip, &chip->playback_fm, cmd);
}
static int snd_via686a_playback_fm_prepare(snd_pcm_substream_t * substream)
{
via686a_t *chip = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
snd_via686a_setup_periods(chip, &chip->playback_fm, substream);
return 0;
}
static snd_pcm_uframes_t snd_via686a_playback_fm_pointer(snd_pcm_substream_t * substream)
{
via686a_t *chip = snd_pcm_substream_chip(substream);
return bytes_to_frames(substream->runtime, snd_via686a_cur_ptr(chip, &chip->playback_fm));
}
static snd_pcm_hardware_t snd_via686a_playback_fm =
{
info: (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE),
formats: SNDRV_PCM_FMTBIT_S16_LE,
rates: SNDRV_PCM_RATE_KNOT,
rate_min: 24000,
rate_max: 24000,
channels_min: 2,
channels_max: 2,
buffer_bytes_max: 128 * 1024,
period_bytes_min: 32,
period_bytes_max: 128 * 1024,
periods_min: 1,
periods_max: VIA_MAX_FRAGS,
fifo_size: 0,
};
static int snd_via686a_playback_fm_open(snd_pcm_substream_t * substream)
{
via686a_t *chip = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
int err;
if ((runtime->dma_area = snd_malloc_pci_pages_fallback(chip->pci, chip->dma_fm_size, &runtime->dma_addr, &runtime->dma_bytes)) == NULL)
return -ENOMEM;
chip->playback_fm.substream = substream;
runtime->hw = snd_via686a_playback_fm;
#if 0
runtime->hw.rates = chip->ac97->rates_front_dac;
if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
runtime->hw.min_rate = 48000;
#endif
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
return err;
return 0;
}
static int snd_via686a_playback_fm_close(snd_pcm_substream_t * substream)
{
via686a_t *chip = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
chip->playback_fm.substream = NULL;
snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr);
/* disable DAC power */
snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200);
return 0;
}
static snd_pcm_ops_t snd_via686a_playback_fm_ops = {
open: snd_via686a_playback_fm_open,
close: snd_via686a_playback_fm_close,
ioctl: snd_pcm_lib_ioctl,
prepare: snd_via686a_playback_fm_prepare,
trigger: snd_via686a_playback_fm_trigger,
pointer: snd_via686a_playback_fm_pointer,
};
static void snd_via686a_pcm_fm_free(void *private_data)
{
via686a_t *chip = snd_magic_cast(via686a_t, private_data, return);
chip->pcm_fm = NULL;
snd_pcm_lib_preallocate_pci_free_for_all(ensoniq->pci, pcm);
}
static int __devinit snd_via686a_pcm_fm(via686a_t *chip, int device, snd_pcm_t ** rpcm)
{
snd_pcm_t *pcm;
int err;
if (rpcm)
*rpcm = NULL;
err = snd_pcm_new(chip->card, "VIA 82C686A - FM DAC", device, 1, 0, &pcm);
if (err < 0)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686a_playback_fm_ops);
pcm->private_data = chip;
pcm->private_free = snd_via686a_pcm_fm_free;
pcm->info_flags = 0;
strcpy(pcm->name, "VIA 82C686A - FM DAC");
snd_pcm_add_buffer_bytes_controls(pcm);
snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024);
chip->pcm_fm = pcm;
if (rpcm)
*rpcm = NULL;
return 0;
}
#endif
/*
* Mixer part
......@@ -979,7 +948,7 @@ static int __devinit snd_via686a_chip_init(via686a_t *chip)
/* disable interrupts */
snd_via686a_channel_reset(chip, &chip->playback);
snd_via686a_channel_reset(chip, &chip->capture);
snd_via686a_channel_reset(chip, &chip->playback_fm);
/*snd_via686a_channel_reset(chip, &chip->playback_fm);*/
return 0;
}
......@@ -990,13 +959,10 @@ static int snd_via686a_free(via686a_t *chip)
/* disable interrupts */
snd_via686a_channel_reset(chip, &chip->playback);
snd_via686a_channel_reset(chip, &chip->capture);
snd_via686a_channel_reset(chip, &chip->playback_fm);
/*snd_via686a_channel_reset(chip, &chip->playback_fm);*/
/* --- */
__end_hw:
if(chip->irq >= 0)
synchronize_irq(chip->irq);
if (chip->tables)
snd_free_pci_pages(chip->pci, 3 * sizeof(unsigned int) * VIA_MAX_FRAGS * 2, chip->tables, chip->tables_addr);
__end_hw:
if (chip->res_port) {
release_resource(chip->res_port);
kfree_nocheck(chip->res_port);
......@@ -1061,23 +1027,7 @@ static int __devinit snd_via686a_create(snd_card_t * card,
/* initialize offsets */
chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS;
chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS;
chip->playback_fm.reg_offset = VIA_REG_FM_STATUS;
/* allocate buffer descriptor lists */
/* the start of each lists must be aligned to 8 bytes */
chip->tables = (unsigned int *)snd_malloc_pci_pages(pci, 3 * sizeof(unsigned int) * VIA_MAX_FRAGS * 2, &chip->tables_addr);
if (chip->tables == NULL) {
snd_via686a_free(chip);
return -ENOMEM;
}
/* tables must be aligned to 8 bytes, but the kernel pages
are much bigger, so we don't care */
chip->playback.table = chip->tables;
chip->playback.table_addr = chip->tables_addr;
chip->capture.table = chip->playback.table + VIA_MAX_FRAGS * 2;
chip->capture.table_addr = chip->playback.table_addr + sizeof(unsigned int) * VIA_MAX_FRAGS * 2;
chip->playback_fm.table = chip->capture.table + VIA_MAX_FRAGS * 2;
chip->playback_fm.table_addr = chip->capture.table_addr + sizeof(unsigned int) * VIA_MAX_FRAGS * 2;
/*chip->playback_fm.reg_offset = VIA_REG_FM_STATUS;*/
if ((err = snd_via686a_chip_init(chip)) < 0) {
snd_via686a_free(chip);
......
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