2019-05-27 09:55:05 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-17 02:20:36 +04:00
/*
* ALSA modem driver for Intel ICH ( i8x0 ) chipsets
*
2007-10-15 11:50:19 +04:00
* Copyright ( c ) 2000 Jaroslav Kysela < perex @ perex . cz >
2005-04-17 02:20:36 +04:00
*
2005-10-10 13:45:31 +04:00
* This is modified ( by Sasha Khapyorsky < sashak @ alsa - project . org > ) version
2005-04-17 02:20:36 +04:00
* of ALSA ICH sound driver intel8x0 . c .
*/
2015-01-28 18:49:33 +03:00
# include <linux/io.h>
2005-04-17 02:20:36 +04:00
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/init.h>
# include <linux/pci.h>
# include <linux/slab.h>
2011-07-15 21:13:37 +04:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/ac97_codec.h>
# include <sound/info.h>
# include <sound/initval.h>
2007-10-15 11:50:19 +04:00
MODULE_AUTHOR ( " Jaroslav Kysela <perex@perex.cz> " ) ;
2005-11-17 17:04:53 +03:00
MODULE_DESCRIPTION ( " Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; "
" SiS 7013; NVidia MCP/2/2S/3 modems " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_SUPPORTED_DEVICE ( " {{Intel,82801AA-ICH}, "
" {Intel,82901AB-ICH0}, "
" {Intel,82801BA-ICH2}, "
" {Intel,82801CA-ICH3}, "
" {Intel,82801DB-ICH4}, "
" {Intel,ICH5}, "
" {Intel,ICH6}, "
" {Intel,ICH7}, "
" {Intel,MX440}, "
" {SiS,7013}, "
" {NVidia,NForce Modem}, "
" {NVidia,NForce2 Modem}, "
" {NVidia,NForce2s Modem}, "
" {NVidia,NForce3 Modem}, "
" {AMD,AMD768}} " ) ;
2005-10-04 10:46:51 +04:00
static int index = - 2 ; /* Exclude the first card */
static char * id = SNDRV_DEFAULT_STR1 ; /* ID for this card */
2006-05-17 19:14:51 +04:00
static int ac97_clock ;
2005-04-17 02:20:36 +04:00
2005-10-04 10:46:51 +04:00
module_param ( index , int , 0444 ) ;
2005-04-17 02:20:36 +04:00
MODULE_PARM_DESC ( index , " Index value for Intel i8x0 modemcard. " ) ;
2005-10-04 10:46:51 +04:00
module_param ( id , charp , 0444 ) ;
2005-04-17 02:20:36 +04:00
MODULE_PARM_DESC ( id , " ID string for Intel i8x0 modemcard. " ) ;
2005-10-04 10:46:51 +04:00
module_param ( ac97_clock , int , 0444 ) ;
2005-04-17 02:20:36 +04:00
MODULE_PARM_DESC ( ac97_clock , " AC'97 codec clock (0 = auto-detect). " ) ;
2005-10-06 15:47:23 +04:00
/* just for backward compatibility */
2011-12-15 07:19:36 +04:00
static bool enable ;
2005-10-20 18:53:49 +04:00
module_param ( enable , bool , 0444 ) ;
2005-10-06 15:47:23 +04:00
2005-04-17 02:20:36 +04:00
/*
* Direct registers
*/
enum { DEVICE_INTEL , DEVICE_SIS , DEVICE_ALI , DEVICE_NFORCE } ;
# define ICHREG(x) ICH_REG_##x
# define DEFINE_REGSET(name,base) \
enum { \
ICH_REG_ # # name # # _BDBAR = base + 0x0 , /* dword - buffer descriptor list base address */ \
ICH_REG_ # # name # # _CIV = base + 0x04 , /* byte - current index value */ \
ICH_REG_ # # name # # _LVI = base + 0x05 , /* byte - last valid index */ \
ICH_REG_ # # name # # _SR = base + 0x06 , /* byte - status register */ \
ICH_REG_ # # name # # _PICB = base + 0x08 , /* word - position in current buffer */ \
ICH_REG_ # # name # # _PIV = base + 0x0a , /* byte - prefetched index value */ \
ICH_REG_ # # name # # _CR = base + 0x0b , /* byte - control register */ \
} ;
/* busmaster blocks */
DEFINE_REGSET ( OFF , 0 ) ; /* offset */
/* values for each busmaster block */
/* LVI */
# define ICH_REG_LVI_MASK 0x1f
/* SR */
# define ICH_FIFOE 0x10 /* FIFO error */
# define ICH_BCIS 0x08 /* buffer completion interrupt status */
# define ICH_LVBCI 0x04 /* last valid buffer completion interrupt */
# define ICH_CELV 0x02 /* current equals last valid */
# define ICH_DCH 0x01 /* DMA controller halted */
/* PIV */
# define ICH_REG_PIV_MASK 0x1f /* mask */
/* CR */
# define ICH_IOCE 0x10 /* interrupt on completion enable */
# define ICH_FEIE 0x08 /* fifo error interrupt enable */
# define ICH_LVBIE 0x04 /* last valid buffer interrupt enable */
# define ICH_RESETREGS 0x02 /* reset busmaster registers */
# define ICH_STARTBM 0x01 /* start busmaster operation */
/* global block */
# define ICH_REG_GLOB_CNT 0x3c /* dword - global control */
# define ICH_TRIE 0x00000040 /* tertiary resume interrupt enable */
# define ICH_SRIE 0x00000020 /* secondary resume interrupt enable */
# define ICH_PRIE 0x00000010 /* primary resume interrupt enable */
# define ICH_ACLINK 0x00000008 /* AClink shut off */
# define ICH_AC97WARM 0x00000004 /* AC'97 warm reset */
# define ICH_AC97COLD 0x00000002 /* AC'97 cold reset */
# define ICH_GIE 0x00000001 /* GPI interrupt enable */
# define ICH_REG_GLOB_STA 0x40 /* dword - global status */
# define ICH_TRI 0x20000000 /* ICH4: tertiary (AC_SDIN2) resume interrupt */
# define ICH_TCR 0x10000000 /* ICH4: tertiary (AC_SDIN2) codec ready */
# define ICH_BCS 0x08000000 /* ICH4: bit clock stopped */
# define ICH_SPINT 0x04000000 /* ICH4: S/PDIF interrupt */
# define ICH_P2INT 0x02000000 /* ICH4: PCM2-In interrupt */
# define ICH_M2INT 0x01000000 /* ICH4: Mic2-In interrupt */
# define ICH_SAMPLE_CAP 0x00c00000 /* ICH4: sample capability bits (RO) */
# define ICH_MULTICHAN_CAP 0x00300000 /* ICH4: multi-channel capability bits (RO) */
# define ICH_MD3 0x00020000 /* modem power down semaphore */
# define ICH_AD3 0x00010000 /* audio power down semaphore */
# define ICH_RCS 0x00008000 /* read completion status */
# define ICH_BIT3 0x00004000 /* bit 3 slot 12 */
# define ICH_BIT2 0x00002000 /* bit 2 slot 12 */
# define ICH_BIT1 0x00001000 /* bit 1 slot 12 */
# define ICH_SRI 0x00000800 /* secondary (AC_SDIN1) resume interrupt */
# define ICH_PRI 0x00000400 /* primary (AC_SDIN0) resume interrupt */
# define ICH_SCR 0x00000200 /* secondary (AC_SDIN1) codec ready */
# define ICH_PCR 0x00000100 /* primary (AC_SDIN0) codec ready */
# define ICH_MCINT 0x00000080 /* MIC capture interrupt */
# define ICH_POINT 0x00000040 /* playback interrupt */
# define ICH_PIINT 0x00000020 /* capture interrupt */
# define ICH_NVSPINT 0x00000010 /* nforce spdif interrupt */
# define ICH_MOINT 0x00000004 /* modem playback interrupt */
# define ICH_MIINT 0x00000002 /* modem capture interrupt */
# define ICH_GSCI 0x00000001 /* GPI status change interrupt */
# define ICH_REG_ACC_SEMA 0x44 /* byte - codec write semaphore */
# define ICH_CAS 0x01 /* codec access semaphore */
# define ICH_MAX_FRAGS 32 /* max hw frags */
/*
*
*/
enum { ICHD_MDMIN , ICHD_MDMOUT , ICHD_MDMLAST = ICHD_MDMOUT } ;
enum { ALID_MDMIN , ALID_MDMOUT , ALID_MDMLAST = ALID_MDMOUT } ;
2005-11-17 17:04:53 +03:00
# define get_ichdev(substream) (substream->runtime->private_data)
2005-04-17 02:20:36 +04:00
2005-11-17 17:04:53 +03:00
struct ichdev {
2005-04-17 02:20:36 +04:00
unsigned int ichd ; /* ich device number */
unsigned long reg_offset ; /* offset to bmaddr */
2018-07-26 00:24:08 +03:00
__le32 * bdbar ; /* CPU address (32bit) */
2005-04-17 02:20:36 +04:00
unsigned int bdbar_addr ; /* PCI bus address (32bit) */
2005-11-17 17:04:53 +03:00
struct snd_pcm_substream * substream ;
2005-04-17 02:20:36 +04:00
unsigned int physbuf ; /* physical address (32bit) */
unsigned int size ;
unsigned int fragsize ;
unsigned int fragsize1 ;
unsigned int position ;
int frags ;
int lvi ;
int lvi_frag ;
int civ ;
int ack ;
int ack_reload ;
unsigned int ack_bit ;
unsigned int roff_sr ;
unsigned int roff_picb ;
unsigned int int_sta_mask ; /* interrupt status mask */
unsigned int ali_slot ; /* ALI DMA slot */
2005-11-17 17:04:53 +03:00
struct snd_ac97 * ac97 ;
} ;
2005-04-17 02:20:36 +04:00
2005-11-17 17:04:53 +03:00
struct intel8x0m {
2005-04-17 02:20:36 +04:00
unsigned int device_type ;
int irq ;
2006-10-06 19:06:39 +04:00
void __iomem * addr ;
void __iomem * bmaddr ;
2005-04-17 02:20:36 +04:00
struct pci_dev * pci ;
2005-11-17 17:04:53 +03:00
struct snd_card * card ;
2005-04-17 02:20:36 +04:00
int pcm_devs ;
2005-11-17 17:04:53 +03:00
struct snd_pcm * pcm [ 2 ] ;
struct ichdev ichd [ 2 ] ;
2005-04-17 02:20:36 +04:00
unsigned int in_ac97_init : 1 ;
2005-11-17 17:04:53 +03:00
struct snd_ac97_bus * ac97_bus ;
struct snd_ac97 * ac97 ;
2005-04-17 02:20:36 +04:00
spinlock_t reg_lock ;
struct snd_dma_buffer bdbars ;
u32 bdbars_count ;
u32 int_sta_reg ; /* interrupt status register */
u32 int_sta_mask ; /* interrupt status mask */
unsigned int pcm_pos_shift ;
} ;
2014-08-08 17:56:03 +04:00
static const struct pci_device_id snd_intel8x0m_ids [ ] = {
2009-06-25 09:13:35 +04:00
{ PCI_VDEVICE ( INTEL , 0x2416 ) , DEVICE_INTEL } , /* 82801AA */
{ PCI_VDEVICE ( INTEL , 0x2426 ) , DEVICE_INTEL } , /* 82901AB */
{ PCI_VDEVICE ( INTEL , 0x2446 ) , DEVICE_INTEL } , /* 82801BA */
{ PCI_VDEVICE ( INTEL , 0x2486 ) , DEVICE_INTEL } , /* ICH3 */
{ PCI_VDEVICE ( INTEL , 0x24c6 ) , DEVICE_INTEL } , /* ICH4 */
{ PCI_VDEVICE ( INTEL , 0x24d6 ) , DEVICE_INTEL } , /* ICH5 */
{ PCI_VDEVICE ( INTEL , 0x266d ) , DEVICE_INTEL } , /* ICH6 */
{ PCI_VDEVICE ( INTEL , 0x27dd ) , DEVICE_INTEL } , /* ICH7 */
{ PCI_VDEVICE ( INTEL , 0x7196 ) , DEVICE_INTEL } , /* 440MX */
{ PCI_VDEVICE ( AMD , 0x7446 ) , DEVICE_INTEL } , /* AMD768 */
{ PCI_VDEVICE ( SI , 0x7013 ) , DEVICE_SIS } , /* SI7013 */
{ PCI_VDEVICE ( NVIDIA , 0x01c1 ) , DEVICE_NFORCE } , /* NFORCE */
{ PCI_VDEVICE ( NVIDIA , 0x0069 ) , DEVICE_NFORCE } , /* NFORCE2 */
{ PCI_VDEVICE ( NVIDIA , 0x0089 ) , DEVICE_NFORCE } , /* NFORCE2s */
{ PCI_VDEVICE ( NVIDIA , 0x00d9 ) , DEVICE_NFORCE } , /* NFORCE3 */
2011-05-19 18:48:27 +04:00
{ PCI_VDEVICE ( AMD , 0x746e ) , DEVICE_INTEL } , /* AMD8111 */
2005-04-17 02:20:36 +04:00
#if 0
2009-06-25 09:13:35 +04:00
{ PCI_VDEVICE ( AL , 0x5455 ) , DEVICE_ALI } , /* Ali5455 */
2005-04-17 02:20:36 +04:00
# endif
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , snd_intel8x0m_ids ) ;
/*
* Lowlevel I / O - busmaster
*/
2006-10-06 19:06:39 +04:00
static inline u8 igetbyte ( struct intel8x0m * chip , u32 offset )
2005-04-17 02:20:36 +04:00
{
2006-10-06 19:06:39 +04:00
return ioread8 ( chip - > bmaddr + offset ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-06 19:06:39 +04:00
static inline u16 igetword ( struct intel8x0m * chip , u32 offset )
2005-04-17 02:20:36 +04:00
{
2006-10-06 19:06:39 +04:00
return ioread16 ( chip - > bmaddr + offset ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-06 19:06:39 +04:00
static inline u32 igetdword ( struct intel8x0m * chip , u32 offset )
2005-04-17 02:20:36 +04:00
{
2006-10-06 19:06:39 +04:00
return ioread32 ( chip - > bmaddr + offset ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-06 19:06:39 +04:00
static inline void iputbyte ( struct intel8x0m * chip , u32 offset , u8 val )
2005-04-17 02:20:36 +04:00
{
2006-10-06 19:06:39 +04:00
iowrite8 ( val , chip - > bmaddr + offset ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-06 19:06:39 +04:00
static inline void iputword ( struct intel8x0m * chip , u32 offset , u16 val )
2005-04-17 02:20:36 +04:00
{
2006-10-06 19:06:39 +04:00
iowrite16 ( val , chip - > bmaddr + offset ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-06 19:06:39 +04:00
static inline void iputdword ( struct intel8x0m * chip , u32 offset , u32 val )
2005-04-17 02:20:36 +04:00
{
2006-10-06 19:06:39 +04:00
iowrite32 ( val , chip - > bmaddr + offset ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Lowlevel I / O - AC ' 97 registers
*/
2006-10-06 19:06:39 +04:00
static inline u16 iagetword ( struct intel8x0m * chip , u32 offset )
2005-04-17 02:20:36 +04:00
{
2006-10-06 19:06:39 +04:00
return ioread16 ( chip - > addr + offset ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-06 19:06:39 +04:00
static inline void iaputword ( struct intel8x0m * chip , u32 offset , u16 val )
2005-04-17 02:20:36 +04:00
{
2006-10-06 19:06:39 +04:00
iowrite16 ( val , chip - > addr + offset ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Basic I / O
*/
/*
* access to AC97 codec via normal i / o ( for ICH and SIS7013 )
*/
/* return the GLOB_STA bit for the corresponding codec */
2005-11-17 17:04:53 +03:00
static unsigned int get_ich_codec_bit ( struct intel8x0m * chip , unsigned int codec )
2005-04-17 02:20:36 +04:00
{
static unsigned int codec_bit [ 3 ] = {
ICH_PCR , ICH_SCR , ICH_TCR
} ;
2008-08-08 19:12:14 +04:00
if ( snd_BUG_ON ( codec > = 3 ) )
return ICH_PCR ;
2005-04-17 02:20:36 +04:00
return codec_bit [ codec ] ;
}
2005-11-17 17:04:53 +03:00
static int snd_intel8x0m_codec_semaphore ( struct intel8x0m * chip , unsigned int codec )
2005-04-17 02:20:36 +04:00
{
int time ;
if ( codec > 1 )
return - EIO ;
codec = get_ich_codec_bit ( chip , codec ) ;
/* codec ready ? */
if ( ( igetdword ( chip , ICHREG ( GLOB_STA ) ) & codec ) = = 0 )
return - EIO ;
/* Anyone holding a semaphore for 1 msec should be shot... */
time = 100 ;
do {
if ( ! ( igetbyte ( chip , ICHREG ( ACC_SEMA ) ) & ICH_CAS ) )
return 0 ;
udelay ( 10 ) ;
} while ( time - - ) ;
2011-03-31 05:57:33 +04:00
/* access to some forbidden (non existent) ac97 registers will not
2005-04-17 02:20:36 +04:00
* reset the semaphore . So even if you don ' t get the semaphore , still
* continue the access . We don ' t need the semaphore anyway . */
2014-02-25 17:40:45 +04:00
dev_err ( chip - > card - > dev ,
" codec_semaphore: semaphore is not ready [0x%x][0x%x] \n " ,
2005-04-17 02:20:36 +04:00
igetbyte ( chip , ICHREG ( ACC_SEMA ) ) , igetdword ( chip , ICHREG ( GLOB_STA ) ) ) ;
iagetword ( chip , 0 ) ; /* clear semaphore flag */
/* I don't care about the semaphore */
return - EBUSY ;
}
2011-03-11 13:37:55 +03:00
static void snd_intel8x0m_codec_write ( struct snd_ac97 * ac97 ,
unsigned short reg ,
unsigned short val )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct intel8x0m * chip = ac97 - > private_data ;
2005-04-17 02:20:36 +04:00
if ( snd_intel8x0m_codec_semaphore ( chip , ac97 - > num ) < 0 ) {
if ( ! chip - > in_ac97_init )
2014-02-25 17:40:45 +04:00
dev_err ( chip - > card - > dev ,
" codec_write %d: semaphore is not ready for register 0x%x \n " ,
ac97 - > num , reg ) ;
2005-04-17 02:20:36 +04:00
}
iaputword ( chip , reg + ac97 - > num * 0x80 , val ) ;
}
2011-03-11 13:37:55 +03:00
static unsigned short snd_intel8x0m_codec_read ( struct snd_ac97 * ac97 ,
unsigned short reg )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct intel8x0m * chip = ac97 - > private_data ;
2005-04-17 02:20:36 +04:00
unsigned short res ;
unsigned int tmp ;
if ( snd_intel8x0m_codec_semaphore ( chip , ac97 - > num ) < 0 ) {
if ( ! chip - > in_ac97_init )
2014-02-25 17:40:45 +04:00
dev_err ( chip - > card - > dev ,
" codec_read %d: semaphore is not ready for register 0x%x \n " ,
ac97 - > num , reg ) ;
2005-04-17 02:20:36 +04:00
res = 0xffff ;
} else {
res = iagetword ( chip , reg + ac97 - > num * 0x80 ) ;
if ( ( tmp = igetdword ( chip , ICHREG ( GLOB_STA ) ) ) & ICH_RCS ) {
/* reset RCS and preserve other R/WC bits */
2005-11-17 17:04:53 +03:00
iputdword ( chip , ICHREG ( GLOB_STA ) ,
tmp & ~ ( ICH_SRI | ICH_PRI | ICH_TRI | ICH_GSCI ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! chip - > in_ac97_init )
2014-02-25 17:40:45 +04:00
dev_err ( chip - > card - > dev ,
" codec_read %d: read timeout for register 0x%x \n " ,
ac97 - > num , reg ) ;
2005-04-17 02:20:36 +04:00
res = 0xffff ;
}
}
2005-04-07 22:21:21 +04:00
if ( reg = = AC97_GPIO_STATUS )
iagetword ( chip , 0 ) ; /* clear semaphore */
2005-04-17 02:20:36 +04:00
return res ;
}
/*
* DMA I / O
*/
2011-03-11 13:37:55 +03:00
static void snd_intel8x0m_setup_periods ( struct intel8x0m * chip , struct ichdev * ichdev )
2005-04-17 02:20:36 +04:00
{
int idx ;
2018-07-26 00:24:08 +03:00
__le32 * bdbar = ichdev - > bdbar ;
2005-04-17 02:20:36 +04:00
unsigned long port = ichdev - > reg_offset ;
iputdword ( chip , port + ICH_REG_OFF_BDBAR , ichdev - > bdbar_addr ) ;
if ( ichdev - > size = = ichdev - > fragsize ) {
ichdev - > ack_reload = ichdev - > ack = 2 ;
ichdev - > fragsize1 = ichdev - > fragsize > > 1 ;
for ( idx = 0 ; idx < ( ICH_REG_LVI_MASK + 1 ) * 2 ; idx + = 4 ) {
bdbar [ idx + 0 ] = cpu_to_le32 ( ichdev - > physbuf ) ;
bdbar [ idx + 1 ] = cpu_to_le32 ( 0x80000000 | /* interrupt on completion */
ichdev - > fragsize1 > > chip - > pcm_pos_shift ) ;
bdbar [ idx + 2 ] = cpu_to_le32 ( ichdev - > physbuf + ( ichdev - > size > > 1 ) ) ;
bdbar [ idx + 3 ] = cpu_to_le32 ( 0x80000000 | /* interrupt on completion */
ichdev - > fragsize1 > > chip - > pcm_pos_shift ) ;
}
ichdev - > frags = 2 ;
} else {
ichdev - > ack_reload = ichdev - > ack = 1 ;
ichdev - > fragsize1 = ichdev - > fragsize ;
for ( idx = 0 ; idx < ( ICH_REG_LVI_MASK + 1 ) * 2 ; idx + = 2 ) {
bdbar [ idx + 0 ] = cpu_to_le32 ( ichdev - > physbuf + ( ( ( idx > > 1 ) * ichdev - > fragsize ) % ichdev - > size ) ) ;
bdbar [ idx + 1 ] = cpu_to_le32 ( 0x80000000 | /* interrupt on completion */
ichdev - > fragsize > > chip - > pcm_pos_shift ) ;
2009-02-05 18:09:57 +03:00
/*
2014-02-25 17:40:45 +04:00
dev_dbg ( chip - > card - > dev , " bdbar[%i] = 0x%x [0x%x] \n " ,
2009-02-05 18:09:57 +03:00
idx + 0 , bdbar [ idx + 0 ] , bdbar [ idx + 1 ] ) ;
*/
2005-04-17 02:20:36 +04:00
}
ichdev - > frags = ichdev - > size / ichdev - > fragsize ;
}
iputbyte ( chip , port + ICH_REG_OFF_LVI , ichdev - > lvi = ICH_REG_LVI_MASK ) ;
ichdev - > civ = 0 ;
iputbyte ( chip , port + ICH_REG_OFF_CIV , 0 ) ;
ichdev - > lvi_frag = ICH_REG_LVI_MASK % ichdev - > frags ;
ichdev - > position = 0 ;
#if 0
2014-02-25 17:40:45 +04:00
dev_dbg ( chip - > card - > dev ,
" lvi_frag = %i, frags = %i, period_size = 0x%x, period_size1 = 0x%x \n " ,
2009-02-05 18:09:57 +03:00
ichdev - > lvi_frag , ichdev - > frags , ichdev - > fragsize ,
ichdev - > fragsize1 ) ;
2005-04-17 02:20:36 +04:00
# endif
/* clear interrupts */
iputbyte ( chip , port + ichdev - > roff_sr , ICH_FIFOE | ICH_BCIS | ICH_LVBCI ) ;
}
/*
* Interrupt handler
*/
2011-03-11 13:37:55 +03:00
static inline void snd_intel8x0m_update ( struct intel8x0m * chip , struct ichdev * ichdev )
2005-04-17 02:20:36 +04:00
{
unsigned long port = ichdev - > reg_offset ;
int civ , i , step ;
int ack = 0 ;
civ = igetbyte ( chip , port + ICH_REG_OFF_CIV ) ;
if ( civ = = ichdev - > civ ) {
// snd_printd("civ same %d\n", civ);
step = 1 ;
ichdev - > civ + + ;
ichdev - > civ & = ICH_REG_LVI_MASK ;
} else {
step = civ - ichdev - > civ ;
if ( step < 0 )
step + = ICH_REG_LVI_MASK + 1 ;
// if (step != 1)
// snd_printd("step = %d, %d -> %d\n", step, ichdev->civ, civ);
ichdev - > civ = civ ;
}
ichdev - > position + = step * ichdev - > fragsize1 ;
ichdev - > position % = ichdev - > size ;
ichdev - > lvi + = step ;
ichdev - > lvi & = ICH_REG_LVI_MASK ;
iputbyte ( chip , port + ICH_REG_OFF_LVI , ichdev - > lvi ) ;
for ( i = 0 ; i < step ; i + + ) {
ichdev - > lvi_frag + + ;
ichdev - > lvi_frag % = ichdev - > frags ;
2005-11-17 17:04:53 +03:00
ichdev - > bdbar [ ichdev - > lvi * 2 ] = cpu_to_le32 ( ichdev - > physbuf +
ichdev - > lvi_frag *
ichdev - > fragsize1 ) ;
#if 0
2014-02-25 17:40:45 +04:00
dev_dbg ( chip - > card - > dev ,
" new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x \n " ,
2005-11-17 17:04:53 +03:00
ichdev - > lvi * 2 , ichdev - > bdbar [ ichdev - > lvi * 2 ] ,
ichdev - > bdbar [ ichdev - > lvi * 2 + 1 ] , inb ( ICH_REG_OFF_PIV + port ) ,
inl ( port + 4 ) , inb ( port + ICH_REG_OFF_CR ) ) ;
# endif
2005-04-17 02:20:36 +04:00
if ( - - ichdev - > ack = = 0 ) {
ichdev - > ack = ichdev - > ack_reload ;
ack = 1 ;
}
}
if ( ack & & ichdev - > substream ) {
spin_unlock ( & chip - > reg_lock ) ;
snd_pcm_period_elapsed ( ichdev - > substream ) ;
spin_lock ( & chip - > reg_lock ) ;
}
iputbyte ( chip , port + ichdev - > roff_sr , ICH_FIFOE | ICH_BCIS | ICH_LVBCI ) ;
}
2011-03-11 13:37:55 +03:00
static irqreturn_t snd_intel8x0m_interrupt ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct intel8x0m * chip = dev_id ;
struct ichdev * ichdev ;
2005-04-17 02:20:36 +04:00
unsigned int status ;
unsigned int i ;
spin_lock ( & chip - > reg_lock ) ;
status = igetdword ( chip , chip - > int_sta_reg ) ;
if ( status = = 0xffffffff ) { /* we are not yet resumed */
spin_unlock ( & chip - > reg_lock ) ;
return IRQ_NONE ;
}
if ( ( status & chip - > int_sta_mask ) = = 0 ) {
if ( status )
iputdword ( chip , chip - > int_sta_reg , status ) ;
spin_unlock ( & chip - > reg_lock ) ;
return IRQ_NONE ;
}
for ( i = 0 ; i < chip - > bdbars_count ; i + + ) {
ichdev = & chip - > ichd [ i ] ;
if ( status & ichdev - > int_sta_mask )
2011-03-11 13:37:55 +03:00
snd_intel8x0m_update ( chip , ichdev ) ;
2005-04-17 02:20:36 +04:00
}
/* ack them */
iputdword ( chip , chip - > int_sta_reg , status & chip - > int_sta_mask ) ;
spin_unlock ( & chip - > reg_lock ) ;
return IRQ_HANDLED ;
}
/*
* PCM part
*/
2011-03-11 13:37:55 +03:00
static int snd_intel8x0m_pcm_trigger ( struct snd_pcm_substream * substream , int cmd )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct intel8x0m * chip = snd_pcm_substream_chip ( substream ) ;
struct ichdev * ichdev = get_ichdev ( substream ) ;
2005-04-17 02:20:36 +04:00
unsigned char val = 0 ;
unsigned long port = ichdev - > reg_offset ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
val = ICH_IOCE | ICH_STARTBM ;
break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
val = 0 ;
break ;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
val = ICH_IOCE ;
break ;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
val = ICH_IOCE | ICH_STARTBM ;
break ;
default :
return - EINVAL ;
}
iputbyte ( chip , port + ICH_REG_OFF_CR , val ) ;
if ( cmd = = SNDRV_PCM_TRIGGER_STOP ) {
/* wait until DMA stopped */
while ( ! ( igetbyte ( chip , port + ichdev - > roff_sr ) & ICH_DCH ) ) ;
/* reset whole DMA things */
iputbyte ( chip , port + ICH_REG_OFF_CR , ICH_RESETREGS ) ;
}
return 0 ;
}
2011-03-11 13:37:55 +03:00
static int snd_intel8x0m_hw_params ( struct snd_pcm_substream * substream ,
2005-11-17 17:04:53 +03:00
struct snd_pcm_hw_params * hw_params )
2005-04-17 02:20:36 +04:00
{
return snd_pcm_lib_malloc_pages ( substream , params_buffer_bytes ( hw_params ) ) ;
}
2011-03-11 13:37:55 +03:00
static int snd_intel8x0m_hw_free ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
return snd_pcm_lib_free_pages ( substream ) ;
}
2011-03-11 13:37:55 +03:00
static snd_pcm_uframes_t snd_intel8x0m_pcm_pointer ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct intel8x0m * chip = snd_pcm_substream_chip ( substream ) ;
struct ichdev * ichdev = get_ichdev ( substream ) ;
2005-04-17 02:20:36 +04:00
size_t ptr1 , ptr ;
ptr1 = igetword ( chip , ichdev - > reg_offset + ichdev - > roff_picb ) < < chip - > pcm_pos_shift ;
if ( ptr1 ! = 0 )
ptr = ichdev - > fragsize1 - ptr1 ;
else
ptr = 0 ;
ptr + = ichdev - > position ;
if ( ptr > = ichdev - > size )
return 0 ;
return bytes_to_frames ( substream - > runtime , ptr ) ;
}
2005-11-17 17:04:53 +03:00
static int snd_intel8x0m_pcm_prepare ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct intel8x0m * chip = snd_pcm_substream_chip ( substream ) ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct ichdev * ichdev = get_ichdev ( substream ) ;
2005-04-17 02:20:36 +04:00
ichdev - > physbuf = runtime - > dma_addr ;
ichdev - > size = snd_pcm_lib_buffer_bytes ( substream ) ;
ichdev - > fragsize = snd_pcm_lib_period_bytes ( substream ) ;
snd_ac97_write ( ichdev - > ac97 , AC97_LINE1_RATE , runtime - > rate ) ;
snd_ac97_write ( ichdev - > ac97 , AC97_LINE1_LEVEL , 0 ) ;
2011-03-11 13:37:55 +03:00
snd_intel8x0m_setup_periods ( chip , ichdev ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2017-08-12 18:31:28 +03:00
static const struct snd_pcm_hardware snd_intel8x0m_stream =
2005-04-17 02:20:36 +04:00
{
. info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME ) ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
. rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_KNOT ,
. rate_min = 8000 ,
. rate_max = 16000 ,
. channels_min = 1 ,
. channels_max = 1 ,
. buffer_bytes_max = 64 * 1024 ,
. period_bytes_min = 32 ,
. period_bytes_max = 64 * 1024 ,
. periods_min = 1 ,
. periods_max = 1024 ,
. fifo_size = 0 ,
} ;
2005-11-17 17:04:53 +03:00
static int snd_intel8x0m_pcm_open ( struct snd_pcm_substream * substream , struct ichdev * ichdev )
2005-04-17 02:20:36 +04:00
{
2017-06-07 15:21:31 +03:00
static const unsigned int rates [ ] = { 8000 , 9600 , 12000 , 16000 } ;
static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
2005-04-17 02:20:36 +04:00
. count = ARRAY_SIZE ( rates ) ,
. list = rates ,
. mask = 0 ,
} ;
2005-11-17 17:04:53 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
int err ;
ichdev - > substream = substream ;
runtime - > hw = snd_intel8x0m_stream ;
2005-11-17 17:04:53 +03:00
err = snd_pcm_hw_constraint_list ( runtime , 0 , SNDRV_PCM_HW_PARAM_RATE ,
& hw_constraints_rates ) ;
2005-04-17 02:20:36 +04:00
if ( err < 0 )
return err ;
runtime - > private_data = ichdev ;
return 0 ;
}
2005-11-17 17:04:53 +03:00
static int snd_intel8x0m_playback_open ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct intel8x0m * chip = snd_pcm_substream_chip ( substream ) ;
2005-04-17 02:20:36 +04:00
return snd_intel8x0m_pcm_open ( substream , & chip - > ichd [ ICHD_MDMOUT ] ) ;
}
2005-11-17 17:04:53 +03:00
static int snd_intel8x0m_playback_close ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct intel8x0m * chip = snd_pcm_substream_chip ( substream ) ;
2005-04-17 02:20:36 +04:00
chip - > ichd [ ICHD_MDMOUT ] . substream = NULL ;
return 0 ;
}
2005-11-17 17:04:53 +03:00
static int snd_intel8x0m_capture_open ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct intel8x0m * chip = snd_pcm_substream_chip ( substream ) ;
2005-04-17 02:20:36 +04:00
return snd_intel8x0m_pcm_open ( substream , & chip - > ichd [ ICHD_MDMIN ] ) ;
}
2005-11-17 17:04:53 +03:00
static int snd_intel8x0m_capture_close ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct intel8x0m * chip = snd_pcm_substream_chip ( substream ) ;
2005-04-17 02:20:36 +04:00
chip - > ichd [ ICHD_MDMIN ] . substream = NULL ;
return 0 ;
}
2017-08-10 14:47:36 +03:00
static const struct snd_pcm_ops snd_intel8x0m_playback_ops = {
2005-04-17 02:20:36 +04:00
. open = snd_intel8x0m_playback_open ,
. close = snd_intel8x0m_playback_close ,
. ioctl = snd_pcm_lib_ioctl ,
2011-03-11 13:37:55 +03:00
. hw_params = snd_intel8x0m_hw_params ,
. hw_free = snd_intel8x0m_hw_free ,
2005-04-17 02:20:36 +04:00
. prepare = snd_intel8x0m_pcm_prepare ,
2011-03-11 13:37:55 +03:00
. trigger = snd_intel8x0m_pcm_trigger ,
. pointer = snd_intel8x0m_pcm_pointer ,
2005-04-17 02:20:36 +04:00
} ;
2017-08-10 14:47:36 +03:00
static const struct snd_pcm_ops snd_intel8x0m_capture_ops = {
2005-04-17 02:20:36 +04:00
. open = snd_intel8x0m_capture_open ,
. close = snd_intel8x0m_capture_close ,
. ioctl = snd_pcm_lib_ioctl ,
2011-03-11 13:37:55 +03:00
. hw_params = snd_intel8x0m_hw_params ,
. hw_free = snd_intel8x0m_hw_free ,
2005-04-17 02:20:36 +04:00
. prepare = snd_intel8x0m_pcm_prepare ,
2011-03-11 13:37:55 +03:00
. trigger = snd_intel8x0m_pcm_trigger ,
. pointer = snd_intel8x0m_pcm_pointer ,
2005-04-17 02:20:36 +04:00
} ;
struct ich_pcm_table {
char * suffix ;
2017-08-10 14:47:36 +03:00
const struct snd_pcm_ops * playback_ops ;
const struct snd_pcm_ops * capture_ops ;
2005-04-17 02:20:36 +04:00
size_t prealloc_size ;
size_t prealloc_max_size ;
int ac97_idx ;
} ;
2012-12-06 21:35:10 +04:00
static int snd_intel8x0m_pcm1 ( struct intel8x0m * chip , int device ,
struct ich_pcm_table * rec )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct snd_pcm * pcm ;
2005-04-17 02:20:36 +04:00
int err ;
char name [ 32 ] ;
if ( rec - > suffix )
sprintf ( name , " Intel ICH - %s " , rec - > suffix ) ;
else
strcpy ( name , " Intel ICH " ) ;
err = snd_pcm_new ( chip - > card , name , device ,
rec - > playback_ops ? 1 : 0 ,
rec - > capture_ops ? 1 : 0 , & pcm ) ;
if ( err < 0 )
return err ;
if ( rec - > playback_ops )
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_PLAYBACK , rec - > playback_ops ) ;
if ( rec - > capture_ops )
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_CAPTURE , rec - > capture_ops ) ;
pcm - > private_data = chip ;
pcm - > info_flags = 0 ;
2005-09-29 13:48:17 +04:00
pcm - > dev_class = SNDRV_PCM_CLASS_MODEM ;
2005-04-17 02:20:36 +04:00
if ( rec - > suffix )
sprintf ( pcm - > name , " %s - %s " , chip - > card - > shortname , rec - > suffix ) ;
else
strcpy ( pcm - > name , chip - > card - > shortname ) ;
chip - > pcm [ device ] = pcm ;
snd_pcm_lib_preallocate_pages_for_all ( pcm , SNDRV_DMA_TYPE_DEV ,
snd_dma_pci_data ( chip - > pci ) ,
rec - > prealloc_size ,
rec - > prealloc_max_size ) ;
return 0 ;
}
2012-12-06 21:35:10 +04:00
static struct ich_pcm_table intel_pcms [ ] = {
2005-04-17 02:20:36 +04:00
{
. suffix = " Modem " ,
. playback_ops = & snd_intel8x0m_playback_ops ,
. capture_ops = & snd_intel8x0m_capture_ops ,
. prealloc_size = 32 * 1024 ,
. prealloc_max_size = 64 * 1024 ,
} ,
} ;
2012-12-06 21:35:10 +04:00
static int snd_intel8x0m_pcm ( struct intel8x0m * chip )
2005-04-17 02:20:36 +04:00
{
int i , tblsize , device , err ;
struct ich_pcm_table * tbl , * rec ;
# if 1
tbl = intel_pcms ;
tblsize = 1 ;
# else
switch ( chip - > device_type ) {
case DEVICE_NFORCE :
tbl = nforce_pcms ;
tblsize = ARRAY_SIZE ( nforce_pcms ) ;
break ;
case DEVICE_ALI :
tbl = ali_pcms ;
tblsize = ARRAY_SIZE ( ali_pcms ) ;
break ;
default :
tbl = intel_pcms ;
tblsize = 2 ;
break ;
}
# endif
device = 0 ;
for ( i = 0 ; i < tblsize ; i + + ) {
rec = tbl + i ;
if ( i > 0 & & rec - > ac97_idx ) {
/* activate PCM only when associated AC'97 codec */
if ( ! chip - > ichd [ rec - > ac97_idx ] . ac97 )
continue ;
}
2011-03-11 13:37:55 +03:00
err = snd_intel8x0m_pcm1 ( chip , device , rec ) ;
2005-04-17 02:20:36 +04:00
if ( err < 0 )
return err ;
device + + ;
}
chip - > pcm_devs = device ;
return 0 ;
}
/*
* Mixer part
*/
2011-03-11 13:37:55 +03:00
static void snd_intel8x0m_mixer_free_ac97_bus ( struct snd_ac97_bus * bus )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct intel8x0m * chip = bus - > private_data ;
2005-04-17 02:20:36 +04:00
chip - > ac97_bus = NULL ;
}
2011-03-11 13:37:55 +03:00
static void snd_intel8x0m_mixer_free_ac97 ( struct snd_ac97 * ac97 )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct intel8x0m * chip = ac97 - > private_data ;
2005-04-17 02:20:36 +04:00
chip - > ac97 = NULL ;
}
2012-12-06 21:35:10 +04:00
static int snd_intel8x0m_mixer ( struct intel8x0m * chip , int ac97_clock )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct snd_ac97_bus * pbus ;
struct snd_ac97_template ac97 ;
struct snd_ac97 * x97 ;
2005-04-17 02:20:36 +04:00
int err ;
unsigned int glob_sta = 0 ;
2005-11-17 17:04:53 +03:00
static struct snd_ac97_bus_ops ops = {
2011-03-11 13:37:55 +03:00
. write = snd_intel8x0m_codec_write ,
. read = snd_intel8x0m_codec_read ,
2005-04-17 02:20:36 +04:00
} ;
chip - > in_ac97_init = 1 ;
memset ( & ac97 , 0 , sizeof ( ac97 ) ) ;
ac97 . private_data = chip ;
2011-03-11 13:37:55 +03:00
ac97 . private_free = snd_intel8x0m_mixer_free_ac97 ;
2006-10-24 20:25:29 +04:00
ac97 . scaps = AC97_SCAP_SKIP_AUDIO | AC97_SCAP_POWER_SAVE ;
2005-04-17 02:20:36 +04:00
glob_sta = igetdword ( chip , ICHREG ( GLOB_STA ) ) ;
if ( ( err = snd_ac97_bus ( chip - > card , 0 , & ops , chip , & pbus ) ) < 0 )
goto __err ;
2011-03-11 13:37:55 +03:00
pbus - > private_free = snd_intel8x0m_mixer_free_ac97_bus ;
2005-04-17 02:20:36 +04:00
if ( ac97_clock > = 8000 & & ac97_clock < = 48000 )
pbus - > clock = ac97_clock ;
chip - > ac97_bus = pbus ;
ac97 . pci = chip - > pci ;
ac97 . num = glob_sta & ICH_SCR ? 1 : 0 ;
if ( ( err = snd_ac97_mixer ( pbus , & ac97 , & x97 ) ) < 0 ) {
2014-02-25 17:40:45 +04:00
dev_err ( chip - > card - > dev ,
" Unable to initialize codec #%d \n " , ac97 . num ) ;
2005-04-17 02:20:36 +04:00
if ( ac97 . num = = 0 )
goto __err ;
return err ;
}
chip - > ac97 = x97 ;
if ( ac97_is_modem ( x97 ) & & ! chip - > ichd [ ICHD_MDMIN ] . ac97 ) {
chip - > ichd [ ICHD_MDMIN ] . ac97 = x97 ;
chip - > ichd [ ICHD_MDMOUT ] . ac97 = x97 ;
}
chip - > in_ac97_init = 0 ;
return 0 ;
__err :
/* clear the cold-reset bit for the next chance */
if ( chip - > device_type ! = DEVICE_ALI )
2005-11-17 17:04:53 +03:00
iputdword ( chip , ICHREG ( GLOB_CNT ) ,
igetdword ( chip , ICHREG ( GLOB_CNT ) ) & ~ ICH_AC97COLD ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
/*
*
*/
2005-11-17 17:04:53 +03:00
static int snd_intel8x0m_ich_chip_init ( struct intel8x0m * chip , int probing )
2005-04-17 02:20:36 +04:00
{
unsigned long end_time ;
unsigned int cnt , status , nstatus ;
/* put logic to right state */
/* first clear status bits */
status = ICH_RCS | ICH_MIINT | ICH_MOINT ;
cnt = igetdword ( chip , ICHREG ( GLOB_STA ) ) ;
iputdword ( chip , ICHREG ( GLOB_STA ) , cnt & status ) ;
/* ACLink on, 2 channels */
cnt = igetdword ( chip , ICHREG ( GLOB_CNT ) ) ;
cnt & = ~ ( ICH_ACLINK ) ;
/* finish cold or do warm reset */
cnt | = ( cnt & ICH_AC97COLD ) = = 0 ? ICH_AC97COLD : ICH_AC97WARM ;
iputdword ( chip , ICHREG ( GLOB_CNT ) , cnt ) ;
ALSA: intel8x0m: wait a bit before warm reset check
At every resume a laptop I use prints this message (at KERN_ERR level):
ALSA sound/pci/intel8x0m.c:904: AC'97 warm reset still in progress? [0x2]
The thing to note here is that 0x2 corresponds to ICH_AC97COLD. Ie, what
seems to be happening is that the register involved indicated a warm
reset for some time (as the ICH_AC97WARM bit was set) but by the time
the warning is printed, and that same register is checked again, that
bit is already cleared and only the ICH_AC97COLD bit is still set.
It turns out a warm reset needs some time to settle, but it is currently
checked right away. The test therefore fails the first time it is done
and schedule_timeout_uninterruptible() will be called. Once we return
from that jiffies is already (far) past end_time on this laptop, so we
exit the loop, print a warning, and exit the function while the warm
reset actually succeeded.
A way to fix this is to call usleep_range() after writing to the
register involved. A handful of tests suggest 500 usecs is a safe value.
(This might punish the "finish cold reset" case, but on this laptop such
a cold reset apparently never happens, so I can't say for sure.)
While we're at it drop the extra single tick from end_time, as it looks
rather silly.
Signed-off-by: Paul Bolle <pebolle@tiscali.nl>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2011-03-11 13:24:52 +03:00
usleep_range ( 500 , 1000 ) ; /* give warm reset some time */
end_time = jiffies + HZ / 4 ;
2005-04-17 02:20:36 +04:00
do {
if ( ( igetdword ( chip , ICHREG ( GLOB_CNT ) ) & ICH_AC97WARM ) = = 0 )
goto __ok ;
2005-11-17 12:37:40 +03:00
schedule_timeout_uninterruptible ( 1 ) ;
2005-04-17 02:20:36 +04:00
} while ( time_after_eq ( end_time , jiffies ) ) ;
2014-02-25 17:40:45 +04:00
dev_err ( chip - > card - > dev , " AC'97 warm reset still in progress? [0x%x] \n " ,
2005-11-17 17:04:53 +03:00
igetdword ( chip , ICHREG ( GLOB_CNT ) ) ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
__ok :
if ( probing ) {
/* wait for any codec ready status.
* Once it becomes ready it should remain ready
* as long as we do not disable the ac97 link .
*/
end_time = jiffies + HZ ;
do {
2005-11-17 17:04:53 +03:00
status = igetdword ( chip , ICHREG ( GLOB_STA ) ) &
( ICH_PCR | ICH_SCR | ICH_TCR ) ;
2005-04-17 02:20:36 +04:00
if ( status )
break ;
2005-11-17 12:37:40 +03:00
schedule_timeout_uninterruptible ( 1 ) ;
2005-04-17 02:20:36 +04:00
} while ( time_after_eq ( end_time , jiffies ) ) ;
if ( ! status ) {
/* no codec is found */
2014-02-25 17:40:45 +04:00
dev_err ( chip - > card - > dev ,
" codec_ready: codec is not ready [0x%x] \n " ,
2005-11-17 17:04:53 +03:00
igetdword ( chip , ICHREG ( GLOB_STA ) ) ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
/* up to two codecs (modem cannot be tertiary with ICH4) */
nstatus = ICH_PCR | ICH_SCR ;
/* wait for other codecs ready status. */
end_time = jiffies + HZ / 4 ;
while ( status ! = nstatus & & time_after_eq ( end_time , jiffies ) ) {
2005-11-17 12:37:40 +03:00
schedule_timeout_uninterruptible ( 1 ) ;
2005-04-17 02:20:36 +04:00
status | = igetdword ( chip , ICHREG ( GLOB_STA ) ) & nstatus ;
}
} else {
/* resume phase */
status = 0 ;
if ( chip - > ac97 )
status | = get_ich_codec_bit ( chip , chip - > ac97 - > num ) ;
/* wait until all the probed codecs are ready */
end_time = jiffies + HZ ;
do {
2005-11-17 17:04:53 +03:00
nstatus = igetdword ( chip , ICHREG ( GLOB_STA ) ) &
( ICH_PCR | ICH_SCR | ICH_TCR ) ;
2005-04-17 02:20:36 +04:00
if ( status = = nstatus )
break ;
2005-11-17 12:37:40 +03:00
schedule_timeout_uninterruptible ( 1 ) ;
2005-04-17 02:20:36 +04:00
} while ( time_after_eq ( end_time , jiffies ) ) ;
}
if ( chip - > device_type = = DEVICE_SIS ) {
/* unmute the output on SIS7012 */
iputword ( chip , 0x4c , igetword ( chip , 0x4c ) | 1 ) ;
}
return 0 ;
}
2011-03-11 13:37:55 +03:00
static int snd_intel8x0m_chip_init ( struct intel8x0m * chip , int probing )
2005-04-17 02:20:36 +04:00
{
unsigned int i ;
int err ;
if ( ( err = snd_intel8x0m_ich_chip_init ( chip , probing ) ) < 0 )
return err ;
iagetword ( chip , 0 ) ; /* clear semaphore flag */
/* disable interrupts */
for ( i = 0 ; i < chip - > bdbars_count ; i + + )
iputbyte ( chip , ICH_REG_OFF_CR + chip - > ichd [ i ] . reg_offset , 0x00 ) ;
/* reset channels */
for ( i = 0 ; i < chip - > bdbars_count ; i + + )
iputbyte ( chip , ICH_REG_OFF_CR + chip - > ichd [ i ] . reg_offset , ICH_RESETREGS ) ;
/* initialize Buffer Descriptor Lists */
for ( i = 0 ; i < chip - > bdbars_count ; i + + )
iputdword ( chip , ICH_REG_OFF_BDBAR + chip - > ichd [ i ] . reg_offset , chip - > ichd [ i ] . bdbar_addr ) ;
return 0 ;
}
2011-03-11 13:37:55 +03:00
static int snd_intel8x0m_free ( struct intel8x0m * chip )
2005-04-17 02:20:36 +04:00
{
unsigned int i ;
if ( chip - > irq < 0 )
goto __hw_end ;
/* disable interrupts */
for ( i = 0 ; i < chip - > bdbars_count ; i + + )
iputbyte ( chip , ICH_REG_OFF_CR + chip - > ichd [ i ] . reg_offset , 0x00 ) ;
/* reset channels */
for ( i = 0 ; i < chip - > bdbars_count ; i + + )
iputbyte ( chip , ICH_REG_OFF_CR + chip - > ichd [ i ] . reg_offset , ICH_RESETREGS ) ;
2008-04-22 19:28:11 +04:00
__hw_end :
2008-04-22 15:50:34 +04:00
if ( chip - > irq > = 0 )
2008-04-22 19:28:11 +04:00
free_irq ( chip - > irq , chip ) ;
2005-04-17 02:20:36 +04:00
if ( chip - > bdbars . area )
snd_dma_free_pages ( & chip - > bdbars ) ;
2006-10-06 19:06:39 +04:00
if ( chip - > addr )
pci_iounmap ( chip - > pci , chip - > addr ) ;
if ( chip - > bmaddr )
pci_iounmap ( chip - > pci , chip - > bmaddr ) ;
2005-04-17 02:20:36 +04:00
pci_release_regions ( chip - > pci ) ;
pci_disable_device ( chip - > pci ) ;
kfree ( chip ) ;
return 0 ;
}
2012-08-14 20:12:04 +04:00
# ifdef CONFIG_PM_SLEEP
2005-04-17 02:20:36 +04:00
/*
* power management
*/
2012-07-02 17:20:37 +04:00
static int intel8x0m_suspend ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2012-07-02 17:20:37 +04:00
struct snd_card * card = dev_get_drvdata ( dev ) ;
2005-11-17 18:10:01 +03:00
struct intel8x0m * chip = card - > private_data ;
2005-04-17 02:20:36 +04:00
2005-11-17 18:10:01 +03:00
snd_power_change_state ( card , SNDRV_CTL_POWER_D3hot ) ;
snd_ac97_suspend ( chip - > ac97 ) ;
2006-10-11 20:52:53 +04:00
if ( chip - > irq > = 0 ) {
2006-09-22 17:30:42 +04:00
free_irq ( chip - > irq , chip ) ;
2006-10-11 20:52:53 +04:00
chip - > irq = - 1 ;
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
2012-07-02 17:20:37 +04:00
static int intel8x0m_resume ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2012-07-02 17:20:37 +04:00
struct pci_dev * pci = to_pci_dev ( dev ) ;
struct snd_card * card = dev_get_drvdata ( dev ) ;
2005-11-17 18:10:01 +03:00
struct intel8x0m * chip = card - > private_data ;
2011-03-11 13:37:55 +03:00
if ( request_irq ( pci - > irq , snd_intel8x0m_interrupt ,
2011-06-10 18:36:37 +04:00
IRQF_SHARED , KBUILD_MODNAME , chip ) ) {
2014-02-25 17:40:45 +04:00
dev_err ( dev , " unable to grab IRQ %d, disabling device \n " ,
pci - > irq ) ;
2006-10-11 20:52:53 +04:00
snd_card_disconnect ( card ) ;
return - EIO ;
}
2006-09-22 17:30:42 +04:00
chip - > irq = pci - > irq ;
2011-03-11 13:37:55 +03:00
snd_intel8x0m_chip_init ( chip , 0 ) ;
2005-11-17 18:10:01 +03:00
snd_ac97_resume ( chip - > ac97 ) ;
2005-04-17 02:20:36 +04:00
2005-11-17 18:10:01 +03:00
snd_power_change_state ( card , SNDRV_CTL_POWER_D0 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2012-07-02 17:20:37 +04:00
static SIMPLE_DEV_PM_OPS ( intel8x0m_pm , intel8x0m_suspend , intel8x0m_resume ) ;
# define INTEL8X0M_PM_OPS &intel8x0m_pm
# else
# define INTEL8X0M_PM_OPS NULL
2012-08-14 20:12:04 +04:00
# endif /* CONFIG_PM_SLEEP */
2005-04-17 02:20:36 +04:00
2005-11-17 17:04:53 +03:00
static void snd_intel8x0m_proc_read ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct intel8x0m * chip = entry - > private_data ;
2005-04-17 02:20:36 +04:00
unsigned int tmp ;
snd_iprintf ( buffer , " Intel8x0m \n \n " ) ;
if ( chip - > device_type = = DEVICE_ALI )
return ;
tmp = igetdword ( chip , ICHREG ( GLOB_STA ) ) ;
2005-11-17 17:04:53 +03:00
snd_iprintf ( buffer , " Global control : 0x%08x \n " ,
igetdword ( chip , ICHREG ( GLOB_CNT ) ) ) ;
2005-04-17 02:20:36 +04:00
snd_iprintf ( buffer , " Global status : 0x%08x \n " , tmp ) ;
snd_iprintf ( buffer , " AC'97 codecs ready :%s%s%s%s \n " ,
tmp & ICH_PCR ? " primary " : " " ,
tmp & ICH_SCR ? " secondary " : " " ,
tmp & ICH_TCR ? " tertiary " : " " ,
( tmp & ( ICH_PCR | ICH_SCR | ICH_TCR ) ) = = 0 ? " none " : " " ) ;
}
2012-12-06 21:35:10 +04:00
static void snd_intel8x0m_proc_init ( struct intel8x0m * chip )
2005-04-17 02:20:36 +04:00
{
2019-02-04 18:01:39 +03:00
snd_card_ro_proc_new ( chip - > card , " intel8x0m " , chip ,
snd_intel8x0m_proc_read ) ;
2005-04-17 02:20:36 +04:00
}
2011-03-11 13:37:55 +03:00
static int snd_intel8x0m_dev_free ( struct snd_device * device )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct intel8x0m * chip = device - > device_data ;
2011-03-11 13:37:55 +03:00
return snd_intel8x0m_free ( chip ) ;
2005-04-17 02:20:36 +04:00
}
struct ich_reg_info {
unsigned int int_sta_mask ;
unsigned int offset ;
} ;
2012-12-06 21:35:10 +04:00
static int snd_intel8x0m_create ( struct snd_card * card ,
struct pci_dev * pci ,
unsigned long device_type ,
struct intel8x0m * * r_intel8x0m )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct intel8x0m * chip ;
2005-04-17 02:20:36 +04:00
int err ;
unsigned int i ;
unsigned int int_sta_masks ;
2005-11-17 17:04:53 +03:00
struct ichdev * ichdev ;
static struct snd_device_ops ops = {
2011-03-11 13:37:55 +03:00
. dev_free = snd_intel8x0m_dev_free ,
2005-04-17 02:20:36 +04:00
} ;
static struct ich_reg_info intel_regs [ 2 ] = {
{ ICH_MIINT , 0 } ,
{ ICH_MOINT , 0x10 } ,
} ;
struct ich_reg_info * tbl ;
2011-03-11 13:41:06 +03:00
* r_intel8x0m = NULL ;
2005-04-17 02:20:36 +04:00
if ( ( err = pci_enable_device ( pci ) ) < 0 )
return err ;
[ALSA] Replace with kzalloc() - pci stuff
AD1889 driver,ATIIXP driver,ATIIXP-modem driver,AZT3328 driver
BT87x driver,CMIPCI driver,CS4281 driver,ENS1370/1+ driver
ES1938 driver,ES1968 driver,FM801 driver,Intel8x0 driver
Intel8x0-modem driver,Maestro3 driver,SonicVibes driver,VIA82xx driver
VIA82xx-modem driver,AC97 Codec,AK4531 codec,au88x0 driver
CA0106 driver,CS46xx driver,EMU10K1/EMU10K2 driver,HDA Codec driver
HDA generic driver,HDA Intel driver,ICE1712 driver,ICE1724 driver
KORG1212 driver,MIXART driver,NM256 driver,Trident driver,YMFPCI driver
Replace kcalloc(1,..) with kzalloc().
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-09-09 16:21:46 +04:00
chip = kzalloc ( sizeof ( * chip ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( chip = = NULL ) {
pci_disable_device ( pci ) ;
return - ENOMEM ;
}
spin_lock_init ( & chip - > reg_lock ) ;
chip - > device_type = device_type ;
chip - > card = card ;
chip - > pci = pci ;
chip - > irq = - 1 ;
if ( ( err = pci_request_regions ( pci , card - > shortname ) ) < 0 ) {
kfree ( chip ) ;
pci_disable_device ( pci ) ;
return err ;
}
if ( device_type = = DEVICE_ALI ) {
/* ALI5455 has no ac97 region */
2006-10-06 19:06:39 +04:00
chip - > bmaddr = pci_iomap ( pci , 0 , 0 ) ;
2005-04-17 02:20:36 +04:00
goto port_inited ;
}
2006-10-06 19:06:39 +04:00
if ( pci_resource_flags ( pci , 2 ) & IORESOURCE_MEM ) /* ICH4 and Nforce */
chip - > addr = pci_iomap ( pci , 2 , 0 ) ;
else
chip - > addr = pci_iomap ( pci , 0 , 0 ) ;
if ( ! chip - > addr ) {
2014-02-25 17:40:45 +04:00
dev_err ( card - > dev , " AC'97 space ioremap problem \n " ) ;
2011-03-11 13:37:55 +03:00
snd_intel8x0m_free ( chip ) ;
2006-10-06 19:06:39 +04:00
return - EIO ;
2005-04-17 02:20:36 +04:00
}
2006-10-06 19:06:39 +04:00
if ( pci_resource_flags ( pci , 3 ) & IORESOURCE_MEM ) /* ICH4 */
chip - > bmaddr = pci_iomap ( pci , 3 , 0 ) ;
else
chip - > bmaddr = pci_iomap ( pci , 1 , 0 ) ;
if ( ! chip - > bmaddr ) {
2014-02-25 17:40:45 +04:00
dev_err ( card - > dev , " Controller space ioremap problem \n " ) ;
2011-03-11 13:37:55 +03:00
snd_intel8x0m_free ( chip ) ;
2006-10-06 19:06:39 +04:00
return - EIO ;
2005-04-17 02:20:36 +04:00
}
port_inited :
/* initialize offsets */
chip - > bdbars_count = 2 ;
tbl = intel_regs ;
for ( i = 0 ; i < chip - > bdbars_count ; i + + ) {
ichdev = & chip - > ichd [ i ] ;
ichdev - > ichd = i ;
ichdev - > reg_offset = tbl [ i ] . offset ;
ichdev - > int_sta_mask = tbl [ i ] . int_sta_mask ;
if ( device_type = = DEVICE_SIS ) {
/* SiS 7013 swaps the registers */
ichdev - > roff_sr = ICH_REG_OFF_PICB ;
ichdev - > roff_picb = ICH_REG_OFF_SR ;
} else {
ichdev - > roff_sr = ICH_REG_OFF_SR ;
ichdev - > roff_picb = ICH_REG_OFF_PICB ;
}
if ( device_type = = DEVICE_ALI )
ichdev - > ali_slot = ( ichdev - > reg_offset - 0x40 ) / 0x10 ;
}
/* SIS7013 handles the pcm data in bytes, others are in words */
chip - > pcm_pos_shift = ( device_type = = DEVICE_SIS ) ? 0 : 1 ;
/* allocate buffer descriptor lists */
/* the start of each lists must be aligned to 8 bytes */
if ( snd_dma_alloc_pages ( SNDRV_DMA_TYPE_DEV , snd_dma_pci_data ( pci ) ,
chip - > bdbars_count * sizeof ( u32 ) * ICH_MAX_FRAGS * 2 ,
& chip - > bdbars ) < 0 ) {
2011-03-11 13:37:55 +03:00
snd_intel8x0m_free ( chip ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
/* tables must be aligned to 8 bytes here, but the kernel pages
are much bigger , so we don ' t care ( on i386 ) */
int_sta_masks = 0 ;
for ( i = 0 ; i < chip - > bdbars_count ; i + + ) {
ichdev = & chip - > ichd [ i ] ;
2018-07-26 00:24:08 +03:00
ichdev - > bdbar = ( ( __le32 * ) chip - > bdbars . area ) + ( i * ICH_MAX_FRAGS * 2 ) ;
2005-04-17 02:20:36 +04:00
ichdev - > bdbar_addr = chip - > bdbars . addr + ( i * sizeof ( u32 ) * ICH_MAX_FRAGS * 2 ) ;
int_sta_masks | = ichdev - > int_sta_mask ;
}
chip - > int_sta_reg = ICH_REG_GLOB_STA ;
chip - > int_sta_mask = int_sta_masks ;
2018-08-28 17:39:10 +03:00
pci_set_master ( pci ) ;
2011-03-11 13:37:55 +03:00
if ( ( err = snd_intel8x0m_chip_init ( chip , 1 ) ) < 0 ) {
snd_intel8x0m_free ( chip ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2018-08-28 17:39:10 +03:00
if ( request_irq ( pci - > irq , snd_intel8x0m_interrupt , IRQF_SHARED ,
KBUILD_MODNAME , chip ) ) {
dev_err ( card - > dev , " unable to grab IRQ %d \n " , pci - > irq ) ;
snd_intel8x0m_free ( chip ) ;
return - EBUSY ;
}
chip - > irq = pci - > irq ;
2005-04-17 02:20:36 +04:00
if ( ( err = snd_device_new ( card , SNDRV_DEV_LOWLEVEL , chip , & ops ) ) < 0 ) {
2011-03-11 13:37:55 +03:00
snd_intel8x0m_free ( chip ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2011-03-11 13:41:06 +03:00
* r_intel8x0m = chip ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static struct shortname_table {
unsigned int id ;
const char * s ;
2012-12-06 21:35:10 +04:00
} shortnames [ ] = {
[ALSA] Remove superfluous PCI ID definitions
CS46xx driver,EMU10K1/EMU10K2 driver,PCM Midlevel,Trident driver
YMFPCI driver,BT87x driver,CMIPCI driver,CS4281 driver
ENS1370/1+ driver,ES1938 driver,ES1968 driver,Intel8x0 driver
Intel8x0-modem driver,Maestro3 driver,RME32 driver,RME96 driver
SonicVibes driver,VIA82xx driver,ALI5451 driver,ICE1712 driver
ICE1724 driver,NM256 driver,RME HDSP driver,RME9652 driver
Remove superfluous PCI ID definitions.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-09-07 16:08:11 +04:00
{ PCI_DEVICE_ID_INTEL_82801AA_6 , " Intel 82801AA-ICH " } ,
{ PCI_DEVICE_ID_INTEL_82801AB_6 , " Intel 82901AB-ICH0 " } ,
2005-04-17 02:20:36 +04:00
{ PCI_DEVICE_ID_INTEL_82801BA_6 , " Intel 82801BA-ICH2 " } ,
{ PCI_DEVICE_ID_INTEL_440MX_6 , " Intel 440MX " } ,
[ALSA] Remove superfluous PCI ID definitions
CS46xx driver,EMU10K1/EMU10K2 driver,PCM Midlevel,Trident driver
YMFPCI driver,BT87x driver,CMIPCI driver,CS4281 driver
ENS1370/1+ driver,ES1938 driver,ES1968 driver,Intel8x0 driver
Intel8x0-modem driver,Maestro3 driver,RME32 driver,RME96 driver
SonicVibes driver,VIA82xx driver,ALI5451 driver,ICE1712 driver
ICE1724 driver,NM256 driver,RME HDSP driver,RME9652 driver
Remove superfluous PCI ID definitions.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-09-07 16:08:11 +04:00
{ PCI_DEVICE_ID_INTEL_82801CA_6 , " Intel 82801CA-ICH3 " } ,
{ PCI_DEVICE_ID_INTEL_82801DB_6 , " Intel 82801DB-ICH4 " } ,
{ PCI_DEVICE_ID_INTEL_82801EB_6 , " Intel ICH5 " } ,
{ PCI_DEVICE_ID_INTEL_ICH6_17 , " Intel ICH6 " } ,
{ PCI_DEVICE_ID_INTEL_ICH7_19 , " Intel ICH7 " } ,
2005-04-17 02:20:36 +04:00
{ 0x7446 , " AMD AMD768 " } ,
{ PCI_DEVICE_ID_SI_7013 , " SiS SI7013 " } ,
[ALSA] Remove superfluous PCI ID definitions
CS46xx driver,EMU10K1/EMU10K2 driver,PCM Midlevel,Trident driver
YMFPCI driver,BT87x driver,CMIPCI driver,CS4281 driver
ENS1370/1+ driver,ES1938 driver,ES1968 driver,Intel8x0 driver
Intel8x0-modem driver,Maestro3 driver,RME32 driver,RME96 driver
SonicVibes driver,VIA82xx driver,ALI5451 driver,ICE1712 driver
ICE1724 driver,NM256 driver,RME HDSP driver,RME9652 driver
Remove superfluous PCI ID definitions.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-09-07 16:08:11 +04:00
{ PCI_DEVICE_ID_NVIDIA_MCP1_MODEM , " NVidia nForce " } ,
2005-04-17 02:20:36 +04:00
{ PCI_DEVICE_ID_NVIDIA_MCP2_MODEM , " NVidia nForce2 " } ,
{ PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM , " NVidia nForce2s " } ,
{ PCI_DEVICE_ID_NVIDIA_MCP3_MODEM , " NVidia nForce3 " } ,
2011-05-19 18:48:27 +04:00
{ 0x746e , " AMD AMD8111 " } ,
2005-04-17 02:20:36 +04:00
#if 0
{ 0x5455 , " ALi M5455 " } ,
# endif
{ 0 } ,
} ;
2012-12-06 21:35:10 +04:00
static int snd_intel8x0m_probe ( struct pci_dev * pci ,
const struct pci_device_id * pci_id )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:04:53 +03:00
struct snd_card * card ;
struct intel8x0m * chip ;
2005-04-17 02:20:36 +04:00
int err ;
struct shortname_table * name ;
2014-01-29 17:20:19 +04:00
err = snd_card_new ( & pci - > dev , index , id , THIS_MODULE , 0 , & card ) ;
2008-12-28 18:44:30 +03:00
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
strcpy ( card - > driver , " ICH-MODEM " ) ;
strcpy ( card - > shortname , " Intel ICH " ) ;
for ( name = shortnames ; name - > id ; name + + ) {
if ( pci - > device = = name - > id ) {
strcpy ( card - > shortname , name - > s ) ;
break ;
}
}
strcat ( card - > shortname , " Modem " ) ;
if ( ( err = snd_intel8x0m_create ( card , pci , pci_id - > driver_data , & chip ) ) < 0 ) {
snd_card_free ( card ) ;
return err ;
}
2005-11-17 18:10:01 +03:00
card - > private_data = chip ;
2005-04-17 02:20:36 +04:00
2011-03-11 13:37:55 +03:00
if ( ( err = snd_intel8x0m_mixer ( chip , ac97_clock ) ) < 0 ) {
2005-04-17 02:20:36 +04:00
snd_card_free ( card ) ;
return err ;
}
2011-03-11 13:37:55 +03:00
if ( ( err = snd_intel8x0m_pcm ( chip ) ) < 0 ) {
2005-04-17 02:20:36 +04:00
snd_card_free ( card ) ;
return err ;
}
snd_intel8x0m_proc_init ( chip ) ;
2006-10-06 19:06:39 +04:00
sprintf ( card - > longname , " %s at irq %i " ,
card - > shortname , chip - > irq ) ;
2005-04-17 02:20:36 +04:00
if ( ( err = snd_card_register ( card ) ) < 0 ) {
snd_card_free ( card ) ;
return err ;
}
pci_set_drvdata ( pci , card ) ;
return 0 ;
}
2012-12-06 21:35:10 +04:00
static void snd_intel8x0m_remove ( struct pci_dev * pci )
2005-04-17 02:20:36 +04:00
{
snd_card_free ( pci_get_drvdata ( pci ) ) ;
}
2012-04-24 14:25:00 +04:00
static struct pci_driver intel8x0m_driver = {
2011-06-10 18:20:20 +04:00
. name = KBUILD_MODNAME ,
2005-04-17 02:20:36 +04:00
. id_table = snd_intel8x0m_ids ,
. probe = snd_intel8x0m_probe ,
2012-12-06 21:35:10 +04:00
. remove = snd_intel8x0m_remove ,
2012-07-02 17:20:37 +04:00
. driver = {
. pm = INTEL8X0M_PM_OPS ,
} ,
2005-04-17 02:20:36 +04:00
} ;
2012-04-24 14:25:00 +04:00
module_pci_driver ( intel8x0m_driver ) ;