Audio_LinuxALSA_5536_1.00.0500/ 0000755 0113737 0000121 00000000000 10404376553 015573 5 ustar background apache Audio_LinuxALSA_5536_1.00.0500/README.ALSA.txt 0000644 0113737 0000121 00000021535 10404376553 020016 0 ustar background apache AMD Geode(TM) LX/GX Processor and AMD Geode(TM) CS5536/CS5535 Companion Device
Linux ALSA Audio Driver - Release Notes
Version: 01.00.0500, General Release
Release Date: July 21, 2005
--------------------------------------------------------------------------------
PRODUCT INFORMATION
--------------------------------------------------------------------------------
Developed under Linux Kernel 2.6.11
This product includes:
- os_inc.c
- os_inc.h
- amd_geode.c
- amd_geode.h
- configure.ac
- Makefile.in
- README (this file)
- AUTHORS
- COPYING
Features
--------
- Playback of audio files in all formats including MP3 files
- Recording of audio files in all formats
- AC97 interface
- Compatible with AD1819 codec
- Compatible with 2.6.x Kernel
Applications Tested:
Playback:
- XMMS
- gmplayer (1.06pre-RPKM-3.4.2 over OSS )
- noatun
- aumix
- XINE player
- alsaplayer
- QuakeII
- aplay
- sox play
Recording:
- arecord
- sox rec
Dependencies
------------
- Linux Kernel 2.6.11
- ALSA rel. 1.6
- kernel patch v01.01.0302 or later (AC97 support,pcm_oss.c patch).
- To support OSS-based applications which use MMAP (like quake2) it is
necessary to patch the file pcm_oss.c in the kernel tree at
"/linux-2.6.11/sound/core/oss". Without this, the application is not
able to open the driver correctly!
Functional Changes
------------------
- Bug #2825: MIC Mixer at 0, MIC not muted [ALSA]
- Bug #3761: linux 2.6 l-16.001-036 ALSA garbled playback of recorded cd,
line, mic files
- Bug #2707 README corrections [ALSA]
Defects Corrected
-----------------
Issue: PBZ#2825 MIC Mixer at 0, MIC not muted [ALSA]
Description:
The Mixer sliders in alsamixer and aumix for PCM, LINE, CD, and MIC devices
move audio consistently down from 100 to 50 (although 50 drops the volume to
silent). At 49, the volume jumps back up to Full volume and then keeps moving
down as you move to 0.
Resolution:
Corrected an ALSA layer probing problem.
Issue: PBZ#3761 ALSA garbled playback of recorded cd, line, mic files
Description:
At 12000 Hz, files recorded using arecord/ALSA and played back with a
play/ALSA have "garbled" playback. Playback was not garbled during play/OSS
emulation playback.
Resolution:
Under any circumstances arecord chooses non-integer values for buffer-size and
period-size. This leads to a moving interrupt pointer which was still not
implemented for capturing.
Known Errata
------------
PBZ#5989 MPLAYER - A/V Sync is off, Video ahead of Audio
Notes: We recommend that builds of MPlayer use ALSA by default (as opposed
to OSS). If one needs to use the OSS-plugin, then we recommend this
driver be compiled with the setting DEFAULT_DMA_BUFFERSIZE=0x10000.
--------------------------------------------------------------------------------
VALIDATION ENVIRONMENT
--------------------------------------------------------------------------------
Validated on AMD Geode(TM) Norwich Development Board - Rev 2 Rework H and
Rev 3 Rework E with:
Processor: AMD Geode LX processor, silicon rev. C1 @ 433 MHz
Chipset: AMD Geode CS5536, silicon rev. B1
PCI Bus Speed: 66 MHz
Memory: 256 MB PC3200 RAM
CRT: NEC MultiSync FP2141SB
Flat Panel: Samsung LTM213U3-L07 1600x1200 2-channel LVDS,
Keyboard/Mouse: PS/2 on Moray 1.1
BIOS: GeodeROM 1.06.06
Operating System: Gentoo 2005.0
Kernel: v2.6.11
Kernel Patch: LX Kernel Patch v02.02.0100
Graphics Driver: LX Xorg Graphics driver v03.00.0100
Audio Driver: 5536 ALSA Audio driver v01.00.0500
AES Driver: LX AES driver v02.01.0100
AccessBus Driver: LX ACB Driver v01.00.0401
Video4Linux2 Driver: LX Video4Linux2 driver v03.02.0100
Other Software: N/A
--------------------------------------------------------------------------------
INSTALLATION INSTRUCTIONS
--------------------------------------------------------------------------------
Kernel Configuration
---------------------
- In order to use audio drivers under Linux, the ALSA
modules have to be installed.
How to build the Kernel with the ALSA installed:
1. Type: "cd /usr/src/linux"
2. Type: "make menuconfig"
3. Select "Loadable module support" and un-check
"Module versioning support" if set.
4. Select "Device Drivers" option
5. Select "Sound"
6. Select "Advanced Linux Sound Architecture"
7. Select "PCI Devices"
8. Check "AC97" (press M to include as a module)
9. From the main menu select exit, when prompted to save config,
say yes." This will separate the enabling sound step from the
exit/save step and the build step.
Save configuration and exit
Build kernel/modules and install as described in README.kernel.txt
Compiling and installing the driver (standalone):
-----------------------------------------------------------------------
- After unpacking the tarball, which contains the files listed above
in a plain directory, run :
1. autoconf (builds .configure)
2. ./configure --with-kernel-path= "your path to the kernel sources"
3. make install
You need to have root permissions to build and install the driver.
Follow the instructions of the install script
The built driver will be located in the local directory as well as in
/lib/modules/$kernel-path/kernel/drivers/misc
(Make sure that module support is enabled in the kernel configuration)
!!!! Additionally it's required to build the kernel with enabled
ALSA - ac97 driver. When the ac97 driver is not available, the amd_geode
audio driver cannot be loaded!!
(see above)
- run alsaconf
The OSS emulation documentation can be found under
$(KERNELDIR)/Documentation/sound/oss or on the internet at
http://www.alsa-project.org/~iwai/OSS-Emulation.html
--------------------------------------------------------------------------------
RELEASE HISTORY (Previous Version)
--------------------------------------------------------------------------------
Version: 01.00.0405
Release Date: May 26, 2005
Dependencies
------------
- Linux Kernel 2.6.11
- ALSA rel. 1.6
- kernel patch v01.01.0302 or later (AC97 support,pcm_oss.c patch).
- To support OSS-based applications that use MMAP (like quake2) it's necessary
to patch the file pcm_oss.c in the kernel tree at "/linux-2.6.11/sound/core/oss"
Without this, the application is not able to open the driver correctly!
Functional Changes
------------------
- First release
Defects Corrected
-----------------
- First release
Known Errata
------------
Issue: #2825 MIC Mixer at 0, MIC not muted [ALSA]
Description:
The Mixer sliders in alsamixer and aumix for PCM, LINE, CD, and MIC devices move
audio consistently down from 100 to 50 (although 50 drops the volume to silent).
At 49, the volume jumps back up to Full volume and then keeps moving down as
you move to 0.
Resolution:
Driver issue to be fixed with the next release.
Issue: #3761 ALSA garbled playback of recorded cd, line, mic files
Description:
At 12000 Hz, files recorded using arecord/ALSA and played back with aplay/ALSA
have "garbled" playback. Playback was not garbled during play/OSS emulation
playback.
Resolution:
Driver issue to be fixed with the next release.
================================================================================
Copyright
---------
Copyright 2005 Advanced Micro Devices, Inc. All rights reserved.
The contents of this document are provided in connection with Advanced Micro
Devices, Inc. ("AMD") products. AMD makes no representations or warranties
with respect to the accuracy or completeness of the contents of this
publication and reserves the right to make changes to specifications and
product descriptions at any time without notice. No license, whether express,
implied, arising by estoppel or otherwise, to any intellectual property rights
is granted by this publication. Except as set forth in AMD's Standard Terms
and Conditions of Sale, AMD assumes no liability whatsoever, and disclaims any
express or implied warranty, relating to its products including, but not
limited to, the implied warranty of merchantability, fitness for a particular
purpose, or infringement of any intellectual property right. AMD's products
are not designed, intended, authorized or warranted for use as components in
systems intended for surgical implant into the body, or in other applications
intended to support or sustain life, or in any other application in which the
failure of AMD's product could create a situation where personal injury, death,
or severe property or environmental damage may occur. AMD reserves the right
to discontinue or make changes to its products at any time without notice.
Trademarks
----------
AMD, the AMD Arrow logo, and combinations thereof, and Geode are trademarks
of Advanced Micro Devices, Inc.
Other product names used in this publication are for identification purposes
only and may be trademarks of their respective companies.
================================================================================
Audio_LinuxALSA_5536_1.00.0500/os_inc.c 0000444 0113737 0000121 00000012017 10247160737 017210 0 ustar background apache /*
*
* Copyright (c) Advanced Micro Devices, Inc.
*
* 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
*
*
*
*
*
* CS5535 documentation available from AMD.
*
*/
#include "os_inc.h"
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9))
#include
#endif
unsigned long TotalAllocatedMemory = 0;
unsigned long flags;
spinlock_t lock;
//
// OS-Specific calls
//
void OS_DbgMsg( unsigned char *szFormat, ... )
{
#ifdef AUDIO_DEBUG
char szBuf[ 128 ];
va_list arglist;
va_start( arglist, szFormat );
vsprintf( szBuf, szFormat, arglist );
va_end( arglist );
printk ( szBuf );
#endif
}
void * OS_AllocateMemory (unsigned long Size)
{
TotalAllocatedMemory+=Size;
return kmalloc( Size, GFP_KERNEL );
}
void OS_Free (unsigned char *pucBuff)
{
kfree( pucBuff );
}
void * OS_AllocateDMAMemory (void* pAdapterObject, unsigned long Size, unsigned long *PhysicalAddress)
{
unsigned int NumberOfPages, order;
void* VirtualAddress;
struct page *page, *pend;
VirtualAddress=NULL;
order=0;
NumberOfPages=Size/1024;
for (order = 0; (1 << order)<(int)NumberOfPages; order++);
VirtualAddress = (void *) __get_free_pages(GFP_KERNEL, order);
if(VirtualAddress)
{
*PhysicalAddress = virt_to_bus(VirtualAddress);
//
// now mark the pages as reserved; otherwise remap_page_range doesn't do what we want
//
pend = virt_to_page(VirtualAddress + (PAGE_SIZE << order) - 1);
for (page = virt_to_page(VirtualAddress); page <= pend; page++)
set_bit(PG_reserved, &((page)->flags));
TotalAllocatedMemory+=Size;
OS_DbgMsg("Allocating: %d - TotalAllocatedMemory: %d\n", Size, TotalAllocatedMemory);
}
return VirtualAddress;
}
void OS_FreeDMAMemory (void* pAdapterObject, void * VirtualAddress, unsigned long PhysicalAddress, unsigned long Size)
{
unsigned int NumberOfPages, order=0;
struct page *page, *pend;
OS_DbgMsg("OS_INC: Freeing %08X\n", PhysicalAddress);
NumberOfPages = Size/1024;
for (order = 0; (1 << order) < (int)NumberOfPages; order++);
//
// undo marking the pages as reserved
//
pend = virt_to_page(VirtualAddress + (PAGE_SIZE << order) - 1);
for (page = virt_to_page(VirtualAddress); page <= pend; page++)
clear_bit(PG_reserved, &((page)->flags));
free_pages((unsigned long) VirtualAddress, order);
TotalAllocatedMemory-=Size;
OS_DbgMsg("Freeing: %d - TotalAllocatedMemory: %d\n", Size, TotalAllocatedMemory);
VirtualAddress = NULL;
}
//
// Copies n bytes of audio data from the user to the DMA buffer space
//
unsigned char OS_CopyFromUser (unsigned char *DMABuffer, unsigned char *UserBuffer, unsigned long Count)
{
if (copy_from_user(DMABuffer, UserBuffer, Count))
{
return 0;
}
return 1;
}
unsigned char OS_CopyToUser (unsigned char *UserBuffer, unsigned char *DMABuffer, unsigned long Count)
{
if (copy_to_user(UserBuffer, DMABuffer, Count))
{
return 0;
}
return 1;
}
unsigned long OS_ReadPortULong(unsigned short Port)
{
return inl((int) Port);
}
void OS_WritePortULong (unsigned short Port, unsigned long Data)
{
outl (Data, (int) Port);
}
unsigned short OS_ReadPortUShort (unsigned short Port)
{
return inw((int) Port);
}
void OS_WritePortUShort (unsigned short Port, unsigned short Data)
{
outw (Data, (int) Port);
}
unsigned char OS_ReadPortUChar(unsigned short Port)
{
return inb((int) Port);
}
void OS_WritePortUChar (unsigned short Port, unsigned char Data)
{
outb (Data, (int) Port);
}
unsigned long OS_VirtualAddress (unsigned long PhysicalAddress)
{
return (unsigned long) phys_to_virt (PhysicalAddress);
}
unsigned long OS_Get_F3BAR0_Virt(unsigned long BAR_Phys)
{
unsigned long F3BAR_Virt;
F3BAR_Virt=(unsigned long) ioremap(BAR_Phys,128);
return F3BAR_Virt;
}
void OS_Sleep (unsigned long milliseconds)
{
if(!in_atomic())
{
current->state = TASK_INTERRUPTIBLE;
schedule_timeout( ( milliseconds * HZ ) / 1000 );
}
}
void OS_InitSpinLock (void)
{
spin_lock_init (&lock);
}
void OS_SpinLock (void)
{
spin_lock_irqsave (&lock, flags);
}
void OS_SpinUnlock (void)
{
spin_unlock_irqrestore (&lock, flags);
}
Audio_LinuxALSA_5536_1.00.0500/os_inc.h 0000444 0113737 0000121 00000007447 10247160737 017230 0 ustar background apache /*
*
* Copyright (c) Advanced Micro Devices, Inc.
*
* 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
*
*
*
*
*
* CS5535 documentation available from AMD.
*
*/
#ifndef __OS_INC_H__
#define __OS_INC_H__
#include
//#define AUDIO_DEBUG TRUE
//
// DMA Buffer constants
//
#define DEFAULT_FRAGMENT_SIZE 8192
#define DEFAULT_NUMBER_OF_FRAGMENTS 8
#define DEFAULT_DMA_BUFFER_SIZE DEFAULT_NUMBER_OF_FRAGMENTS*DEFAULT_FRAGMENT_SIZE
#define SIZE_OF_EACH_PRD_BLOCK 64 // Each PRD entry will point to 32 bytes (8 samples) of the DMA buffer
//
// Device and Vendor IDs - Needed like this for OSS only
//
#define CYRIX_VENDOR_ID 0x1078
#define NATIONAL_VENDOR_ID 0x100B
//
// Audio Device IDs
//
#define CX5530_DEV_ID 0x0103
#define SC1200_DEV_ID 0x0503
#define CS5535_DEV_ID 0x002E
#include
#include
#include
#include
//#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//---------------------------
// OS-Specific
//---------------------------
/* printing messages */
void OS_DbgMsg( unsigned char *szFormat, ... );
unsigned long OS_ReadPortULong (unsigned short Port);
unsigned short OS_ReadPortUShort (unsigned short Port);
unsigned char OS_ReadPortUChar (unsigned short Port);
void OS_WritePortULong (unsigned short Port, unsigned long Data);
void OS_WritePortUShort (unsigned short Port, unsigned short Data);
void OS_WritePortUChar (unsigned short Port, unsigned char Data);
void* OS_AllocateMemory (unsigned long Size);
void* OS_AllocateDMAMemory (void* pAdapterObject, unsigned long Size, unsigned long *PhysicalAddress);
void OS_FreeDMAMemory (void* pAdapterObject, void* VirtualAddress, unsigned long PhysicalAddress, unsigned long Size);
unsigned char OS_CopyFromUser (unsigned char *DMABuffer, unsigned char *UserBuffer, unsigned long Count);
unsigned char OS_CopyToUser (unsigned char *UserBuffer, unsigned char *DMABuffer, unsigned long Count);
unsigned long OS_VirtualAddress (unsigned long PhysAddress);
unsigned long OS_Get_F3BAR0_Virt (unsigned long BAR_Phys);
void OS_Free (unsigned char *pucBuff);
void OS_Sleep (unsigned long Delay);
void OS_InitSpinLock (void);
void OS_SpinLock (void);
void OS_SpinUnlock (void);
#endif // __OS_INC_H__
Audio_LinuxALSA_5536_1.00.0500/force48k.patch 0000444 0113737 0000121 00000001672 10247160736 020244 0 ustar background apache --- amd_geode.c 2004-11-03 19:07:40.000000000 -0700
+++ amd_geode.c_force48K 2004-12-07 18:31:13.000000000 -0700
@@ -226,8 +226,8 @@
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID ),
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .rate_min = 8000,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
@@ -246,8 +246,8 @@
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .rate_min = 8000,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
Audio_LinuxALSA_5536_1.00.0500/configure.ac 0000444 0113737 0000121 00000000662 10247160736 020062 0 ustar background apache AC_INIT(configure.ac)
AC_PROG_CC
AC_CHECK_TOOL(LD, ld, :)
AC_ARG_WITH(kernel-path, [ --with-kernel-path Specify the kernel path],
KERNEL_SRC=$withval, KERNEL_SRC="/usr/src/linux")
AC_ARG_WITH(module-path, [ --with-module-path Specify where to install the modules], INSTALL_MOD_PATH=$withval, INSTALL_MOD_PATH=/lib/modules)
CFLAGS="$CFLAGS -Wall"
AC_SUBST(KERNEL_SRC)
AC_SUBST(INSTALL_MOD_PATH)
AC_OUTPUT([
./Makefile
])
Audio_LinuxALSA_5536_1.00.0500/pcm_oss_2.6.10.patch 0000444 0113737 0000121 00000000655 10247160737 021067 0 ustar background apache --- ./linux-2.6.10/sound/core/oss/pcm_oss.c.org 2004-12-24 22:35:25.000000000 +0100
+++ ./linux-2.6.10/sound/core/oss/pcm_oss.c 2005-04-20 12:46:59.000000000 +0200
@@ -2232,8 +2232,8 @@
if ((err = snd_pcm_oss_change_params(substream)) < 0)
return err;
}
- if (runtime->oss.plugin_first != NULL)
- return -EIO;
+// if (runtime->oss.plugin_first != NULL)
+// return -EIO;
if (area->vm_pgoff != 0)
return -EINVAL;
Audio_LinuxALSA_5536_1.00.0500/amd_geode.c 0000444 0113737 0000121 00000256150 10247160736 017651 0 ustar background apache /*
*
* Copyright (c) Advanced Micro Devices, Inc.
*
* 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
*
*
*
*
*
* CS5535 documentation available from AMD.
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "os_inc.h"
#include "amd_geode.h"
#define SNDRV_GET_ID
#include
#define CARD_NAME "CS5535 Audio"
#define DRIVER_NAME "GEODE"
#define GEODEALSA_BUILD_NUM "0500"
#define DRIVER_VERSION "1.0." GEODEALSA_BUILD_NUM
MODULE_AUTHOR("Jens Altmann ");
MODULE_DESCRIPTION("Geode CS5535/6 sound");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{AMD, Geode CS5535/6}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
MODULE_PARM_DESC(index, "Index value for Geode CS5535/6 soundcard.");
MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
MODULE_PARM_DESC(id, "ID string for Geode CS5535/6 soundcard.");
MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
MODULE_PARM_DESC(enable, "Enable Geode CS5535/6 soundcard.");
static char version[] __devinitdata =
KERN_INFO "Geode ALSA: version " DRIVER_VERSION " time " __TIME__ " " __DATE__ "\n";
#define chip_t geode_t
#define BM0_IRQ 0x04
#define BM1_IRQ 0x08
#define BITS_8_TO_16(x) ( ( (long) ((unsigned char) x - 128) ) << 8 )
/*
* PCI ids
*/
#ifndef PCI_VENDOR_ID_NS
#define PCI_VENDOR_ID_NS 0x100b
#endif
#ifndef PCI_VENDOR_ID_AMD
#define PCI_VENDOR_ID_AMD 0x1022
#endif
#ifndef PCI_DEVICE_ID_NS_CS5535_AUDIO
#define PCI_DEVICE_ID_NS_CS5535_AUDIO 0x002e
#endif
#ifndef PCI_DEVICE_ID_AMD_CS5536_AUDIO
#define PCI_DEVICE_ID_AMD_CS5536_AUDIO 0x2093
#endif
static struct pci_device_id snd_amd_ids[] =
{
{PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_AMD,PCI_DEVICE_ID_AMD_CS5536_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0,},
};
PGEODEAUDIO pGeode;
MODULE_DEVICE_TABLE(pci, snd_amd_ids);
/* The actual rates supported by the card. */
static unsigned int samplerates[8] = {
8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000,
};
static snd_pcm_hw_constraint_list_t constraints_rates =
{
.count = ARRAY_SIZE(samplerates),
.list = samplerates,
.mask = 0,
};
typedef struct
{
snd_card_t* card;
struct pci_dev* pci;
unsigned long iobase;
unsigned long irq;
unsigned long ioflags;
unsigned long port;
struct resource *res_port;
snd_pcm_t *pcm;
ac97_t *ac97;
snd_pcm_substream_t* playbackSubstream;
snd_pcm_substream_t* recordSubstream;
} snd_amd_t;
/* definition of the chip-specific record */
typedef snd_amd_t geode_t;
/*prototypes*/
static int __devinit snd_geode_new_pcm(geode_t *pAmd);
static int __devinit snd_geode_mixer_new(geode_t *pAmd);
static unsigned short snd_geode_ac97_read(ac97_t *ac97, unsigned short reg);
static void snd_geode_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val);
static unsigned char snd_geode_wave_open (PGEODEAUDIO pGeode,snd_pcm_substream_t *substream);
static unsigned char snd_hw_InterruptID (PGEODEAUDIO pGeode);
static void snd_hw_ClearIRQ (PGEODEAUDIO pGeode);
static PGEODEAUDIO snd_hw_Initialize (unsigned int Irq, geode_t *pAmd);
static void snd_hw_FreePRD (PGEODEAUDIO pGeode, unsigned long Channel);
static unsigned char snd_hw_AllocatePRD (PGEODEAUDIO pGeode,
unsigned long Channel,
unsigned long DMABufferSize,
unsigned long periodSizeInBytes);
static void snd_hw_InitPRD (PGEODEAUDIO pGeode,
unsigned long Channel,
unsigned long DMAPhys,
unsigned long dmasize,
unsigned long periodSizeInBytes);
static void snd_hw_ResumeDMA (PGEODEAUDIO pGeode, unsigned long Channel);
static void snd_hw_StartDMA (PGEODEAUDIO pGeode, unsigned long Channel);
static void snd_hw_StopDMA (PGEODEAUDIO pGeode, unsigned long Channel);
static unsigned char snd_hw_ClearStat(PGEODEAUDIO pGeode, unsigned long Channel);
static unsigned short snd_hw_CodecRead ( PGEODEAUDIO pGeode, unsigned char CodecRegister);
static void snd_hw_CodecWrite( PGEODEAUDIO pGeode, unsigned char CodecRegister, unsigned short CodecData);
static unsigned char snd_hw_WaitForBit(PGEODEAUDIO pGeode,
unsigned long Offset,
unsigned long Bit,
unsigned char Operation,
unsigned long timeout,
unsigned long *pReturnValue);
static void snd_hw_SetCodecRate(PGEODEAUDIO pGeode, unsigned long Channel, unsigned long SampleRate);
static unsigned long snd_hw_InitAudioRegs(PGEODEAUDIO pGeode);
static void snd_hw_FreeAlignedDMAMemory ( PGEODEAUDIO pGeode, PALLOC_INFO pAllocInfo );
static unsigned char snd_hw_AllocateAlignedDMAMemory (PGEODEAUDIO pGeode,
unsigned long Size,
PALLOC_INFO pAllocInfo);
static void snd_hw_SetupIRQ (PGEODEAUDIO pGeode, unsigned long Irq);
static int snd_geode_playback_open(snd_pcm_substream_t *substream);
static int snd_geode_playback_close(snd_pcm_substream_t *substream);
static int snd_geode_capture_open(snd_pcm_substream_t *substream);
static int snd_geode_capture_close(snd_pcm_substream_t *substream);
static int snd_geode_pcm_hw_params(snd_pcm_substream_t *substream,
snd_pcm_hw_params_t * hw_params);
static int snd_geode_pcm_hw_free(snd_pcm_substream_t *substream);
static int snd_geode_pcm_prepare(snd_pcm_substream_t *substream);
static int snd_geode_pcm_trigger(snd_pcm_substream_t *substream,int cmd);
static snd_pcm_uframes_t snd_geode_pcm_playback_pointer(snd_pcm_substream_t *substream);
static snd_pcm_uframes_t snd_geode_pcm_capture_pointer(snd_pcm_substream_t *substream);
static int __devinit snd_amd_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id);
static void __devexit snd_amd_remove(struct pci_dev *pci);
static void snd_amd_adjust_prd_table(int channel, snd_pcm_runtime_t* pRuntime);
#ifdef CONFIG_PM
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9))
static int snd_geode_suspend(snd_card_t *card, unsigned int state);
static int snd_geode_resume(snd_card_t *card, unsigned int state);
#else
static int snd_geode_suspend(struct pci_dev *dev, u32 state);
static int snd_geode_resume(struct pci_dev *dev);
#endif
static unsigned long snd_geode_SetPowerState(PGEODEAUDIO pGeode, GEODEAUDIO_POWER_STATE NewPowerState);
static void snd_hw_ResetCodec (PGEODEAUDIO pGeode);
static unsigned char snd_hw_CheckCodecPowerBit(PGEODEAUDIO pGeode, unsigned short Bit, unsigned char Operation);
static unsigned char snd_hw_CodecFullOn (PGEODEAUDIO pGeode);
static void snd_hw_SaveAudioContext (PGEODEAUDIO pGeode);
static void snd_hw_RestoreAudioContext (PGEODEAUDIO pGeode);
#endif
#define geode_t_magic 0x15052003
/* operators */
static snd_pcm_ops_t snd_geode_playback_ops =
{
.open = snd_geode_playback_open,
.close = snd_geode_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_geode_pcm_hw_params,
.hw_free = snd_geode_pcm_hw_free,
.prepare = snd_geode_pcm_prepare,
.trigger = snd_geode_pcm_trigger,
.pointer = snd_geode_pcm_playback_pointer,
//.copy = snd_geode_pcm_playback_copy,
//.silence = snd_geode_pcm_silence,
};
/* operators */
static snd_pcm_ops_t snd_geode_capture_ops =
{
.open = snd_geode_capture_open,
.close = snd_geode_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_geode_pcm_hw_params,
.hw_free = snd_geode_pcm_hw_free,
.prepare = snd_geode_pcm_prepare,
.trigger = snd_geode_pcm_trigger,
.pointer = snd_geode_pcm_capture_pointer,
};
/* hardware definition */
static snd_pcm_hardware_t snd_geode_playback_hw =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID ),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_KNOT/*24k*/,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = DMA_PLAYBACK_BUFFERSIZE,
.period_bytes_min = 64,
.period_bytes_max = DMA_PLAYBACK_BUFFERSIZE,
.periods_min = 1,
.periods_max = 1024,
};
/* hardware definition */
static snd_pcm_hardware_t snd_geode_capture_hw =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT/*24k*/,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = DMA_CAPTURE_BUFFERSIZE,
.period_bytes_min = 64,
.period_bytes_max = DMA_CAPTURE_BUFFERSIZE,
.periods_min = 1,
.periods_max = 1024,
};
static struct pci_driver driver =
{
.name = "Geode CS5535",
.id_table = snd_amd_ids,
.probe = snd_amd_probe,
.remove = __devexit_p(snd_amd_remove),
#ifdef CONFIG_PM
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9))
SND_PCI_PM_CALLBACKS
#else
.suspend = snd_geode_suspend,
.resume = snd_geode_resume,
#endif // LINUX_VERSION_CODE
#endif //CONFIG_PM
};
/**
* \ingroup
* \brief
* Updates the PRD-table to satisfy non integer
* relations between buffersize and period size
*
*
* \param int channel
* snd_pcm_runtime_t pRuntime
* \return
*/
static void snd_amd_adjust_prd_table(int channel, snd_pcm_runtime_t* pRuntime)
{
PPRD_ENTRY pPRDTab;
int idx,maxidx,nextidx,overnextidx;
unsigned long prdaddr;
unsigned long current_ptr,periodBytes,baseaddr,rest,buffersize;
periodBytes = frames_to_bytes(pRuntime,pRuntime->period_size);
buffersize= pRuntime->dma_bytes;
if((periodBytes*pRuntime->periods) == buffersize)
{
return; /*Nothing to do - the PRD table is fix*/
}
OS_DbgMsg("-->snd_amd_adjust_prd_table\n");
baseaddr=pGeode->AudioChannel[channel].DMA_AllocInfo.PhysicalAddress;
if(pGeode->AudioChannel[channel].Running==TRUE)
{
current_ptr =OS_ReadPortULong ((unsigned short) pGeode->AudioChannel[channel].AudioBusMaster.DMAPointer)-
baseaddr;
}
else
{
current_ptr = 0;
}
pPRDTab=&pGeode->AudioChannel[channel].PRDTable[0];
maxidx=pGeode->AudioChannel[channel].PRDEntriesAllocated-2;
idx=maxidx;
while(idx)
{
prdaddr=pPRDTab[idx].ulPhysAddr-baseaddr;
if(current_ptr>=prdaddr && current_ptr<=prdaddr+0x20)
{
break;
}
idx--;
}
if(idx>maxidx)
{
idx=0;
nextidx=1;
overnextidx=2;
}
else
{
nextidx=idx+1;
if(nextidx>maxidx)
{
nextidx=0;
overnextidx=1;
}
}
overnextidx=nextidx+1;
if(overnextidx>maxidx)
{
overnextidx=0;
}
OS_DbgMsg("maxidx=%d, idx=%d, nextidx=%d ,overnextidx=%d, cur_ptr=%.4X ,buffersize=%X\n",maxidx, idx, nextidx,overnextidx,current_ptr,buffersize);
if(((pPRDTab[idx].ulPhysAddr-baseaddr)+2*periodBytes) <= buffersize)
{
pPRDTab[nextidx].ulPhysAddr = pPRDTab[idx].ulPhysAddr+(pPRDTab[idx].SizeFlags & (~PRD_EOP_BIT));
pPRDTab[nextidx].SizeFlags = periodBytes | PRD_EOP_BIT;
pPRDTab[overnextidx].ulPhysAddr=(pPRDTab[nextidx].ulPhysAddr-baseaddr) + (pPRDTab[nextidx].SizeFlags & (~PRD_EOP_BIT));
pPRDTab[overnextidx].ulPhysAddr%=buffersize;
pPRDTab[overnextidx].ulPhysAddr+=baseaddr;
rest=buffersize - (pPRDTab[overnextidx].ulPhysAddr-baseaddr);
if(rest>=periodBytes)
pPRDTab[overnextidx].SizeFlags= periodBytes | PRD_EOP_BIT;
else
pPRDTab[overnextidx].SizeFlags=rest;
}
else
{
pPRDTab[nextidx].ulPhysAddr = (pPRDTab[idx].ulPhysAddr-baseaddr)+(pPRDTab[idx].SizeFlags & (~PRD_EOP_BIT));
pPRDTab[nextidx].ulPhysAddr%=buffersize;
if((pPRDTab[idx].SizeFlags&PRD_EOP_BIT))
{
rest=(buffersize-pPRDTab[nextidx].ulPhysAddr);
if(rest>periodBytes)
{
pPRDTab[nextidx].SizeFlags = periodBytes | PRD_EOP_BIT;
}
else
{
pPRDTab[nextidx].SizeFlags = rest;
}
}
pPRDTab[nextidx].ulPhysAddr+=baseaddr;
pPRDTab[overnextidx].ulPhysAddr=(pPRDTab[nextidx].ulPhysAddr-baseaddr) + (pPRDTab[nextidx].SizeFlags & (~PRD_EOP_BIT));
pPRDTab[overnextidx].ulPhysAddr%=buffersize;
if(!(pPRDTab[nextidx].SizeFlags&PRD_EOP_BIT))
{
pPRDTab[overnextidx].SizeFlags=(periodBytes-(pPRDTab[nextidx].SizeFlags & (~PRD_EOP_BIT))) | PRD_EOP_BIT;
}
else
{
if((pPRDTab[overnextidx].ulPhysAddr+periodBytes) <= buffersize)
{
pPRDTab[overnextidx].SizeFlags=periodBytes | PRD_EOP_BIT;
}
else
{
rest=buffersize-pPRDTab[overnextidx].ulPhysAddr;
pPRDTab[overnextidx].SizeFlags=rest;
}
}
pPRDTab[overnextidx].ulPhysAddr+=baseaddr;
}
OS_DbgMsg("PRDTable:\n");
for (idx=0; idx<(pGeode->AudioChannel[channel].PRDEntriesAllocated); ++idx)
{
OS_DbgMsg("snd_hw_InitPRD[%.3X],address: %.8X, flags: %.8X\n",idx,pGeode->AudioChannel[channel].PRDTable[idx].ulPhysAddr, pGeode->AudioChannel[channel].PRDTable[idx].SizeFlags);
}
OS_DbgMsg("<--snd_amd_adjust_prd_table\n");
}
/**
* \ingroup linux GX layer
* \brief
* Called when an interrupt from the DMA controller occurs.
* The function is bound when the interrupt will be allocated.
* Notifies the upper alsa layer of finished processing of a
* sample fragment
*
* \param int irq,
* void *dev_id,
* struct pt_regs *regs
* \return IRQ_HANDLED
*/
static irqreturn_t snd_amd_interrupt(int irq, void *dev_id,struct pt_regs *regs)
{
unsigned char IntID;
geode_t *pAmd;
IntID = snd_hw_InterruptID(pGeode);
snd_hw_ClearIRQ(pGeode);
pAmd = dev_id;
if(IntID & BM0_IRQ)
{
OS_DbgMsg(">>BM0_IRQ\n");
snd_pcm_period_elapsed(pAmd->playbackSubstream);
snd_amd_adjust_prd_table(CHANNEL0_PLAYBACK,pAmd->playbackSubstream->runtime);
}
if(IntID & BM1_IRQ)
{
snd_pcm_period_elapsed(pAmd->recordSubstream);
snd_amd_adjust_prd_table(CHANNEL1_RECORD,pAmd->recordSubstream->runtime);
//OS_DbgMsg(">>BM1_IRQ\n");
}
return IRQ_HANDLED;
}
/**
* \ingroup linux GX layer
* \brief
* Called to free hardware related ressources like
* IO space, IRQ, chip specific data
*
* \param geode_t *pAmd
* \return int ; errorcode if successful=0
*/
static int snd_amd_free(geode_t *pAmd)
{
OS_DbgMsg("-->snd_amd_free\n");
/*release the i/o port*/
if (pAmd->res_port)
{
release_resource(pAmd->res_port);
kfree_nocheck(pAmd->res_port);
}
/*release the irq*/
if (pAmd->irq >= 0)
{
synchronize_irq(pAmd->irq);
free_irq(pAmd->irq, (void *)pAmd);
}
/* release the data*/
kfree(pAmd);
OS_DbgMsg("<--snd_amd_free\n");
return 0;
}
/**
* \ingroup linux GX layer
* \brief
* Callback for the drivers "dev_free" function.
* Frees port and irq allocation via call to "snd_amd_free".
*
* \param snd_device_t *device
* \return int ; errorcode if successful=0
*/
static int snd_amd_dev_free(snd_device_t *device)
{
int ret;
geode_t *pAmd;
OS_DbgMsg("-->snd_amd_dev_free\n");
pAmd = device->device_data;
ret=snd_amd_free(pAmd);
OS_DbgMsg("<--snd_amd_dev_free\n");
return(ret);
}
/**
* \ingroup linux GX layer
* \brief
* Does all hardware specific allocation and initialization
* 1. allocate memory for the HW data structure (geode_t)
* 2. allocate interrupt and IO-space
* 3. Initialize the Hardware
* This function will be called from the drivers probe callback
*
* \param snd_card_t* card,
* struct pci_dev* pci
* geode_t ** rchip)
* \return int ;errorcode if succesful=0
*/
static int __devinit snd_amd_create(snd_card_t *card,
struct pci_dev *pci,
geode_t **rchip)
{
geode_t *pAmd;
int err;
static snd_device_ops_t ops =
{
.dev_free = snd_amd_dev_free,
};
OS_DbgMsg("-->snd_amd_create\n");
*rchip = NULL;
if (pci_enable_device(pci))
{
return -EIO;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9))
/*allocate a chip-specific data with magic-alloc*/
pAmd = (geode_t *)kcalloc(1, sizeof(geode_t), GFP_KERNEL);
#else
pAmd = snd_magic_kcalloc(geode_t, 0, GFP_KERNEL);
#endif
if (pAmd == NULL)
{
OS_DbgMsg("<--snd_amd_create failed:(..calloc)\n");
return -ENOMEM;
}
card->private_data=pAmd;
pAmd->card = card;
pAmd->pci = pci;
pAmd->irq = -1;
/*(1) PCI resource allocation*/
pAmd->port = pci_resource_start(pci, 0);
if ((pAmd->res_port = request_region(pAmd->port, 8,
"My Chip")) == NULL) {
snd_amd_free(pAmd);
printk(KERN_ERR "cannot allocate the port\n");
return -EBUSY;
}
if (request_irq(pci->irq, snd_amd_interrupt,
SA_INTERRUPT|SA_SHIRQ, "My Chip",(void *)pAmd))
{
snd_amd_free(pAmd);
printk(KERN_ERR "cannot grab irq\n");
return -EBUSY;
}
pAmd->irq = pci->irq;
pAmd->iobase=pci_resource_start(pci, 0);
pAmd->ioflags=pci_resource_flags(pci, 0);
/*(2) initialization of the chip hardware*/
pGeode=snd_hw_Initialize((unsigned int) pci->irq,pAmd);
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,pAmd, &ops)) < 0)
{
snd_amd_free(pAmd);
OS_DbgMsg("<--snd_amd_create failed:(snd_device_new)\n");
return err;
}
*rchip = pAmd;
snd_card_set_dev(card, &pci->dev);
#ifdef CONFIG_PM
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9))
snd_card_set_pm_callback(card, snd_geode_suspend, snd_geode_resume, pAmd);
#endif
#endif
OS_DbgMsg("<--snd_amd_create\n");
return 0;
}
/**
* \ingroup linux GX layer
* \brief
* Called from pci_module_init immediately after loading
* the driver.Creates Mixer and PCM instance and
* registers the driver.
*
* \param struct pci_dev *pci,
* const struct pci_device_id *pci_id
* \return int= o if successful
*/
static int __devinit snd_amd_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev;
snd_card_t *card;
geode_t *pAmd;
int err;
OS_DbgMsg("-->snd_amd_probe\n");
if (dev >= SNDRV_CARDS)
{
OS_DbgMsg("<--snd_amd_probe failed:(dev >= SNDRV_CARDS)\n");
return -ENODEV;
}
if (!enable[dev])
{
dev++;
OS_DbgMsg("<--snd_amd_probe failed:(!enable[dev])\n");
return -ENOENT;
}
card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
if (card == NULL)
{
OS_DbgMsg("<--snd_amd_probe failed:(card == NULL)\n");
return -ENOMEM;
}
if ((err = snd_amd_create(card, pci, &pAmd)) < 0)
{
snd_card_free(card);
OS_DbgMsg("<--snd_amd_probe failed:(snd_amd_create)\n");
return err;
}
strcpy(card->driver, "CS553x audio");
strcpy(card->shortname, "Geode CS553x audio");
sprintf(card->longname, "%s at 0x%lx irq %i",
card->shortname, (long)pAmd->iobase, (int)pAmd->irq);
if ((err = snd_geode_new_pcm(pAmd)) < 0)
{
snd_card_free(card);
OS_DbgMsg("<--snd_amd_probe failed:(snd_geode_new_pcm)\n");
return err;
}
if ((err = snd_geode_mixer_new(pAmd)) < 0)
{
snd_card_free(card);
OS_DbgMsg("<--snd_amd_probe failed:(snd_geode_mixer_new)\n");
return err;
}
if ((err = snd_card_register(card)) < 0)
{
snd_card_free(card);
OS_DbgMsg("<--snd_amd_probe failed:(snd_card_register)\n");
return err;
}
pci_set_drvdata(pci, card);
dev++;
OS_DbgMsg("<--snd_amd_probe\n");
return 0;
}
/**
* \ingroup linux GX layer
* \brief
* Called from the module unload callback. Release all
* data allocated by the driver by calling snd_card_free
*
* \param struct pci_dev *pci
*/
static void __devexit snd_amd_remove(struct pci_dev *pci)
{
OS_DbgMsg("-->snd_amd_remove pci=%X\n",pci);
snd_card_free(pci_get_drvdata(pci));
pci_set_drvdata(pci, NULL);
OS_Free((unsigned char*)pGeode);
OS_DbgMsg("<--snd_amd_remove\n");
}
/**
* \ingroup linux GX layer
* \brief
* Callback function called before start of playback.
* Returns the hardware capabilities to the upper
* layer.
*
* \param snd_pcm_substream_t *substream
* \return always success
*/
static int snd_geode_playback_open(snd_pcm_substream_t *substream)
{
geode_t *pAmd;
OS_DbgMsg("-->snd_geode_playback_open\n");
pAmd = snd_pcm_substream_chip(substream);
pAmd->playbackSubstream=substream;
substream->runtime->hw = snd_geode_playback_hw;
snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
OS_DbgMsg("<--snd_geode_playback_open\n");
return 0;
}
/**
* \ingroup linux GX layer
* \brief
* Callback function called when playback.ends
* Does actually nothing here.
*
* \param snd_pcm_substream_t *substream
* \return always success
*/
static int snd_geode_playback_close(snd_pcm_substream_t *substream)
{
geode_t *pAmd;
OS_DbgMsg("-->snd_geode_playback_close\n");
pAmd = snd_pcm_substream_chip(substream);
pAmd->playbackSubstream=NULL;
OS_DbgMsg("<--snd_geode_playback_close\n");
return 0;
}
/**
* \ingroup linux GX layer
* \brief
* Callback function called before start of recording.
* Returns the hardware capabilities to the upper
* layer.
*
* \param snd_pcm_substream_t *substream
* \return always success
*/
static int snd_geode_capture_open(snd_pcm_substream_t *substream)
{
geode_t *pAmd;
snd_pcm_runtime_t *runtime;
OS_DbgMsg("-->snd_geode_capture_open\n");
pAmd = snd_pcm_substream_chip(substream);
pAmd->recordSubstream=substream;
runtime = substream->runtime;
runtime->hw = snd_geode_capture_hw;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
OS_DbgMsg("<--snd_geode_capture_open\n");
return 0;
}
/**
* \ingroup linux GX layer
* \brief
* Callback function called when capturing.ends
* Does actually nothing here.
*
* \param snd_pcm_substream_t *substream
* \return always success
*/
static int snd_geode_capture_close(snd_pcm_substream_t *substream)
{
geode_t *pAmd;
OS_DbgMsg("-->snd_geode_capture_close\n");
pAmd = snd_pcm_substream_chip(substream);
pAmd->recordSubstream=NULL;
OS_DbgMsg("<--snd_geode_capture_close\n");
return 0;
}
/**
* \ingroup linux GX layer
* \brief
* Callback function called before the PCM interface is used.
* Calls the allocation function for the dma page allocation
*
* \param snd_pcm_substream_t *substream
* \return always success
*/
static int snd_geode_pcm_hw_params(snd_pcm_substream_t *substream,
snd_pcm_hw_params_t * hw_params)
{
int pages;
OS_DbgMsg("-->snd_geode_pcm_hw_params\n");
pages=snd_pcm_lib_malloc_pages(substream,params_buffer_bytes(hw_params));
OS_DbgMsg("<--snd_geode_pcm_hw_params: pages=%X\n",pages);
return pages;
}
/**
* \ingroup linux GX layer
* \brief
* Callback function called before the PCM interface will be closed.
* Release the driver allocated PRD table and calls
* the fre function in the ALSA layer to release the DMA buffers
*
* \param snd_pcm_substream_t *substream
* \return return value of snd_pcm_lib_free_pages
*/
static int snd_geode_pcm_hw_free(snd_pcm_substream_t *substream)
{
int pages;
unsigned long channel;
OS_DbgMsg("-->snd_geode_pcm_hw_free\n");
pages= snd_pcm_lib_free_pages(substream);
channel=(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)?CHANNEL0_PLAYBACK:CHANNEL1_RECORD;
if(pGeode->AudioChannel[channel].fAllocated==TRUE)
{
snd_hw_FreePRD(pGeode,channel);
pGeode->AudioChannel[channel].fAllocated=FALSE;
}
OS_DbgMsg("<--snd_geode_pcm_hw_free\n");
return pages;
}
/**
* \ingroup linux GX layer
* \brief
* Callback function called before the PCM interface opened.
* Calls snd_geode_wave_open which initialize the PRD table
* and necessary codec registers.
*
* \param snd_pcm_substream_t *substream
* \return always success
*/
static int snd_geode_pcm_prepare(snd_pcm_substream_t *substream)
{
geode_t *pAmd;
OS_DbgMsg("-->snd_geode_pcm_prepare\n");
pAmd= snd_pcm_substream_chip(substream);
snd_geode_wave_open( pGeode,substream);
OS_DbgMsg("<--snd_geode_pcm_prepare\n");
return 0;
}
/**
* \ingroup linux GX layer
* \brief
* Callback function to start or stop the DMA transfer for
* record or playback
*
* \param snd_pcm_substream_t *substream
* int cmd 0=STOP,1=START
* \return 0 if successful
*/
static int snd_geode_pcm_trigger(snd_pcm_substream_t *substream,int cmd)
{
unsigned long channel;
OS_DbgMsg("-->snd_geode_pcm_trigger=%d ch=%d\n",cmd,substream->stream);
channel=(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)?CHANNEL0_PLAYBACK:CHANNEL1_RECORD;
switch (cmd)
{
case SNDRV_PCM_TRIGGER_START:
/*start the PCM engine*/
snd_hw_StartDMA (pGeode,channel);
break;
case SNDRV_PCM_TRIGGER_STOP:
/*stop the PCM engine*/
snd_hw_StopDMA (pGeode, channel);
break;
default:
OS_DbgMsg("<--snd_geode_pcm_trigger failed:(EINVAL)\n");
return -EINVAL;
}
OS_DbgMsg("<--snd_geode_pcm_trigger\n");
return 0;
}
/**
* \ingroup linux GX layer
* \brief
* This callback is called when the PCM middle layer inquires the current
* hardware position on the playback buffer.
* The position will be returned in frames ranged from 0 to (buffer_size - 1).
* For playback at sample sizes of 8Bit and/or single channel the pointer
* is adjusted to the expected range in the ALSA layer.
*
* \param snd_pcm_substream_t *substream
*
* \return snd_pcm_uframes_t
*/
static snd_pcm_uframes_t snd_geode_pcm_playback_pointer(snd_pcm_substream_t *substream)
{
unsigned int current_ptr=0;
snd_pcm_uframes_t bpf;
//OS_DbgMsg("-->snd_geode_pcm_playback_pointer\n");
/*fetch the current hardware pointer*/
current_ptr = OS_ReadPortULong ((unsigned short) pGeode->AudioChannel[CHANNEL0_PLAYBACK].AudioBusMaster.DMAPointer)-
pGeode->AudioChannel[CHANNEL0_PLAYBACK].DMA_AllocInfo.PhysicalAddress;
bpf=current_ptr;
if(substream->runtime->format <= SNDRV_PCM_FORMAT_U8)
{
bpf/=2;
}
if(substream->runtime->channels == 1)
{
bpf/=2;
}
bpf=bytes_to_frames(substream->runtime,bpf);
OS_DbgMsg("current_ptr(bytes)=%X\n",current_ptr);
OS_DbgMsg("<--snd_geode_pcm_playback_pointer(frames): bpf=%X\n",bpf);
return bpf;
}
/**
* \ingroup linux GX layer
* \brief
* This callback is called when the PCM middle layer inquires the current
* hardware position on the capture buffer.
* The position will be returned in frames ranged from 0 to (buffer_size - 1).
*
* \param snd_pcm_substream_t *substream
*
* \return snd_pcm_uframes_t
*/
static snd_pcm_uframes_t snd_geode_pcm_capture_pointer(snd_pcm_substream_t *substream)
{
unsigned int current_ptr=0;
snd_pcm_uframes_t bpf;
//OS_DbgMsg("-->snd_geode_pcm_capture_pointer\n");
/*fetch the current hardware pointer*/
current_ptr = OS_ReadPortULong ((unsigned short) pGeode->AudioChannel[CHANNEL1_RECORD].AudioBusMaster.DMAPointer)-
pGeode->AudioChannel[CHANNEL1_RECORD].DMA_AllocInfo.PhysicalAddress;
bpf=bytes_to_frames(substream->runtime,current_ptr);
OS_DbgMsg("current_ptr=%X\n",current_ptr);
OS_DbgMsg("<--snd_geode_pcm_capture_pointer: bpf=%X buffer_size = 0x%x, period_size = 0x%x\n",
bpf,substream->runtime->buffer_size, substream->runtime->period_size);
return bpf;
}
/**
* \ingroup linux GX layer
* \brief
* Called from snd_amd_probe to allocate and setup the
* AC97 Mixer structure.
*
* \param geode_t *pAmd
* \return 0 if successful
*/
static int __devinit snd_geode_mixer_new(geode_t *pAmd)
{
ac97_bus_t *pbus;
int err=0;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9))
ac97_template_t ac97;
static ac97_bus_ops_t ops = {
.write = snd_geode_ac97_write,
.read = snd_geode_ac97_read,
};
if ((err = snd_ac97_bus(pAmd->card, 0, &ops, NULL, &pbus)) < 0)
#else
ac97_t ac97;
ac97_bus_t bus;
memset(&bus, 0, sizeof(bus));
bus.write = snd_geode_ac97_write;
bus.read = snd_geode_ac97_read;
if ((err = snd_ac97_bus(pAmd->card, &bus, &pbus)) < 0)
#endif
{
return err;
}
OS_DbgMsg("-->snd_geode_mixer_new\n");
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = pAmd;
err = snd_ac97_mixer(pbus, &ac97, &pAmd->ac97);
OS_DbgMsg("<--snd_geode_mixer_new=%X\n",err);
return err;
}
/**
* \ingroup linux GX layer
* \brief
* Callback for read access to the MIXER registers
*
* \param ac97_t *ac97
* unsigned short reg
*
* \return 0 if successful
*/
static unsigned short
snd_geode_ac97_read(ac97_t *ac97, unsigned short reg)
{
geode_t *pAmd;
unsigned short ret = 0;
//OS_DbgMsg("-->snd_geode_ac97_read reg=%X\n",reg);
pAmd = ac97->private_data;
ret=snd_hw_CodecRead(pGeode, reg);
/*special handling for HP-flag*/
#ifndef SUPPORT_HEADPHONE_OUT
if(reg==RESET)
{
ret&=~(HEADHONE_AVAIL);
}
#endif
OS_DbgMsg("<--snd_geode_ac97_read= [%X]=%X\n",reg, ret);
return ret;
}
/**
* \ingroup linux GX layer
* \brief
* Callback for write access to the MIXER registers
*
* \param ac97_t *ac97
* unsigned short reg
* unsigned short val
*
* \return 0 if successful
*/
static void
snd_geode_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
{
geode_t *pAmd;
OS_DbgMsg("-->snd_geode_ac97_write reg=%x, data=%X\n",reg,val);
pAmd = ac97->private_data;
if (reg==MASTER_VOLUME)
{ /*handle overflow*/
if(val & 0x6000)
{
val= 0x1F00 | (val & 0x80FF);
}
if(val & 0x0060)
{
val= 0x001F | (val & 0xFF00);
}
snd_hw_CodecWrite(pGeode,reg,val);
snd_hw_CodecWrite(pGeode,LINE_LEV_OUT_VOL, val);
}
else if(reg>=MIC_VOLUME && reg<=PCM_OUT_VOL)
{
if(val & 0x2000)
{
val&=0x81FF;
val|=0x1F00;
}
if(val & 0x0020)
{
val&=0xFF1F;
val|=0x001F;
}
if((val&0x1F1F)==0x1F1F)
{ //mute if set to 0
val |=MUTE_MASK;
}
snd_hw_CodecWrite(pGeode,reg,val);
}
else if(reg==PCM_OUT_VOL)
{
if((val&0x1F1F)==0x1F1F)
{ //mute if set to 0
val |=MUTE_MASK;
}
snd_hw_CodecWrite(pGeode,reg,val);
}
else if(reg==MIC_VOLUME)
{
if((val&0x001F)==0x001F)
{ //mute if set to 0
val |=MUTE_MASK;
}
snd_hw_CodecWrite(pGeode,reg,val);
}
else
{
snd_hw_CodecWrite(pGeode,reg,val);
}
//OS_DbgMsg("<--snd_geode_ac97_write\n");
}
/**
* \ingroup linux GX layer
* \brief
* Called when the driver has been loaded fronm the .probe callback
* function. Allocate the memory for the DMA transfer.
* The DMA buffer for playback is 4-times the actually used
* size by the ALSA layer. This is necessary because we have always to
* expand all samples to 2channels/16Bit values.
*
* \param geode_t *pAmd
* \return int 0 on success
*/
static int __devinit snd_geode_new_pcm(geode_t *pAmd)
{
snd_pcm_t *pcm;
int err;
OS_DbgMsg("-->snd_geode_new_pcm\n");
if ((err = snd_pcm_new(pAmd->card, "My Chip", 0, 1, 1,&pcm)) < 0)
{
OS_DbgMsg("<--snd_geode_new_pcm failed:()\n");
return err;
}
pcm->private_data = pAmd;
strcpy(pcm->name, "Geode");
pAmd->pcm = pcm;
/* set operators ,depend on sapmle size it might be changed later*/
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_geode_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_geode_capture_ops);
/* pre-allocation of buffers */
err= snd_pcm_lib_preallocate_pages(pcm->streams[0].substream, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(pAmd->pci), DMA_PLAYBACK_BUFFERSIZE, DMA_PLAYBACK_BUFFERSIZE);
if(err!=0)
{
OS_DbgMsg("<--snd_geode_new_pcm failed: cannot allocate DMA mem for playback\n");
return err;
}
err= snd_pcm_lib_preallocate_pages(pcm->streams[1].substream, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(pAmd->pci), DMA_CAPTURE_BUFFERSIZE, DMA_CAPTURE_BUFFERSIZE);
if(err!=0)
{
OS_DbgMsg("<--snd_geode_new_pcm failed: cannot allocate DMA mem for capture\n");
return err;
}
OS_DbgMsg("<--snd_geode_new_pcm\n");
return 0;
}
/**
* \ingroup linux GX layer
* \brief
* Callback function from the module_init procedure.
* Calls pci_module_init which registers the driver
* in the system.
*
* \return int; error code, if OK then 0
*/
static int __init alsa_card_geode_init(void)
{
int err;
printk(version);
OS_DbgMsg("-->alsa_card_geode_init\n");
if ((err = pci_module_init(&driver)) < 0) {
#ifdef MODULE
OS_DbgMsg(KERN_ERR "<--Geode CS5535 soundcard not found or device busy\n");
#endif
return err;
}
OS_DbgMsg("<--alsa_card_geode_init\n");
return 0;
}
/**
* \ingroup linux GX layer
* \brief
* Callback function for the moduleexit procedure
* Called when the driver will be unloaded.
*
*/
static void __exit alsa_card_geode_exit(void)
{
OS_DbgMsg("-->alsa_card_geode_exit\n");
pci_unregister_driver(&driver);
OS_DbgMsg("<--alsa_card_geode_exit\n");
printk("Geode ALSA: unloaded\n");
}
/**
* \ingroup linux GX layer
* \brief
* Called from the pcm_prepare callback
* when the device will be opened for playback or recording.
* The function calculates the layout for the PRD table and
* configures the HW and all internal variables.
*
* \param PGEODEAUDIO pGeode,
* snd_pcm_substream_t *substream
*
* \return TRUE on success
*
*/
static unsigned char snd_geode_wave_open (PGEODEAUDIO pGeode,snd_pcm_substream_t *substream)
{
unsigned long InterruptFrequencyInDMABytes;
unsigned long channel;
unsigned long SampleRate;
unsigned char nChannels;
unsigned char BitsPerSample;
unsigned long InterruptFrequency;
unsigned long DMABufferSize;
unsigned char DirectDMA;
unsigned long physDmaAddr,virtDmaAddr;
snd_pcm_runtime_t *pRuntime;
channel=(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? CHANNEL0_PLAYBACK : CHANNEL1_RECORD;
pRuntime=substream->runtime;
SampleRate=pRuntime->rate;
nChannels=pRuntime->channels;
BitsPerSample=pRuntime->sample_bits;
InterruptFrequency=pRuntime->period_size;
DirectDMA=pRuntime->status->state;
physDmaAddr=pRuntime->dma_addr;
virtDmaAddr=(unsigned long)pRuntime->dma_area;
OS_DbgMsg("-->snd_geode_wave_open:\n");
OS_DbgMsg("Channel =%d\n",channel);
OS_DbgMsg("SampleRate =%d\n",SampleRate);
OS_DbgMsg("nChannels =%d\n",nChannels);
OS_DbgMsg("BitsPerSample =%d\n",BitsPerSample);
OS_DbgMsg("InterruptFrequency=0x%X\n",InterruptFrequency);
OS_DbgMsg("pPysDmaAddr =0x%X\n",physDmaAddr);
OS_DbgMsg("pVirtDmaAddr =0x%X\n",virtDmaAddr);
OS_DbgMsg("pRuntime->dma_bytes (bytes) =0x%X\n",pRuntime->dma_bytes);
OS_DbgMsg("pRuntime->buffer_size (bytes) =0x%X\n",pRuntime->buffer_size);
OS_DbgMsg("pRuntime->period_size (frames) =0x%X\n",pRuntime->period_size);
OS_DbgMsg("pRuntime->periods =%d\n",pRuntime->periods);
OS_DbgMsg("substream->dma_max=0x%X\n",substream->dma_max);
/*Clear the Audio buffer*/
memset(pRuntime->dma_area,0,substream->dma_max);
/*Save the format*/
pGeode->AudioChannel[channel].IndirectDMA.SampleRate = SampleRate;
pGeode->AudioChannel[channel].IndirectDMA.nChannels = nChannels;
pGeode->AudioChannel[channel].IndirectDMA.BitsPerSample = BitsPerSample;
/*Set the data pointers to 0*/
pGeode->AudioChannel[channel].fDirectDMA = DirectDMA;
if ((BitsPerSample==0) || (nChannels==0))
{
OS_DbgMsg(":snd_geode_wave_open: Invalid BitsPerSample or nChannels\n");
return FALSE;
}
DMABufferSize=pRuntime->dma_bytes;
OS_DbgMsg("used DMABufferSize by Geode driver =0x%X\n",DMABufferSize);
pGeode->AudioChannel[channel].DMA_AllocInfo.PhysicalAddress=physDmaAddr;
pGeode->AudioChannel[channel].DMA_AllocInfo.VirtualAddress=(unsigned char*)virtDmaAddr;
InterruptFrequencyInDMABytes = frames_to_bytes(pRuntime,pRuntime->period_size);
/*Allocate PRD */
if(pGeode->AudioChannel[channel].fAllocated == TRUE)
{
snd_hw_FreePRD (pGeode,channel);
}
if(!snd_hw_AllocatePRD(pGeode, channel, DMABufferSize,InterruptFrequencyInDMABytes))
{
OS_DbgMsg("<--snd_geode_wave_open: failed AllocatePRD\n");
return FALSE;
}
pGeode->AudioChannel[channel].fAllocated = TRUE;
snd_hw_InitPRD (pGeode, channel, pGeode->AudioChannel[channel].DMA_AllocInfo.PhysicalAddress,DMABufferSize,InterruptFrequencyInDMABytes);
snd_hw_SetCodecRate(pGeode, channel, SampleRate);
OS_DbgMsg("<--snd_geode_wave_open:\n");
return TRUE;
}
/**
* \ingroup linux GX layer
* \brief
* This function returns the current Interrupt
* from the DMA busmaster
* 0x04 - DMA0 interrupt (Wave Out)
* 0x08 - DMA1 interrupt (Wave In)
*
* \param PGEODEAUDIO pGeode
* \return unsigned char interrupt ID
*/
static unsigned char snd_hw_InterruptID (PGEODEAUDIO pGeode)
{
volatile unsigned char *TempInterruptID, ID;
if (pGeode->fCS553x)
{
if (pGeode->fIOAccess)
{
ID=(unsigned char) OS_ReadPortUShort((unsigned short) (pGeode->F3BAR0 + 0x12));
}
else
{
TempInterruptID=(unsigned char *) (pGeode->F3BAR0 + 0x12);
ID=*TempInterruptID;
}
}
return (ID);
}
/**
* \ingroup linux GX layer
* \brief
* Clear the IRQ in hardware
*
* \param PGEODEAUDIO pGeode
*/
static void snd_hw_ClearIRQ (PGEODEAUDIO pGeode)
{
if (pGeode->fCS553x)
{
snd_hw_ClearStat(pGeode, CHANNEL0_PLAYBACK);
snd_hw_ClearStat(pGeode, CHANNEL1_RECORD);
}
}
/**
* \ingroup linux GX layer
* \brief
* Initializes the 5535 hardware.
* It has to be called with the IRQ assigned by the OS
* Reset and initialize the CODEC
*
* \param unsigned int interrupt ID
* \return PGEODEAUDIO ;pointer to the HW structure
*/
static PGEODEAUDIO snd_hw_Initialize (unsigned int Irq, geode_t *pAmd)
{
PGEODEAUDIO pGeode;
OS_DbgMsg("-->snd_hw_Initialize\n");
pGeode=(PGEODEAUDIO) OS_AllocateMemory(sizeof(GEODEAUDIO));
if (pGeode==NULL)
{
OS_DbgMsg("<--snd_hw_Initialize: failed\n");
return FALSE;
}
memset (pGeode, 0, sizeof(GEODEAUDIO));
if (pAmd->ioflags & IORESOURCE_IO)
{
pGeode->fIOAccess=TRUE;
pGeode->F3BAR0 = pAmd->iobase;
}
else
{
pGeode->fIOAccess=FALSE;
pGeode->F3BAR0=OS_Get_F3BAR0_Virt(pAmd->iobase);
}
if(!snd_hw_InitAudioRegs(pGeode))
{
OS_DbgMsg("<--snd_hw_Initialize: failed\n");
return FALSE;
}
/*------------------------------------------------------------
Interrupt-Related code
------------------------------------------------------------*/
/*As a default, we assume we are using DirectDMA*/
pGeode->AudioChannel[CHANNEL0_PLAYBACK].fDirectDMA = TRUE;
pGeode->AudioChannel[CHANNEL1_RECORD].fDirectDMA = TRUE;
snd_hw_SetupIRQ (pGeode, (unsigned long) Irq);
/* Save the selected IRQ*/
pGeode->SelectedIRQ=Irq;
/* Prepare the bit mask (To clear Interrupts)*/
pGeode->uIRQMask=1;
pGeode->uIRQMask <<= pGeode->SelectedIRQ;
pGeode->uIRQMask=~pGeode->uIRQMask;
/*------------------------------------------------------------
End of Interrupt-Related code
------------------------------------------------------------*/
pGeode->AudioChannel[CHANNEL0_PLAYBACK].fAllocated = FALSE;
pGeode->AudioChannel[CHANNEL1_RECORD].fAllocated = FALSE;
OS_DbgMsg("<--snd_hw_Initialize\n");
return pGeode;
}
/**
* \ingroup linux GX layer
* \brief
* Frees the PRD allocated by snd_hw_AllocatePRD
*
* \param unsigned long Channel ,the channel(0,1)
* PGEODEAUDIO pGeode
*/
static void snd_hw_FreePRD (PGEODEAUDIO pGeode, unsigned long Channel)
{
OS_DbgMsg("-->snd_hw_FreePRD\n");
snd_hw_FreeAlignedDMAMemory (pGeode, &pGeode->AudioChannel[Channel].PRD_AllocInfo);
pGeode->AudioChannel[Channel].PRDTable = NULL;
OS_DbgMsg("<--snd_hw_FreePRD\n");
}
/**
* \ingroup linux GX layer
* \brief
* Setup the PRD entries of the specified channel with EOP to generate
* interrupts based on the Sample rate and the InterruptFrequency
* value expressed in Miliseconds.
*
* \param PGEODEAUDIO pGeode,
* unsigned long Channel,
* unsigned long SampleRate,
* unsigned long InterruptFrequency,
* unsigned char FrequencyType
*
*/
/**
* \ingroup linux GX layer
* \brief
* Allocates the PRD table for a DMA buffer of DMABufferSize bytes for the specified Channel
*
*
* \param PGEODEAUDIO pGeode, ;hw structure
unsigned long Channel, ;0=playback,1=Capture
unsigned long DMABufferSize ;size of the DMA buffer
* \return unsigned char ;TRUE if successful else FALSE
*/
static unsigned char snd_hw_AllocatePRD (PGEODEAUDIO pGeode,
unsigned long Channel,
unsigned long DMABufferSize,
unsigned long periodSizeInBytes)
{
unsigned long PRDsToAllocate;
OS_DbgMsg("-->snd_hw_AllocatePRD\n");
PRDsToAllocate=(DMABufferSize/periodSizeInBytes+1);
if(DMABufferSize%periodSizeInBytes)
{
PRDsToAllocate++;
}
if(!snd_hw_AllocateAlignedDMAMemory(pGeode,
(PRDsToAllocate * sizeof(PRD_ENTRY)),
&pGeode->AudioChannel[Channel].PRD_AllocInfo))
{
OS_DbgMsg("Could not allocate PRD\n");
return FALSE;
}
pGeode->AudioChannel[Channel].PRDTable = (PPRD_ENTRY) pGeode->AudioChannel[Channel].PRD_AllocInfo.VirtualAddress;
pGeode->AudioChannel[Channel].DMA_AllocInfo.Size = DMABufferSize;
pGeode->AudioChannel[Channel].PRDEntriesAllocated = PRDsToAllocate;
OS_DbgMsg("<--snd_hw_AllocatePRD\n");
return TRUE;
}
/**
* \ingroup linux GX layer
* \brief
* Starts the DMA transfer for the channel specified by Channel.
*
*
* \param PGEODEAUDIO pGeode, ;hw structure
* unsigned long Channel, ;0=playback,1=Capture
*/
static void snd_hw_StartDMA (PGEODEAUDIO pGeode, unsigned long Channel)
{
if(pGeode->AudioChannel[Channel].Running)
return;
if (pGeode->fIOAccess)
{
/*Sets the PRD physical address*/
OS_WritePortULong ( (unsigned short) pGeode->AudioChannel[Channel].AudioBusMaster.PRDTableAddress,
pGeode->AudioChannel[Channel].PRD_AllocInfo.PhysicalAddress);
}
else
{
/*Sets the PRD physical address*/
*((unsigned long *) pGeode->AudioChannel[Channel].AudioBusMaster.PRDTableAddress) = pGeode->AudioChannel[Channel].PRD_AllocInfo.PhysicalAddress;
}
//OS_DbgMsg("snd_hw_StartDMA: *(%.8X)=%.8X\n",pGeode->AudioChannel[Channel].AudioBusMaster.PRDTableAddress,pGeode->AudioChannel[Channel].PRD_AllocInfo.PhysicalAddress);
snd_hw_ResumeDMA (pGeode, Channel);
pGeode->AudioChannel[Channel].Running=TRUE;
}
/**
* \ingroup linux GX layer
* \brief
* Resumes the DMA channel controlled by the specified PRD table
*
*
* \param PGEODEAUDIO pGeode, ;hw structure
* unsigned long Channel, ;0=playback,1=Capture
*/
static void snd_hw_ResumeDMA (PGEODEAUDIO pGeode, unsigned long Channel)
{
OS_DbgMsg("-->snd_hw_ResumeDMA\n");
if (pGeode->fIOAccess)
{
OS_WritePortUChar ( (unsigned short) pGeode->AudioChannel[Channel].AudioBusMaster.CommandRegister,
(unsigned char) (((pGeode->AudioChannel[Channel].AudioBusMaster.DirectionBit) & 0xFC) | ENABLE_BUSMASTER));
}
else
{
*((unsigned char *) pGeode->AudioChannel[Channel].AudioBusMaster.CommandRegister) = pGeode->AudioChannel[Channel].AudioBusMaster.DirectionBit | ENABLE_BUSMASTER;
}
OS_DbgMsg("<--snd_hw_ResumeDMA: *(%.8X)=%.8X\n",pGeode->AudioChannel[Channel].AudioBusMaster.CommandRegister,(pGeode->AudioChannel[Channel].AudioBusMaster.DirectionBit & 0xFC) | ENABLE_BUSMASTER);
}
/**
* \ingroup linux GX layer
* \brief
* Stops the DMA channel controlled by the specified PRD table.
*
*
* \param PGEODEAUDIO pGeode, ;hw structure
* unsigned long Channel, ;0=playback,1=Capture
*/
static void snd_hw_StopDMA (PGEODEAUDIO pGeode, unsigned long Channel)
{
/*if DMA is not running, don't do anything and return.*/
OS_DbgMsg("-->snd_hw_StopDMA\n");
if (!pGeode->AudioChannel[Channel].Running)
{
return;
}
if (pGeode->fCS553x)
{
if (pGeode->fIOAccess)
{
/*Turn OFF DMA*/
OS_WritePortUChar ( (unsigned short) pGeode->AudioChannel[Channel].AudioBusMaster.CommandRegister, STOP_BUSMASTER);
}
else
{
/*Turn OFF DMA */
*((unsigned char *) pGeode->AudioChannel[Channel].AudioBusMaster.CommandRegister) = STOP_BUSMASTER;
}
}
pGeode->AudioChannel[Channel].Running=FALSE;
OS_DbgMsg("<--snd_hw_StopDMA\n");
}
/**
* \ingroup linux GX layer
* \brief
* Clear the status bit of the respective Channel
*
*
* \param PGEODEAUDIO pGeode, ;hw structure
* unsigned long Channel, ;0=playback,1=Capture
* \return unsigned char
*/
static unsigned char snd_hw_ClearStat(PGEODEAUDIO pGeode, unsigned long Channel)
{
volatile unsigned char status; /*Volatile to force read-to-clear.*/
/*Read to clear*/
if (pGeode->fIOAccess)
{
status = OS_ReadPortUChar ((unsigned short) pGeode->AudioChannel[Channel].AudioBusMaster.SMI_StatusRegister);
}
else
{
status = *((unsigned char *) pGeode->AudioChannel[Channel].AudioBusMaster.SMI_StatusRegister);
}
return status;
}
/**
* \ingroup linux GX layer
* \brief
* Reads the specified codec register
*
*
* \param PGEODEAUDIO pGeode, ;hw structure
* unsigned char CodecRegister
* \return unsigned short ;the read value
*/
static unsigned short snd_hw_CodecRead ( PGEODEAUDIO pGeode, unsigned char CodecRegister )
{
unsigned long CodecRegister_data = 0;
unsigned long timeout=10;
volatile unsigned long val=0;
CodecRegister_data = ((unsigned long)CodecRegister)<<24;
CodecRegister_data |= 0x80000000; /* High-bit set (p.106) is a CODEC reg READ.*/
if (pGeode->fCS553x)
{
/* Set the bit. We are going to access the CODEC...*/
CodecRegister_data |= BIT_5535_CODEC_COMMAND_NEW;
/*Request the data*/
OS_WritePortULong((unsigned short) (pGeode->F3BAR0 + CODEC_CONTROL_REG_5535), CodecRegister_data);
/* Now we need to wait for BIT_5535_CODEC_COMMAND_NEW of the Codec control register to clear
(For subsequent Reads/Writes)*/
if (!snd_hw_WaitForBit (pGeode, CODEC_CONTROL_REG_5535, BIT_5535_CODEC_COMMAND_NEW, CLEAR, 500, NULL))
{
OS_DbgMsg("BIT_5535_CODEC_COMMAND_NEW did not clear!!\n");
}
/* Wait for CODEC_STATUS_NEW and confirm the read of the requested register*/
timeout = 50;
do
{
if (pGeode->fIOAccess)
val = OS_ReadPortULong((unsigned short) (pGeode->F3BAR0 + CODEC_STATUS_REG_5535));
else
val = *((unsigned long *)( pGeode->F3BAR0 + CODEC_STATUS_REG_5535 ));
if ((val & BIT_5535_CODEC_STATUS_NEW) && ((unsigned long) CodecRegister == ((0xFF000000 & val)>>24)))
{
break;
}
else
{
/*Wait for 10 miliseconds and try again*/
OS_Sleep(10);
}
} while ( --timeout);
if (!timeout)
{
OS_DbgMsg("Could not read the CODEC!! Returning what we got.\n");
}
}
return( (unsigned short)val );
}
/* \ingroup linux GX layer
* \brief
* Wait for completion of a clear or set of a bit mask in a codec register
*
*
* \param PGEODEAUDIO pGeode,
unsigned long Offset, ; register in which the bit is located
unsigned long Bit, ; bitmask
unsigned char Operation, ; /CLEAR or SET
unsigned long timeout, ;timeout after which the operation will be cancelled
unsigned long *pReturnValue ;the read bit value
*
* \return unsigned char ;success state TURE/FALSE
*/
static unsigned char snd_hw_WaitForBit(PGEODEAUDIO pGeode,
unsigned long Offset,
unsigned long Bit,
unsigned char Operation,
unsigned long timeout,
unsigned long *pReturnValue)
{
volatile unsigned long Temp;
if (pGeode->fIOAccess)
{
Temp = OS_ReadPortULong((unsigned short) (pGeode->F3BAR0 + Offset));
}
else
{
Temp = *((unsigned long *)( pGeode->F3BAR0 + Offset ));
}
while (timeout)
{
if (Operation==CLEAR)
{
if (!(Temp & Bit))
break;
}
else
{
if (Temp & Bit)
break;
}
/*If the Bit is not clear yet, we wait for 1 milisecond and try again*/
OS_Sleep(1);
if (pGeode->fIOAccess)
{
Temp = OS_ReadPortULong((unsigned short) (pGeode->F3BAR0 + Offset));
}
else
{
Temp = *((unsigned long *)( pGeode->F3BAR0 + Offset ));
}
timeout--;
};
if (pReturnValue)
{
*pReturnValue=Temp;
}
if (!timeout)
{
return FALSE;
}
return TRUE;
}
/**
* \ingroup linux GX layer
* \brief
* Writes data to the CODEC
*
*
* \param PGEODEAUDIO pGeode, ;hw structure
* unsigned char CodecRegister
* \return unsigned short ;CodecData
*/
static void snd_hw_CodecWrite( PGEODEAUDIO pGeode, unsigned char CodecRegister, unsigned short CodecData )
{
unsigned long CodecRegister_data;
unsigned long Temp, timeout;
CodecRegister_data = ((unsigned long) CodecRegister)<<24;
CodecRegister_data |= (unsigned long) CodecData;
CodecRegister_data &= CODEC_COMMAND_MASK;
if (pGeode->fCS553x)
{
/*Set the bit. We are going to access the CODEC...*/
CodecRegister_data |= BIT_5535_CODEC_COMMAND_NEW;
/*Write the data*/
OS_WritePortULong((unsigned short) (pGeode->F3BAR0 + CODEC_CONTROL_REG_5535), CodecRegister_data);
//OS_DbgMsg("Writing: %08X\n", CodecRegister_data);
/*We need to wait for bit16 of the Codec control register to clear*/
Temp = OS_ReadPortULong((unsigned short) (pGeode->F3BAR0 + CODEC_CONTROL_REG_5535));
//OS_DbgMsg("Temp: %08X\n", Temp);
timeout = 50;
while ((Temp & BIT_5535_CODEC_COMMAND_NEW) && timeout-- )
{
Temp = OS_ReadPortULong((unsigned short) (pGeode->F3BAR0 + CODEC_CONTROL_REG_5535));
}
if (!timeout)
{
OS_DbgMsg("Could not Write the CODEC!! BIT_5535_CODEC_COMMAND_NEW did not clear!\n");
}
}
}
/**
* \ingroup linux GX layer
* \brief
* Initializes the PRD for the specified channel
*
*
* \param PGEODEAUDIO pGeode, ;hw structure
* unsigned long Channel ;channel 0,1
* unsigned long DMAPhys ;physical address of DMA buffer
*/
static void snd_hw_InitPRD (PGEODEAUDIO pGeode,
unsigned long Channel,
unsigned long DMAPhys,
unsigned long dmasize,
unsigned long InterruptFrequencyInDMABytes)
{
//
// Fill the PRD Entries with the right sizes and flags=0
//
unsigned int i,rest;
unsigned long enddAddr;
OS_DbgMsg("-->snd_hw_InitPRD\n");
enddAddr=DMAPhys+dmasize;
OS_DbgMsg("dmasize=0x%X, enddAddr=0x%X\n",dmasize,enddAddr);
rest=dmasize%InterruptFrequencyInDMABytes;
OS_DbgMsg("rest=%X\n",rest);
for (i=0; i<(pGeode->AudioChannel[Channel].PRDEntriesAllocated-1); ++i)
{
pGeode->AudioChannel[Channel].PRDTable[i].ulPhysAddr = DMAPhys + (i * InterruptFrequencyInDMABytes);
if((pGeode->AudioChannel[Channel].PRDTable[i].ulPhysAddr+InterruptFrequencyInDMABytes)<=enddAddr)
{
pGeode->AudioChannel[Channel].PRDTable[i].SizeFlags = InterruptFrequencyInDMABytes | PRD_EOP_BIT;
}
else
{
pGeode->AudioChannel[Channel].PRDTable[i].SizeFlags =rest;
OS_DbgMsg("adjust len of last prd entry to %X,\n",pGeode->AudioChannel[Channel].PRDTable[i].SizeFlags);
}
}
/* And, last, put the JMP to the top*/
pGeode->AudioChannel[Channel].PRDTable[pGeode->AudioChannel[Channel].PRDEntriesAllocated-1].ulPhysAddr = pGeode->AudioChannel[Channel].PRD_AllocInfo.PhysicalAddress;
pGeode->AudioChannel[Channel].PRDTable[pGeode->AudioChannel[Channel].PRDEntriesAllocated-1].SizeFlags = PRD_JMP_BIT;
OS_DbgMsg("PRDTable:\n");
for (i=0; i<(pGeode->AudioChannel[Channel].PRDEntriesAllocated); ++i)
{
OS_DbgMsg("snd_hw_InitPRD[%.3X],address: %.8X, flags: %.8X\n",i,pGeode->AudioChannel[Channel].PRDTable[i].ulPhysAddr, pGeode->AudioChannel[Channel].PRDTable[i].SizeFlags);
}
OS_DbgMsg("<--snd_hw_InitPRD\n");
}
/**
* \ingroup linux GX layer
* \brief
* Set the rate in the codec for the given channel
*
*
* \param PGEODEAUDIO pGeode, ;hw structure
* unsigned long Channel ;channel 0,1
* unsigned long SampleRate ;sample rate e.g. 44100
*/
static void snd_hw_SetCodecRate(PGEODEAUDIO pGeode, unsigned long Channel, unsigned long SampleRate)
{
unsigned short val;
OS_DbgMsg ("Rate: %d\n", SampleRate);
pGeode->AudioChannel[Channel].SampleRate=SampleRate;
/*If Double-Rate is supported (Bit 2 on register 28h)...*/
val=snd_hw_CodecRead(pGeode, EXTENDED_AUDIO_ID);
if (val & 0x02)
{
OS_DbgMsg ("Codec supports Double rate.\n");
val=snd_hw_CodecRead(pGeode, EXT_AUDIO_CTRL_STAT);
if (SampleRate>48000)
{
snd_hw_CodecWrite(pGeode, EXT_AUDIO_CTRL_STAT, (unsigned short) (val|0x0002));
SampleRate/=2;
}
else
snd_hw_CodecWrite(pGeode, EXT_AUDIO_CTRL_STAT, (unsigned short) (val&0xFFFD));
}
if (pGeode->fAD1819A)
{
OS_DbgMsg ("AD1819...\n");
if (Channel)
snd_hw_CodecWrite(pGeode, AD1819A_PCM_SR1,(unsigned short) SampleRate);
else
snd_hw_CodecWrite(pGeode, AD1819A_PCM_SR0,(unsigned short) SampleRate);
}
else
{
if (Channel)
snd_hw_CodecWrite(pGeode, PCM_LR_ADC_RATE, (unsigned short) SampleRate);
else
snd_hw_CodecWrite(pGeode, PCM_FRONT_DAC_RATE,(unsigned short) SampleRate);
}
}
/**
* \ingroup linux GX layer
* \brief
* Initializes the codec registers to it's default
* values after loading the driver
*
* \param PGEODEAUDIO pGeode, ;hw structure
*
* \return unsigned long ;
*/
static unsigned long snd_hw_InitAudioRegs(PGEODEAUDIO pGeode)
{
OS_DbgMsg("-->snd_hw_InitAudioRegs\n");
pGeode->fCS553x=TRUE;
/*Initialize the Audio BusMaster register pointers*/
OS_DbgMsg("snd_hw_InitAudioRegs: Audio device base address=0x%.8X\n", pGeode->F3BAR0);
pGeode->AudioChannel[CHANNEL0_PLAYBACK].AudioBusMaster.CommandRegister = pGeode->F3BAR0+0x20;
pGeode->AudioChannel[CHANNEL0_PLAYBACK].AudioBusMaster.PRDTableAddress = pGeode->F3BAR0+0x24;
pGeode->AudioChannel[CHANNEL0_PLAYBACK].AudioBusMaster.DMAPointer = pGeode->F3BAR0+0x60;
pGeode->AudioChannel[CHANNEL0_PLAYBACK].AudioBusMaster.DirectionBit = PCI_READS;
pGeode->AudioChannel[CHANNEL0_PLAYBACK].Running=FALSE;
pGeode->AudioChannel[CHANNEL1_RECORD].AudioBusMaster.CommandRegister = pGeode->F3BAR0+0x28;
pGeode->AudioChannel[CHANNEL1_RECORD].AudioBusMaster.PRDTableAddress = pGeode->F3BAR0+0x2C;
pGeode->AudioChannel[CHANNEL1_RECORD].AudioBusMaster.DMAPointer = pGeode->F3BAR0+0x64;
pGeode->AudioChannel[CHANNEL1_RECORD].AudioBusMaster.DirectionBit = PCI_WRITES;
pGeode->AudioChannel[CHANNEL1_RECORD].Running=FALSE;
/*-----------------------------------------------------------------------------------------------
Interrupt-Related code
-----------------------------------------------------------------------------------------------*/
pGeode->IRQControlRegister = pGeode->F3BAR0+0x1C;
pGeode->InternalIRQEnableRegister = pGeode->F3BAR0+0x1A;
pGeode->AudioChannel[CHANNEL0_PLAYBACK].AudioBusMaster.SMI_StatusRegister = pGeode->F3BAR0+0x21;
pGeode->AudioChannel[CHANNEL1_RECORD].AudioBusMaster.SMI_StatusRegister = pGeode->F3BAR0+0x29;
/*-----------------------------------------------------------------------------------------------
End of Interrupt-Related code
-----------------------------------------------------------------------------------------------*/
/*CODEC - RESET and volumes initalization.*/
if (pGeode->fCS553x)
{
/*Set the Warm RESET and CODEC_COMMAND_NEW bits.*/
OS_WritePortULong ( (unsigned short) (pGeode->F3BAR0 + CODEC_CONTROL_REG_5535), 0x00030000 );
if (!snd_hw_WaitForBit (pGeode, CODEC_STATUS_REG_5535, BIT_CODEC_READY, SET, 400, NULL))
{
OS_DbgMsg("Primary Codec NOT Ready...Aborting\n");
return FALSE;
}
}
/*Check which codec is being used */
if (snd_hw_CodecRead(pGeode, AD1819A_VENDORID1) == 0x4144 &&
snd_hw_CodecRead(pGeode, AD1819A_VENDORID2) == 0x5303)
{
pGeode->fAD1819A = TRUE;
/* Enable non-48kHz sample rates. */
snd_hw_CodecWrite (pGeode, AD1819A_SER_CONF, (unsigned short) (snd_hw_CodecRead(pGeode, AD1819A_SER_CONF>>8) | AD1819A_SER_CONF_DRQEN));
}
else
{
pGeode->fAD1819A = FALSE;
snd_hw_CodecWrite (pGeode, EXT_AUDIO_CTRL_STAT, (unsigned short) (snd_hw_CodecRead(pGeode,EXT_AUDIO_CTRL_STAT) | 0x0001));
/* set the VRA bit to ON*/
}
/* set default volume*/
snd_hw_CodecWrite(pGeode, MASTER_VOLUME, 0x0F0F);
snd_hw_CodecWrite(pGeode, PCM_OUT_VOL, 0x0F0F);
snd_hw_CodecWrite(pGeode, PC_BEEP_VOLUME, 0x0000);
snd_hw_CodecWrite(pGeode, PHONE_VOLUME, 0x8000);
snd_hw_CodecWrite(pGeode, MIC_VOLUME, 0x8048);
snd_hw_CodecWrite(pGeode, LINE_IN_VOLUME, 0x0808);
snd_hw_CodecWrite(pGeode, CD_VOLUME, 0x8000);
snd_hw_CodecWrite(pGeode, VIDEO_VOLUME, 0x8000);
snd_hw_CodecWrite(pGeode, TV_VOLUME, 0x8000);
snd_hw_CodecWrite(pGeode, RECORD_SELECT, 0x0000);
snd_hw_CodecWrite(pGeode, RECORD_GAIN, 0x0a0a);
snd_hw_CodecWrite(pGeode, GENERAL_PURPOSE, 0x0200);
snd_hw_CodecWrite(pGeode, MASTER_VOLUME_MONO, 0x0000);
/*Set all the power state bits to 0 (Reg 26h)*/
snd_hw_CodecWrite (pGeode, POWERDOWN_CTRL_STAT, 0x0000);
pGeode->CurrentPowerState=GEODEAUDIO_D0;
OS_DbgMsg("<--snd_hw_InitAudioRegs\n");
return TRUE;
}
/**
* \ingroup linux GX layer
* \brief
* Programs the IRQ
*
*
* \param PGEODEAUDIO pGeode, ;hw structure
* unsigned long Irq
*/
static void snd_hw_SetupIRQ (PGEODEAUDIO pGeode, unsigned long Irq)
{
if (!pGeode->fCS553x)
{
unsigned long SetIRQData;
/*If it is a CS5530, we have to configure VSA...*/
/* VSA2 IRQ config method*/
OS_WritePortUShort( 0xAC1C, 0xFC53 ); /*Unlock virtual registers*/
OS_WritePortUShort( 0xAC1C, 0x108 ); /*ID the audio config virtual regs*/
OS_WritePortUShort( 0xAC1E, (unsigned short) Irq ); /*Set the value*/
/*VSA1 IRQ config method*/
/*Get the Irq and OR it with the command*/
SetIRQData = ((Irq<<16) | 0xA00A);
OS_WritePortULong((unsigned short) PCI_CADDR, 0x800090D0); /*Set the address*/
OS_WritePortULong((unsigned short) PCI_CDATA, SetIRQData); /*Send the command*/
/*Set the InterruptID address for the CS5530*/
pGeode->pInterruptID = (unsigned char *) OS_VirtualAddress(0x000004F0);
}
}
/**
* \ingroup linux GX layer
* \brief
* Returns the allocated memory from the PRD table
*
*
* \param PGEODEAUDIO pGeode - Pointer to the DURAUDIO Structure
* PALLOC_INFO pAllocInfo - Pointer to a ALLOC_INFO strcuture that contains the information
* for the space allocated (Physical, Virtual and Size).
*
*/
static void snd_hw_FreeAlignedDMAMemory ( PGEODEAUDIO pGeode, PALLOC_INFO pAllocInfo )
{
if(pAllocInfo->VirtualAddress)
{
OS_FreeDMAMemory ( pGeode->pAdapterObject, (void *) pAllocInfo->OriginalVirtualAddress, pAllocInfo->OriginalPhysicalAddress, pAllocInfo->OriginalSize);
pAllocInfo->VirtualAddress = NULL;
}
}
/**
* \ingroup linux GX layer
* \brief
* Allocates a 32-bytes aligned DMA memory block.
* Our hardware needs to have DMA and PRD spaces 32-bytes aligned to work correctly.
*
* 1. We save the original Physical and virtual addresses in the AllocInfo structure
* to be able to release the memory afterwards.
*
* 2. We allocate 256 extra bytes to allow the shift.
*
* 3. If the physical adress is not aligned, keep adding to the physical
* and virtual address until it aligns to 32-byte boundary.
*
*
* \param PGEODEAUDIO pGeode - Pointer to the DURAUDIO Structure
* unsigned long Size, - size of memory buffer to allocate
* PALLOC_INFO pAllocInfo - Pointer to a ALLOC_INFO strcuture that contains the information
* for the space allocated (Physical, Virtual and Size).
* \return unsigned char FALSE if failed to allocate, TRUE if succeeds
*
*/
static unsigned char snd_hw_AllocateAlignedDMAMemory (PGEODEAUDIO pGeode,
unsigned long Size,
PALLOC_INFO pAllocInfo)
{
pAllocInfo->Size = Size;
pAllocInfo->OriginalSize = Size + 256;
pAllocInfo->OriginalVirtualAddress = (void *) OS_AllocateDMAMemory ( pGeode->pAdapterObject, pAllocInfo->OriginalSize, &pAllocInfo->OriginalPhysicalAddress);
if (!pAllocInfo->OriginalVirtualAddress)
{
OS_DbgMsg("Could not allocate DMA Memory. Size:%d\n", Size);
return FALSE;
}
pAllocInfo->VirtualAddress = pAllocInfo->OriginalVirtualAddress;
pAllocInfo->PhysicalAddress = pAllocInfo->OriginalPhysicalAddress;
if(pAllocInfo->PhysicalAddress & 0x0000001F)
{
OS_DbgMsg("DMA Memory is not 32-byte aligned! : 0x%08X\n", pAllocInfo->PhysicalAddress);
while (pAllocInfo->PhysicalAddress & 0x0000001F)
{
pAllocInfo->VirtualAddress++;
pAllocInfo->PhysicalAddress++;
}
OS_DbgMsg("Fixed to: 0x%08X.\n", pAllocInfo->PhysicalAddress);
}
return TRUE;
}
#ifdef CONFIG_PM
/**
* \ingroup linux GX layer
* \brief
* Callback when the system goes to suspend. Set the hardware
* in powersave mode. The set State is always D3!
*
* \param struct pci_dev *dev,
* u32 state
* \return always success
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9))
static int snd_geode_suspend(snd_card_t *card, unsigned int state)
{
geode_t *pAmd;
OS_DbgMsg("-->snd_geode_suspend(>=2.6.9),state=%d\n",state);
pAmd = card->private_data;
#else
static int snd_geode_suspend(struct pci_dev *dev, u32 state)
{
geode_t *pAmd;
snd_card_t *card;
OS_DbgMsg("-->snd_geode_suspend(<2.6.9),state=%d\n",state);
card = pci_get_drvdata(dev);
pAmd= card->private_data;
#endif
if(pGeode->CurrentPowerState != GEODEAUDIO_D0)
{
OS_DbgMsg("<--snd_geode_suspend - already in power down!\n");
return 0;
}
snd_pcm_suspend_all(pAmd->pcm);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9))
snd_ac97_suspend(pAmd->ac97);
#endif
snd_geode_SetPowerState(pGeode, GEODEAUDIO_D3);
pci_disable_device(pAmd->pci);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
OS_DbgMsg("<--snd_geode_suspend\n");
return 0;
}
/**
* \ingroup linux GX layer
* \brief
* Callback when the system returns from suspend. Set the hardware
* in active mode.
*
* \param struct pci_dev *dev,
*
* \return always success
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9))
static int snd_geode_resume(snd_card_t *card, unsigned int state)
{
geode_t *pAmd;
OS_DbgMsg("-->snd_geode_resume(>=2,6,9),state=%d\n",state);
pAmd = card->private_data;
#else
static int snd_geode_resume(struct pci_dev *dev)
{
geode_t *pAmd;
snd_card_t *card;
OS_DbgMsg("-->snd_geode_resume(<2,6,9)\n");
card = pci_get_drvdata(dev);
pAmd= card->private_data;
#endif
if(pci_enable_device(pAmd->pci))
{
OS_DbgMsg("<--snd_geode_resume failed\n");
return (-EIO);
}
pci_set_master(pAmd->pci);
if(snd_geode_SetPowerState(pGeode, GEODEAUDIO_D0))
{
OS_DbgMsg("<--snd_geode_resume failed\n");
return (-EIO);
}
snd_ac97_resume(pAmd->ac97);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
OS_DbgMsg("<--snd_geode_resume\n");
return 0;
}
/**
* \ingroup linux GX layer
* \brief
* Function to set the Hardware into the desired power state.
*
* \param struct pci_dev *dev,
*
* \return 0 on success
*/
static unsigned long snd_geode_SetPowerState(PGEODEAUDIO pGeode, GEODEAUDIO_POWER_STATE NewPowerState)
{
if (NewPowerState==pGeode->CurrentPowerState)
return 0;
OS_DbgMsg("-->snd_geode_SetPowerState, state=%d\n",NewPowerState);
switch (NewPowerState)
{
case GEODEAUDIO_D0:
{
switch (pGeode->CurrentPowerState)
{
case GEODEAUDIO_D0:
break;
case GEODEAUDIO_D1:
{
OS_DbgMsg("Coming back from D1.\n");
if (pGeode->fCS553x)
{
/*We are coming back from D1 on a 5535
so, we need to do a AC-Link Warm Reset*/
OS_WritePortULong ( (unsigned short) (pGeode->F3BAR0 + CODEC_CONTROL_REG_5535), BIT_5535_ACLINK_WARM_RESET );
if (!snd_hw_WaitForBit (pGeode, CODEC_STATUS_REG_5535, BIT_CODEC_READY, SET, 400, NULL))
{
OS_DbgMsg("Primary Codec NOT Ready...Aborting\n");
return (-EINVAL);
}
/*Reset the codec*/
snd_hw_ResetCodec (pGeode);
OS_Sleep (50);
}
if (!snd_hw_CodecFullOn (pGeode))
{
OS_DbgMsg("ERROR: CODEC did not power up!\n");
OS_DbgMsg("<--snd_geode_SetPowerState failed\n");
return (-EINVAL);
}
break;
}
case GEODEAUDIO_D2:
case GEODEAUDIO_D3:
{
OS_DbgMsg("Coming back from D2/D3.\n");
if (pGeode->fCS553x)
{
/*We are coming back from D2 or D3 on a 5535
so, we need to do a AC-Link Warm Reset*/
OS_DbgMsg("AC-Link Warm Reset\n");
OS_WritePortULong ( (unsigned short) (pGeode->F3BAR0 + CODEC_CONTROL_REG_5535), BIT_5535_ACLINK_WARM_RESET );
if (!snd_hw_WaitForBit (pGeode, CODEC_STATUS_REG_5535, BIT_CODEC_READY, SET, 400, NULL))
{
OS_DbgMsg("Primary Codec NOT Ready...Aborting\n");
return (-EINVAL);
}
/*Reset the codec*/
snd_hw_ResetCodec(pGeode);
OS_Sleep (50);
}
if (!snd_hw_CodecFullOn (pGeode))
{
OS_DbgMsg("ERROR: CODEC did not power up!\n");
OS_DbgMsg("<--snd_geode_SetPowerState failed\n");
return (-EINVAL);
}
/*If we are coming from D2, D3 or D4, we need to
restore the controller and codec data.*/
snd_hw_RestoreAudioContext (pGeode);
break;
}
case GEODEAUDIO_D4:
{
OS_DbgMsg("Coming back from D4.\n");
/*If we are coming from D4 we have to initialize everything again
because we would be coming back from hibernation or total shutdown*/
snd_hw_SetupIRQ (pGeode, pGeode->SelectedIRQ);
snd_hw_InitAudioRegs (pGeode);
snd_hw_RestoreAudioContext (pGeode);
break;
}
}
pGeode->CurrentPowerState = GEODEAUDIO_D0;
break;
}
case GEODEAUDIO_D1:
{
OS_DbgMsg("In D1.\n");
if ((pGeode->AudioChannel[CHANNEL0_PLAYBACK].Running == FALSE) &&
(pGeode->AudioChannel[CHANNEL1_RECORD].Running == FALSE))
{
if (pGeode->fCS553x)
{
/*Powerdown ADC*/
snd_hw_CodecWrite (pGeode, POWERDOWN_CTRL_STAT, GEODEAUDIO_PWR_PR0);
if (!snd_hw_CheckCodecPowerBit (pGeode, GEODEAUDIO_CODEC_POWER_ADC, CLEAR))
{
OS_DbgMsg("ERROR: CODEC ADC bit not cleared!\n");
OS_DbgMsg("<--snd_geode_SetPowerState failed\n");
return (-EINVAL);
}
/*Powerdown DAC*/
snd_hw_CodecWrite (pGeode, POWERDOWN_CTRL_STAT, GEODEAUDIO_PWR_DIGOFF);
if (!snd_hw_CheckCodecPowerBit (pGeode, GEODEAUDIO_CODEC_POWER_DAC, CLEAR))
{
OS_DbgMsg("ERROR: CODEC DAC bit not cleared!\n");
OS_DbgMsg("<--snd_geode_SetPowerState failed\n");
return (-EINVAL);
}
/*Powerdown Analog*/
snd_hw_CodecWrite (pGeode, POWERDOWN_CTRL_STAT, GEODEAUDIO_PWR_DIGOFF|GEODEAUDIO_PWR_ANLOFF);
/*5535 allows to powerdown AC-link
PLUS Powerdown AC_Link
Powerdown Amp. causes unusual "pop" sound. This issue
will be further investigated in Castle. Currently we don't
power down amplifier in D1. */
snd_hw_CodecWrite (pGeode, POWERDOWN_CTRL_STAT, GEODEAUDIO_PWR_D1_HAWK);
/*Set the AC-Link ShutDown bit*/
OS_WritePortULong((unsigned short) (pGeode->F3BAR0 + CODEC_CONTROL_REG_5535), BIT_5535_ACLINK_SHUTDOWN);
}
else
{
/*Power down ADC/DAC and ANL sections in GX1/SCxx00
Amp. powerdown causes "pop" sound
ADC/DAC powerdown only causes loud static sound, but no static sound
is heard when ANL and ADC/DAC power down simultaneously*/
snd_hw_CodecWrite (pGeode, POWERDOWN_CTRL_STAT, GEODEAUDIO_PWR_DIGOFF | GEODEAUDIO_PWR_ANLOFF);
}
/*We went to D1 */
pGeode->CurrentPowerState = GEODEAUDIO_D1;
}
else
{
/*If we are playing, we don't do anything and stay in D0*/
pGeode->CurrentPowerState = GEODEAUDIO_D0;
}
break;
}
case GEODEAUDIO_D2:
case GEODEAUDIO_D3:
case GEODEAUDIO_D4:
{
OS_DbgMsg("In D2/D3/D4.\n");
/*We are going into D2, D3 or D4.
In this mode the power could be removed or reduced to a point
where the AC97 controller looses information,
so we must save the codec registers.*/
snd_hw_SaveAudioContext(pGeode);
/*Powerdown CODEC*/
snd_hw_CodecWrite (pGeode, POWERDOWN_CTRL_STAT, GEODEAUDIO_PWR_D4);
if (pGeode->fCS553x)
{
/*Set the AC-Link ShutDown bit*/
OS_WritePortULong((unsigned short) (pGeode->F3BAR0 + CODEC_CONTROL_REG_5535), BIT_5535_ACLINK_SHUTDOWN);
}
pGeode->CurrentPowerState = NewPowerState;
break;
}
}
OS_DbgMsg("<--snd_geode_SetPowerState\n");
return 0;
}
/**
* \ingroup linux GX layer
* \brief
* Access for reset to the Codeccontrol register
*
* \param PGEODEAUDIO pGeode
*
*/
static void snd_hw_ResetCodec (PGEODEAUDIO pGeode)
{
OS_DbgMsg("-->snd_hw_ResetCodec\n");
/*Reset codec*/
if (pGeode->fCS553x)
{
OS_WritePortULong ( (unsigned short) (pGeode->F3BAR0 + CODEC_CONTROL_REG_5535), 0L );
}
else
{
*((unsigned long *)( pGeode->F3BAR0 + CODEC_CMD_REG )) = 0L;
}
OS_DbgMsg("<--snd_hw_ResetCodec\n");
}
/**
* \ingroup linux GX layer
* \brief
* Test the actual power state of the codec against
* the value of "Bit"
*
* \param PGEODEAUDIO pGeode
* unsigned short Bit
* unsigned char Operation
*
*
*/
static unsigned char snd_hw_CheckCodecPowerBit(PGEODEAUDIO pGeode, unsigned short Bit, unsigned char Operation)
{
unsigned short CodecData, CodecTimeOut;
CodecTimeOut = 10;
do
{
/*Read the power management register.*/
CodecData=snd_hw_CodecRead (pGeode, POWERDOWN_CTRL_STAT);
/*Check the REF status. Should be ready.*/
if (Operation == CLEAR)
{
if (!(CodecData & Bit))
break;
else
CodecTimeOut--;
}
else
{
if (CodecData & Bit)
break;
else
CodecTimeOut--;
}
/*Let's wait a little, 10ms and then try again.*/
OS_Sleep(10);
} while (CodecTimeOut);
/*Check if we timed out.*/
if (CodecTimeOut == 0)
{
return FALSE;
}
return TRUE;
}
/**
* \ingroup linux GX layer
* \brief
* Power up the codec in 5 steps.
*
*
* \param PGEODEAUDIO pGeode
* \return TRUE on success
*/
static unsigned char snd_hw_CodecFullOn (PGEODEAUDIO pGeode)
{
snd_hw_CodecRead (pGeode, POWERDOWN_CTRL_STAT);
/*Clear EAPD,PR6 and AC-link to power up external and HP amp and Digital interface*/
snd_hw_CodecWrite (pGeode, POWERDOWN_CTRL_STAT, GEODEAUDIO_PWRUP_STEP1);
/*Clear PR3 to power up Analog (Vref off)*/
snd_hw_CodecWrite (pGeode, POWERDOWN_CTRL_STAT, GEODEAUDIO_PWRUP_STEP2);
if (!snd_hw_CheckCodecPowerBit (pGeode, GEODEAUDIO_CODEC_POWER_REF, SET))
{
OS_DbgMsg("REF timed out. CoDec not powered up.\n");
return FALSE;
}
/*A loud "pop" sound is heard without sufficient delay.
It means the REF ready bit being set doesn't reflect
the real reference voltage status when this bit is being checked.
It is codec issue. Adding approximate 1 second delay is
the current workaround.*/
OS_Sleep(1200);
/*Clear PR2 to power up Analog (Vref on)*/
snd_hw_CodecWrite (pGeode, POWERDOWN_CTRL_STAT, GEODEAUDIO_PWRUP_STEP3);
if (!snd_hw_CheckCodecPowerBit (pGeode, GEODEAUDIO_CODEC_POWER_ANL, SET))
{
OS_DbgMsg("ANL timed out. CoDec not powered up.\n");
return FALSE;
}
/*Clear PR1 to power up DAC*/
snd_hw_CodecWrite (pGeode, POWERDOWN_CTRL_STAT, GEODEAUDIO_PWRUP_STEP4);
if (!snd_hw_CheckCodecPowerBit (pGeode, GEODEAUDIO_CODEC_POWER_DAC, SET))
{
OS_DbgMsg("DAC timed out. CoDec not powered up.\n");
return FALSE;
}
/*Clear PR0 to power up ADC*/
snd_hw_CodecWrite (pGeode, POWERDOWN_CTRL_STAT, GEODEAUDIO_PWRUP_STEP5);
if (!snd_hw_CheckCodecPowerBit (pGeode, GEODEAUDIO_CODEC_POWER_ADC, SET))
{
OS_DbgMsg("ADC timed out. CoDec not powered up.\n");
return FALSE;
}
return TRUE;
}
/**
* \ingroup linux GX layer
* \brief
* Stores the content of all necessary codec registers before
* the codec goes to powersave.
*
* \param PGEODEAUDIO pGeode
*
*/
static void snd_hw_SaveAudioContext (PGEODEAUDIO pGeode)
{
unsigned char i, RegIndex = 0;
unsigned long Channel;
unsigned long lTemp;
unsigned short sTemp;
/*Check if DMA is running for each channel.
If it is, save PRD table address and turn it OFF*/
for (Channel=0;ChannelAudioChannel[Channel].Running==TRUE)
{
/*Save Current PRD address */
if (pGeode->fCS553x)
{
pGeode->AudioBusMaster_PRDTableAddress[Channel] = OS_ReadPortULong ((unsigned short) (pGeode->F3BAR0 + 0x24 + (Channel*0x08)));
/*Stop DMA*/
snd_hw_StopDMA (pGeode, Channel);
}
else
{
pGeode->AudioBusMaster_PRDTableAddress[Channel] = *((unsigned long *) (pGeode->F3BAR0+0x24 + (Channel*0x08)));
/*Stop DMA*/
snd_hw_StopDMA (pGeode, Channel);
}
pGeode->AudioChannel[Channel].Running=TRUE;
}
}
/*Save Mixer volumes and settings*/
for (i=0x02; i<=0x1E; i+=2)
{
pGeode->CODECRegisters[RegIndex++] = snd_hw_CodecRead(pGeode, i);
}
/*Save Extended registers (DAC/ADC rates etc...)*/
for (i=0x28; i<=0x38; i+=2)
{
pGeode->CODECRegisters[RegIndex++] = snd_hw_CodecRead(pGeode, i);
}
/*Save F3BAR0 registers */
if (pGeode->fCS553x)
{
for (i=0x00; i<0x7F; ++i)
{
if ( (i < 0x0F) || ((i & 0x0F) == 0x04) || ((i & 0x0F) == 0x0C) || (i >= 0x5C))
{
/*Save 32-bit registers*/
lTemp = OS_ReadPortULong ( (unsigned short) (pGeode->F3BAR0 + i));
pGeode->F3BARSave[i] = (unsigned char) (lTemp & 0x000000FF);
pGeode->F3BARSave[i+1] = (unsigned char) ((lTemp & 0x0000FF00) >> 8);
pGeode->F3BARSave[i+2] = (unsigned char) ((lTemp & 0x00FF0000) >> 16);
pGeode->F3BARSave[i+3] = (unsigned char) ((lTemp & 0xFF000000) >> 24);
OS_DbgMsg("Saved: Offset [%02Xh]: %08X\n", i, lTemp);
i+=3;
}
else
{
if (i == 0x12)
{
/*Save 16bit registers*/
sTemp = OS_ReadPortUShort ( (unsigned short) (pGeode->F3BAR0 + i));
pGeode->F3BARSave[i] = (unsigned char) (sTemp & 0x00FF);
pGeode->F3BARSave[i+1] = (unsigned char) ((sTemp & 0xFF00) >> 8);
OS_DbgMsg("Saved: Offset [%02Xh]: %04X\n", i, sTemp);
i +=1;
}
else
{
/*Save 8bit registers*/
pGeode->F3BARSave[i] = OS_ReadPortUChar ( (unsigned short) (pGeode->F3BAR0 + i));
/*Make sure the bus master is disabled. We found the keyclick
activities were coming in the middle of this saving function.
So, the DMA was running again after checking the running flag
by disabling the bus master. The Bus Master register was saved
as it was (running). It caused the issue of keyclicks repeated
rapidly after resume due to restoring the bus master register
in the running state. Zero out the enable bits in the bus master
register here solved the keyclick issue in S2R. */
if (((i & 0x0F )== 0x00) || ((i & 0x0F) == 0x08))
{
pGeode->F3BARSave[i] &= 0xFC;
}
OS_DbgMsg("Saved: Offset [%02Xh]: %02X\n", i, pGeode->F3BARSave[i]);
}
}
}
}
else
{
memcpy ( pGeode->F3BARSave, (unsigned long *) pGeode->F3BAR0, 0x50 );
pGeode->F3BARSave[20] &= 0xFE; /*Disable Bus Master again*/
}
}
/**
* \ingroup linux GX layer
* \brief
* Restores the content of all necessary codec registers
* after awake from powersave. USing this funktion requires
* calling snd_hw_SaveAudioContext before
*
* \param PGEODEAUDIO pGeode
*
*/
static void snd_hw_RestoreAudioContext (PGEODEAUDIO pGeode)
{
unsigned char i, RegIndex = 0;
unsigned long Channel;
unsigned long ulTemp, ulTemp1, ulTemp2, ulTemp3, ulTemp4;
unsigned short usTemp;
OS_DbgMsg ("Restoring Context.\n");
/*Restore F3BAR0 registers */
if (pGeode->fCS553x)
{
for (i=0x00; i<0x7F; ++i)
{
if ( (i < 0x0F) || ((i & 0x0F) == 0x04) || ((i & 0x0F) == 0x0C) || (i >= 0x5C))
{
/*Restore 32-bit registers*/
ulTemp1 = ((unsigned long) pGeode->F3BARSave[i]);
ulTemp2 = (((unsigned long) pGeode->F3BARSave[i+1]) << 8);
ulTemp3 = (((unsigned long) pGeode->F3BARSave[i+2]) << 16);
ulTemp4 = (((unsigned long) pGeode->F3BARSave[i+3]) << 24);
ulTemp = ulTemp1 | ulTemp2 | ulTemp3 | ulTemp4;
OS_DbgMsg("Restored: Offset [%02Xh]: %08X\n", i, ulTemp);
OS_WritePortULong ( (unsigned short) (pGeode->F3BAR0 + i), ulTemp);
i += 3;
}
else
{
if (i == 0x12)
{
/*Restore 16bit registers*/
usTemp = ((unsigned short) (pGeode->F3BARSave[i]) ||
(unsigned short) (pGeode->F3BARSave[i+1] << 8));
OS_DbgMsg("Restored: Offset [%02Xh]: %04X\n", i, usTemp);
OS_WritePortUShort ( (unsigned short) (pGeode->F3BAR0 + i), usTemp);
i += 1;
}
else
{
/*Save 8bit registers*/
OS_DbgMsg("Restored: Offset [%02Xh]: %02X\n", i, pGeode->F3BARSave[i]);
OS_WritePortUChar ( (unsigned short) (pGeode->F3BAR0 + i), pGeode->F3BARSave[i]);
}
}
}
}
else
{
memcpy ( (unsigned long *) pGeode->F3BAR0, pGeode->F3BARSave, 0x50 );
}
/*Restore Mixer volumes and settings*/
for (i=0x02; i<=0x1E; i+=2)
{
snd_hw_CodecWrite(pGeode, i, pGeode->CODECRegisters[RegIndex++]);
}
/*Restore Extended registers (DAC/ADC rates etc...)*/
for (i=0x28; i<=0x38; i+=2)
{
snd_hw_CodecWrite(pGeode, i, pGeode->CODECRegisters[RegIndex++]);
}
/*Check, for each channel, if DMA was running before suspend.
If it was, turn it back ON.*/
for (Channel=0;ChannelAudioChannel[Channel].Running==TRUE)
{
/*Put back the PRD pointer*/
if (pGeode->fCS553x)
{
OS_WritePortULong ((unsigned short) (pGeode->F3BAR0 + 0x24 + (Channel*0x08)), pGeode->AudioBusMaster_PRDTableAddress[Channel]);
}
else
{
*(unsigned long *) (pGeode->F3BAR0 + 0x24 + (Channel*0x08)) = pGeode->AudioBusMaster_PRDTableAddress[Channel];
}
/*Start DMA*/
snd_hw_ResumeDMA (pGeode, Channel);
}
}
}
#endif
module_init(alsa_card_geode_init)
module_exit(alsa_card_geode_exit)
Audio_LinuxALSA_5536_1.00.0500/amd_geode.h 0000444 0113737 0000121 00000035425 10247160736 017656 0 ustar background apache /*
*
* Copyright (c) Advanced Micro Devices, Inc.
*
* 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
*
*
*
*
*
* CS5535 documentation available from AMD.
*
*/
#ifndef __AMDGEODE_H__
#define __AMDGEODE_H__
#define TRUE 1
#define FALSE 0
#define POLLING 0xFF
#define SET 1
#define CLEAR 0
typedef enum {
WAPI_IN = 0,
WAPI_OUT
} WAPI_INOUT, *PWAPI_INOUT;
#define NUMBER_OF_CHANNELS 2
#define DMA_PLAYBACK_BUFFERSIZE (128*1024)
#define DMA_CAPTURE_BUFFERSIZE (64*1024)
//
// Device and Vendor IDs - Needed like this for OSS only
//
#define CYRIX_VENDOR_ID 0x1078
#define NATIONAL_VENDOR_ID 0x100B
//
// Audio Device IDs
//
#define CX5530_DEV_ID 0x0103
#define SC1200_DEV_ID 0x0503
#define CS5535_DEV_ID 0x002E
//
// PCI Config address.
// Bits 15..11 specify the PCI device.
// Bits 7..2 specify reg in PCI device.
//
#define PCI_CADDR 0x0CF8
//
// PCI Config data reg.
// After PCI_ADDR set, this reg contains
// the corresponding data (the dev reg).
//
#define PCI_CDATA 0x0CFC
//
// Function 3 of 5530 PCI dev is Audio (ISA idx).
//
#define PCI_FUNC3_AUDIO 0x300
#define PCI_AUDIO_CMD_REG 0x04
typedef unsigned char AUDIO_STATE;
#define AUDIO_STATE_IGNORE 0
#define AUDIO_STATE_IN_RECORDING 0x01
#define AUDIO_STATE_IN_OVERFLOW 0x02
#define AUDIO_STATE_IN_STOPPED 0x03
#define AUDIO_STATE_IN_MASK 0x0F
#define AUDIO_STATE_OUT_PLAYING 0x10
#define AUDIO_STATE_OUT_UNDERFLOW 0x20
#define AUDIO_STATE_OUT_STOPPED 0x30
#define AUDIO_STATE_OUT_MASK 0xF0
#define RECORD_RUNNING 0x01
#define RECORD_OVERFLOW 0x02
#define RECORD_STOPPED 0x03
#define PLAYBACK_RUNNING 0x10
#define PLAYBACK_UNDERFLOW 0x20
#define PLAYBACK_STOPPED 0x30
//-----------------------------------------------------------
// BEGIN Interrupt-Related code
//-----------------------------------------------------------
//
// Interrupt IDs
//
#define DMA0_INTERRUPT 0x04
#define DMA1_INTERRUPT 0x08
#define DMA2_INTERRUPT 0x10
#define DMA3_INTERRUPT 0x20
#define DMA4_INTERRUPT 0x40
#define DMA5_INTERRUPT 0x80
//-----------------------------------------------------------
// END Interrupt-Related code
//-----------------------------------------------------------
//
// Bit conversions
//
#define BITS_8_TO_16(x) ( ( (long) ((unsigned char) x - 128) ) << 8 )
#define BITS_16_TO_8(x) ( ( (unsigned char) ((long) x >> 8 ) ) + 128 )
//
// The CODEC commands are actually 16-bit words, into which is inserted
// the codec "target" register, identified by a byte. The 5530 Codec
// controller writes a command unsigned short of 32-bits, that includes the codec
// command unsigned short.
//
#define CODEC_COMMAND_MASK 0xFF00FFFF
//
// The Interaction with the CODEC is a bit cumbersome
// because of the serial interface.
//
#define CODEC_STATUS_REG 0x08 // In Audio mem-map.
#define CODEC_CMD_REG 0x0c // In audio mem-map.
#define CODEC_CMD_VALID 0x00010000
#define CODEC_STATUS_VALID 0x00020000
#define CODEC_STATUS_NEW 0x00010000
#define BIT_CODEC_READY 0x00800000
//
// Registers for the 5535
//
#define CODEC_STATUS_REG_5535 0x08
#define CODEC_CONTROL_REG_5535 0x0c
//
// 5535 Bits
//
#define BIT_5535_CODEC_COMMAND_NEW 0x00010000
#define BIT_5535_CODEC_STATUS_NEW 0x00020000
#define BIT_5535_ACLINK_SHUTDOWN 0x00040000
#define BIT_5535_ACLINK_WARM_RESET 0x00020000
#define BIT_5535_CODEC_READY_PRIM 0x00800000
//
// Codec register indexes. Note these are all shifted left by 16 bits.
//
#define RESET 0x00
#define MASTER_VOLUME 0x02
#define LINE_LEV_OUT_VOL 0x04
#define MASTER_VOLUME_MONO 0x06
#define MASTER_TONE_RL 0x08
#define PC_BEEP_VOLUME 0x0a
#define PHONE_VOLUME 0x0c
#define MIC_VOLUME 0x0e
#define LINE_IN_VOLUME 0x10
#define CD_VOLUME 0x12
#define VIDEO_VOLUME 0x14
#define TV_VOLUME 0x16
#define PCM_OUT_VOL 0x18
#define RECORD_SELECT 0x1a
#define RECORD_GAIN 0x1c
#define RECORD_MIC_GAIN 0x1e
#define GENERAL_PURPOSE 0x20
#define CONTROL_3D 0x22
#define MODEM_RATE 0x24
#define POWERDOWN_CTRL_STAT 0x26
#define EXTENDED_AUDIO_ID 0x28
#define EXT_AUDIO_CTRL_STAT 0x2A
#define PCM_FRONT_DAC_RATE 0x2C
#define PCM_LR_ADC_RATE 0x32
#define VENDOR_ID1 0x7c
#define VENDOR_ID2 0x7e
#define MUTE_MASK 0x8000
#define HEADHONE_AVAIL 0x0010
#define LINE_LEV_RESET_VOL 0x0000 // the reset without the mask
// NOTE: The ATTEN_CTL_BITS default is only 5 because some CODECs are
// not compliant with the 2.1 spec. The value 5 is safe for all Geode
// reference platforms. For platforms that support 6 bit attenuation control
// uncomment the following line:
//#define AC97_2DOT1_6BIT_COMPLIANT
#ifdef AC97_2DOT1_6BIT_COMPLIANT
# define MASTER_ATTEN_CTL_BITS 6
#else
# define MASTER_ATTEN_CTL_BITS 5
#endif
#define MASTER_VOLUME_MAX ( ( 1 << MASTER_ATTEN_CTL_BITS ) - 1 )
#define LINE_LEV_OUT_MAX ( ( 1 << MASTER_ATTEN_CTL_BITS ) - 1 )
//
// AD1819A registers
//
#define AD1819A_SER_CONF 0x74
#define AD1819A_SER_CONF_DRQEN 0x08
#define AD1819A_MISC 0x76
#define AD1819A_PCM_SR0 0x78
#define AD1819A_PCM_SR1 0x7A
#define AD1819A_VENDORID1 0x7C
#define AD1819A_VENDORID2 0x7E
#define CHANNEL0_PLAYBACK 0
#define CHANNEL1_RECORD 1
#define MAX_CHANNELS 2
//
// Interval types
//
#define INTERVAL_BYTES 0x01
#define INTERVAL_MILLISECONDS 0x02
//
// Power Management bits
//
#define GEODEAUDIO_PWR_PR0 0x0100 // PCM in ADC's & input Mux Powerdown
#define GEODEAUDIO_PWR_PR1 0x0200 // PCM out DACs Powerdown
#define GEODEAUDIO_PWR_PR2 0x0400 // Analog Mixer powerdown (Vref still on)
#define GEODEAUDIO_PWR_PR3 0x0800 // Analog Mxer powerdown (Vref off)
#define GEODEAUDIO_PWR_PR4 0x1000 // Digital interface (AC-link) powerdown (external clk off)
#define GEODEAUDIO_PWR_PR5 0x2000 // Internal Clk disable
#define GEODEAUDIO_PWR_PR6 0x4000 // HP amp powerdown
#define GEODEAUDIO_PWR_PR7 0x8000 // External Amplifier Power Down
#define GEODEAUDIO_PWR_D0 0x0000
#define GEODEAUDIO_PWR_D1 GEODEAUDIO_PWR_EXTOFF
#define GEODEAUDIO_PWR_D2 GEODEAUDIO_PWR_PR0|GEODEAUDIO_PWR_PR1|GEODEAUDIO_PWR_PR2|GEODEAUDIO_PWR_PR6|GEODEAUDIO_PWR_PR7
#define GEODEAUDIO_PWR_D3 GEODEAUDIO_PWR_PR0|GEODEAUDIO_PWR_PR1|GEODEAUDIO_PWR_PR2|GEODEAUDIO_PWR_PR6|GEODEAUDIO_PWR_PR7
#define GEODEAUDIO_PWR_D4 GEODEAUDIO_PWR_PR0|GEODEAUDIO_PWR_PR1|GEODEAUDIO_PWR_PR2|GEODEAUDIO_PWR_PR3|GEODEAUDIO_PWR_PR4|GEODEAUDIO_PWR_PR5|GEODEAUDIO_PWR_PR6|GEODEAUDIO_PWR_PR7
#define GEODEAUDIO_PWR_ANLOFF GEODEAUDIO_PWR_PR2|GEODEAUDIO_PWR_PR3 // Analog section OFF
#define GEODEAUDIO_PWR_EXTOFF GEODEAUDIO_PWR_PR6|GEODEAUDIO_PWR_PR7 // HP amp and External Amplifier OFF
#define GEODEAUDIO_PWR_D1_HAWK GEODEAUDIO_PWR_PR0|GEODEAUDIO_PWR_PR1|GEODEAUDIO_PWR_PR2|GEODEAUDIO_PWR_PR3|GEODEAUDIO_PWR_PR4
#define GEODEAUDIO_PWR_DIGOFF GEODEAUDIO_PWR_PR0|GEODEAUDIO_PWR_PR1 // Digital section OFF
#define GEODEAUDIO_PWRUP_STEP1 0x0F00 // Clear EAPD,PR6 and AC-link to power up external and HP amp and Digital interface
#define GEODEAUDIO_PWRUP_STEP2 0x0700 // Clear PR3 to power up Analog (Vref off)
#define GEODEAUDIO_PWRUP_STEP3 0x0300 // Clear PR2 to power up Analog (Vref on)
#define GEODEAUDIO_PWRUP_STEP4 0x0100 // Clear PR1 to power up DAC
#define GEODEAUDIO_PWRUP_STEP5 0x0000 // Clear PR0 to power up ADC
#define GEODEAUDIO_CODEC_POWER_ADC 0x0001
#define GEODEAUDIO_CODEC_POWER_DAC 0x0002
#define GEODEAUDIO_CODEC_POWER_ANL 0x0004
#define GEODEAUDIO_CODEC_POWER_REF 0x0008
//
// Device Power States
//
typedef enum _GEODEAUDIO_POWER_STATE
{
GEODEAUDIO_D0 = 0, // Full On: full power, full functionality
GEODEAUDIO_D1, // Low Power On: fully functional at low power/performance
GEODEAUDIO_D2, // Standby: partially powered with automatic wake
GEODEAUDIO_D3, // Sleep: partially powered with device initiated wake
GEODEAUDIO_D4, // Off: unpowered
} GEODEAUDIO_POWER_STATE, *PGEODEAUDIO_POWER_STATE;
//
// Physical address of the device
//
#define AUDIO_REGS_BUFFER 0x40011000
typedef enum
{
PCM_TYPE_M8,
PCM_TYPE_M16,
PCM_TYPE_S8,
PCM_TYPE_S16
} PCM_TYPE, *PPCM_TYPE;
typedef struct
{
unsigned char sample; // Unsigned 8-bit sample
} SAMPLE_8_MONO;
typedef struct
{
signed short sample; // Signed 16-bit sample
} SAMPLE_16_MONO;
typedef struct
{
unsigned char sample_left; // Unsigned 8-bit sample
unsigned char sample_right; // Unsigned 8-bit sample
} SAMPLE_8_STEREO;
typedef struct
{
unsigned short sample_left; // Signed 16-bit sample
unsigned short sample_right; // Signed 16-bit sample
} SAMPLE_16_STEREO;
typedef union
{
SAMPLE_8_MONO m8;
SAMPLE_16_MONO m16;
SAMPLE_8_STEREO s8;
SAMPLE_16_STEREO s16;
} PCM_SAMPLE, *PPCM_SAMPLE;
#define SAMPLE_SIZE_IN_BYTES 8
//
// Copy of the DDk's extended waveform format structure used for all non-PCM formats.
// this structure is common to all non-PCM formats. (Currently used only by the UAM driver)
//
typedef struct tdurWAVEFORMATEX
{
unsigned short wFormatTag; // format type
unsigned short nChannels; // number of channels (i.e. mono, stereo...)
unsigned long nSamplesPerSec; // sample rate
unsigned long nAvgBytesPerSec; // for buffer estimation
unsigned short nBlockAlign; // block size of data
unsigned short wBitsPerSample; // number of bits per sample of mono data
unsigned short cbSize; // the count in bytes of the size of
// extra information (after cbSize)
} durWAVEFORMATEX, *durLPWAVEFORMATEX;
//
// Copy of the DDK's wave data block header
//
typedef struct tdurWAVEHDR
{
char * lpData; // pointer to locked data buffer
unsigned long dwBufferLength; // length of data buffer
unsigned long dwBytesRecorded; // used for input only
unsigned long dwBytesRead; // Number of bytes Read in this buffer
unsigned long dwUser; // for client's use
unsigned long dwFlags; // assorted flags (see defines)
unsigned long dwLoops; // loop control counter
struct tdurWAVEHDR *lpNext; // reserved for driver
unsigned long reserved; // reserved for driver
} durWAVEHDR, *durPWAVEHDR;
// PRD table flags
#define PRD_JMP_BIT 0x20000000
#define PRD_EOP_BIT 0x40000000
#define PRD_EOT_BIT 0x80000000
typedef struct tagPRDEntry
{
unsigned long ulPhysAddr;
unsigned long SizeFlags;
} PRD_ENTRY, *PPRD_ENTRY;
typedef struct tagALLOC_INFO
{
unsigned long PhysicalAddress;
unsigned char *VirtualAddress;
unsigned long Size;
unsigned long OriginalPhysicalAddress;
unsigned char *OriginalVirtualAddress;
unsigned long OriginalSize;
} ALLOC_INFO, *PALLOC_INFO;
//
// Command register bits
//
#define PCI_READS 0x00
#define PCI_WRITES 0x08
#define ENABLE_BUSMASTER 0x01
#define PAUSE_BUSMASTER 0x03
#define STOP_BUSMASTER 0x00
typedef struct tagGEODEAUDIO
{
//
// PCI audio functions base register.(r/w via i/o).
//
unsigned long PCI_Header_Registers;
//
// Pointer to memory mapped 5530 Audio regs.
//
unsigned long F3BAR0;
//
// Power management
//
GEODEAUDIO_POWER_STATE CurrentPowerState;
unsigned short CODECRegisters[0x38];
unsigned char F3BARSave[0x80];
unsigned long AudioBusMaster_PRDTableAddress[MAX_CHANNELS];
//
// Flags
//
unsigned char fAD1819A;
unsigned char fCS553x;
unsigned char fIOAccess;
unsigned char fPolling;
void *pAdapterObject;
unsigned long v_nVolume; // Initialize with 0!
//-----------------------------------------------------------
// BEGIN Interrupt-Related code
//-----------------------------------------------------------
unsigned char *pInterruptID;
unsigned int SelectedIRQ;
volatile unsigned long IRQControlRegister;
volatile unsigned long InternalIRQEnableRegister;
unsigned short uIRQMask;
//-----------------------------------------------------------
// END Interrupt-Related code
//-----------------------------------------------------------
struct tagAudioChannel
{
unsigned char fDirectDMA;
struct tagIndirectDMA
{
unsigned long CurrentTransferPointer;
volatile long BytesRemainingInDMABuffer;
unsigned long SampleConversionFactor;
unsigned long DmaBufferSize;
//
// Data Format
//
unsigned long SampleRate;
unsigned char nChannels;
unsigned char BitsPerSample;
unsigned long FragmentSize;
//
// Old stuff!
//
unsigned char *dma_page[4];
unsigned long *prd_array;
unsigned long prd_array_phys_adr;
} IndirectDMA;
PPRD_ENTRY PRDTable;
unsigned long PRDOriginalVirtualAddress;
unsigned long PRDOriginalPhysicalAddress;
ALLOC_INFO PRD_AllocInfo;
ALLOC_INFO DMA_AllocInfo;
ALLOC_INFO Work_AllocInfo;
unsigned long PRDEntriesAllocated;
unsigned long StopDMAWaitInMiliseconds;
unsigned char Running;
unsigned char fInUse;
unsigned long SampleRate;
unsigned char fAllocated;
struct tagAudioBusMaster
{
unsigned long CommandRegister;
//-----------------------------------------------------------
// BEGIN Interrupt-Related code
//-----------------------------------------------------------
unsigned long SMI_StatusRegister;
//-----------------------------------------------------------
// END Interrupt-Related code
//-----------------------------------------------------------
unsigned long PRDTableAddress;
unsigned char DirectionBit;
unsigned long CurrentPRDPointer;
unsigned long DMAPointer;
} AudioBusMaster;
} AudioChannel[NUMBER_OF_CHANNELS];
} GEODEAUDIO, *PGEODEAUDIO;
#endif // __AMDGEODE_H__
Audio_LinuxALSA_5536_1.00.0500/Makefile.in 0000444 0113737 0000121 00000003365 10247160735 017643 0 ustar background apache CC=@CC@
LD=@LD@
KERNEL_SRC=@KERNEL_SRC@
# Grab the kernel version right out of the tree
KERNEL_VERSION = $(shell sed -ne 's/"//g;s/^\#define UTS_RELEASE //p' \
$(KERNEL_SRC)/include/linux/version.h)
# This is borrowed from the Orinoco drivers. Thanks fellas...
OLDMAKE = $(filter 2.4%, $(KERNEL_VERSION))
DOT_CONFIG = $(wildcard $(KERNEL_SRC)/.config)
ifeq (,$(DOT_CONFIG))
$(error The kernel source is not configured)
endif
include $(DOT_CONFIG)
CURDIR=$(shell pwd)
ifeq (,$(OLDMAKE))
MODULES=snd-geode.o
snd-geode-objs := amd_geode.o os_inc.o
obj-m += snd-geode.o
all: modules
modules:
$(MAKE) -C $(KERNEL_SRC) M=$(CURDIR) modules
install: modules
install -d $(DESTDIR)/lib/modules/$(KERNEL_VERSION)/kernel/drivers/misc
install -m 0644 $(MODULES:%.o=%.ko) $(DESTDIR)/lib/modules/$(KERNEL_VERSION)/kernel/drivers/misc
clean:
rm -f *.o *.ko *.mod.c .*.cmd
else
MODULES=snd-geode.o
KERNEL_HEADERS = -I$(KERNEL_SRC)/include
CPPFLAGS = -D__KERNEL__ -DMODULE -I. $(KERNEL_HEADERS)
CFLAGS=-O2 -g -Wall -Wstrict-prototypes -fno-strict-aliasing -fno-common -pipe
ifdef CONFIG_MODVERSIONS
MFLAG = -DMODVERSIONS -include $(KERNEL_SRC)/include/linux/modversions.h
endif
SRCS=amd_geode.c os_inc.c
all: $(MODULES)
clean:
@ rm -f *.o *.ko *.mod.c .*.cmd
install: $(MODULES)
@ install -d $(DESTDIR)/lib/modules/$(KERNEL_VERSION)/kernel/drivers/misc
@ install -m 0644 $(MODULES) $(DESTDIR)/lib/modules/$(KERNEL_VERSION)/kernel/drivers/misc/snd-geode.o
snd-geode.o: amd_geode.o os_inc.o
$(LD) -r -o $@ amd_geode.o os_inc.o
%.o: %.c
$(CC) -MD $(CFLAGS) $(CPPFLAGS) $(MFLAG) -c $<
%.s: %.c
$(CC) -MD $(CFLAGS) $(CPPFLAGS) -S $<
%.i: %.c
$(CC) -MD $(CPPFLAGS) -E $< -o $@
-include $(SRCS:%.c=%.d)
endif
distclean: clean
@ rm Makefile clean
Audio_LinuxALSA_5536_1.00.0500/AUTHORS 0000644 0113737 0000121 00000000225 10335135606 016635 0 ustar background apache For bugs, patches, and info requests, please subscribe to info-linux@geode.amd.com.
Send a blank message to: info-linux-subscribe@geode.amd.com
Audio_LinuxALSA_5536_1.00.0500/COPYING 0000444 0113737 0000121 00000043131 10247160735 016624 0 ustar background apache GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
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
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.