162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for C-Media's CMI8330 and CMI8329 soundcards. 462306a36Sopenharmony_ci * Copyright (c) by George Talusan <gstalusan@uwaterloo.ca> 562306a36Sopenharmony_ci * http://www.undergrad.math.uwaterloo.ca/~gstalusa 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * NOTES 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * The extended registers contain mixer settings which are largely 1262306a36Sopenharmony_ci * untapped for the time being. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * MPU401 and SPDIF are not supported yet. I don't have the hardware 1562306a36Sopenharmony_ci * to aid in coding and testing, so I won't bother. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * To quickly load the module, 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * modprobe -a snd-cmi8330 sbport=0x220 sbirq=5 sbdma8=1 2062306a36Sopenharmony_ci * sbdma16=5 wssport=0x530 wssirq=11 wssdma=0 fmport=0x388 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * This card has two mixers and two PCM devices. I've cheesed it such 2362306a36Sopenharmony_ci * that recording and playback can be done through the same device. 2462306a36Sopenharmony_ci * The driver "magically" routes the capturing to the AD1848 codec, 2562306a36Sopenharmony_ci * and playback to the SB16 codec. This allows for full-duplex mode 2662306a36Sopenharmony_ci * to some extent. 2762306a36Sopenharmony_ci * The utilities in alsa-utils are aware of both devices, so passing 2862306a36Sopenharmony_ci * the appropriate parameters to amixer and alsactl will give you 2962306a36Sopenharmony_ci * full control over both mixers. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <linux/init.h> 3362306a36Sopenharmony_ci#include <linux/err.h> 3462306a36Sopenharmony_ci#include <linux/isa.h> 3562306a36Sopenharmony_ci#include <linux/pnp.h> 3662306a36Sopenharmony_ci#include <linux/module.h> 3762306a36Sopenharmony_ci#include <sound/core.h> 3862306a36Sopenharmony_ci#include <sound/wss.h> 3962306a36Sopenharmony_ci#include <sound/opl3.h> 4062306a36Sopenharmony_ci#include <sound/mpu401.h> 4162306a36Sopenharmony_ci#include <sound/sb.h> 4262306a36Sopenharmony_ci#include <sound/initval.h> 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci/* #define ENABLE_SB_MIXER */ 4762306a36Sopenharmony_ci#define PLAYBACK_ON_SB 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ciMODULE_AUTHOR("George Talusan <gstalusan@uwaterloo.ca>"); 5262306a36Sopenharmony_ciMODULE_DESCRIPTION("C-Media CMI8330/CMI8329"); 5362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 5662306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 5762306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; 5862306a36Sopenharmony_ci#ifdef CONFIG_PNP 5962306a36Sopenharmony_cistatic bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; 6062306a36Sopenharmony_ci#endif 6162306a36Sopenharmony_cistatic long sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; 6262306a36Sopenharmony_cistatic int sbirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; 6362306a36Sopenharmony_cistatic int sbdma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; 6462306a36Sopenharmony_cistatic int sbdma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; 6562306a36Sopenharmony_cistatic long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; 6662306a36Sopenharmony_cistatic int wssirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; 6762306a36Sopenharmony_cistatic int wssdma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; 6862306a36Sopenharmony_cistatic long fmport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; 6962306a36Sopenharmony_cistatic long mpuport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; 7062306a36Sopenharmony_cistatic int mpuirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 7362306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for CMI8330/CMI8329 soundcard."); 7462306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 7562306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for CMI8330/CMI8329 soundcard."); 7662306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 7762306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable CMI8330/CMI8329 soundcard."); 7862306a36Sopenharmony_ci#ifdef CONFIG_PNP 7962306a36Sopenharmony_cimodule_param_array(isapnp, bool, NULL, 0444); 8062306a36Sopenharmony_ciMODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard."); 8162306a36Sopenharmony_ci#endif 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cimodule_param_hw_array(sbport, long, ioport, NULL, 0444); 8462306a36Sopenharmony_ciMODULE_PARM_DESC(sbport, "Port # for CMI8330/CMI8329 SB driver."); 8562306a36Sopenharmony_cimodule_param_hw_array(sbirq, int, irq, NULL, 0444); 8662306a36Sopenharmony_ciMODULE_PARM_DESC(sbirq, "IRQ # for CMI8330/CMI8329 SB driver."); 8762306a36Sopenharmony_cimodule_param_hw_array(sbdma8, int, dma, NULL, 0444); 8862306a36Sopenharmony_ciMODULE_PARM_DESC(sbdma8, "DMA8 for CMI8330/CMI8329 SB driver."); 8962306a36Sopenharmony_cimodule_param_hw_array(sbdma16, int, dma, NULL, 0444); 9062306a36Sopenharmony_ciMODULE_PARM_DESC(sbdma16, "DMA16 for CMI8330/CMI8329 SB driver."); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cimodule_param_hw_array(wssport, long, ioport, NULL, 0444); 9362306a36Sopenharmony_ciMODULE_PARM_DESC(wssport, "Port # for CMI8330/CMI8329 WSS driver."); 9462306a36Sopenharmony_cimodule_param_hw_array(wssirq, int, irq, NULL, 0444); 9562306a36Sopenharmony_ciMODULE_PARM_DESC(wssirq, "IRQ # for CMI8330/CMI8329 WSS driver."); 9662306a36Sopenharmony_cimodule_param_hw_array(wssdma, int, dma, NULL, 0444); 9762306a36Sopenharmony_ciMODULE_PARM_DESC(wssdma, "DMA for CMI8330/CMI8329 WSS driver."); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cimodule_param_hw_array(fmport, long, ioport, NULL, 0444); 10062306a36Sopenharmony_ciMODULE_PARM_DESC(fmport, "FM port # for CMI8330/CMI8329 driver."); 10162306a36Sopenharmony_cimodule_param_hw_array(mpuport, long, ioport, NULL, 0444); 10262306a36Sopenharmony_ciMODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8330/CMI8329 driver."); 10362306a36Sopenharmony_cimodule_param_hw_array(mpuirq, int, irq, NULL, 0444); 10462306a36Sopenharmony_ciMODULE_PARM_DESC(mpuirq, "IRQ # for CMI8330/CMI8329 MPU-401 port."); 10562306a36Sopenharmony_ci#ifdef CONFIG_PNP 10662306a36Sopenharmony_cistatic int isa_registered; 10762306a36Sopenharmony_cistatic int pnp_registered; 10862306a36Sopenharmony_ci#endif 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#define CMI8330_RMUX3D 16 11162306a36Sopenharmony_ci#define CMI8330_MUTEMUX 17 11262306a36Sopenharmony_ci#define CMI8330_OUTPUTVOL 18 11362306a36Sopenharmony_ci#define CMI8330_MASTVOL 19 11462306a36Sopenharmony_ci#define CMI8330_LINVOL 20 11562306a36Sopenharmony_ci#define CMI8330_CDINVOL 21 11662306a36Sopenharmony_ci#define CMI8330_WAVVOL 22 11762306a36Sopenharmony_ci#define CMI8330_RECMUX 23 11862306a36Sopenharmony_ci#define CMI8330_WAVGAIN 24 11962306a36Sopenharmony_ci#define CMI8330_LINGAIN 25 12062306a36Sopenharmony_ci#define CMI8330_CDINGAIN 26 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic const unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] = 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci 0x40, /* 16 - recording mux (SB-mixer-enabled) */ 12562306a36Sopenharmony_ci#ifdef ENABLE_SB_MIXER 12662306a36Sopenharmony_ci 0x40, /* 17 - mute mux (Mode2) */ 12762306a36Sopenharmony_ci#else 12862306a36Sopenharmony_ci 0x0, /* 17 - mute mux */ 12962306a36Sopenharmony_ci#endif 13062306a36Sopenharmony_ci 0x0, /* 18 - vol */ 13162306a36Sopenharmony_ci 0x0, /* 19 - master volume */ 13262306a36Sopenharmony_ci 0x0, /* 20 - line-in volume */ 13362306a36Sopenharmony_ci 0x0, /* 21 - cd-in volume */ 13462306a36Sopenharmony_ci 0x0, /* 22 - wave volume */ 13562306a36Sopenharmony_ci 0x0, /* 23 - mute/rec mux */ 13662306a36Sopenharmony_ci 0x0, /* 24 - wave rec gain */ 13762306a36Sopenharmony_ci 0x0, /* 25 - line-in rec gain */ 13862306a36Sopenharmony_ci 0x0 /* 26 - cd-in rec gain */ 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_citypedef int (*snd_pcm_open_callback_t)(struct snd_pcm_substream *); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cienum card_type { 14462306a36Sopenharmony_ci CMI8330, 14562306a36Sopenharmony_ci CMI8329 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistruct snd_cmi8330 { 14962306a36Sopenharmony_ci#ifdef CONFIG_PNP 15062306a36Sopenharmony_ci struct pnp_dev *cap; 15162306a36Sopenharmony_ci struct pnp_dev *play; 15262306a36Sopenharmony_ci struct pnp_dev *mpu; 15362306a36Sopenharmony_ci#endif 15462306a36Sopenharmony_ci struct snd_card *card; 15562306a36Sopenharmony_ci struct snd_wss *wss; 15662306a36Sopenharmony_ci struct snd_sb *sb; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci struct snd_pcm *pcm; 15962306a36Sopenharmony_ci struct snd_cmi8330_stream { 16062306a36Sopenharmony_ci struct snd_pcm_ops ops; 16162306a36Sopenharmony_ci snd_pcm_open_callback_t open; 16262306a36Sopenharmony_ci void *private_data; /* sb or wss */ 16362306a36Sopenharmony_ci } streams[2]; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci enum card_type type; 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci#ifdef CONFIG_PNP 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic const struct pnp_card_device_id snd_cmi8330_pnpids[] = { 17162306a36Sopenharmony_ci { .id = "CMI0001", .devs = { { "@X@0001" }, { "@@@0001" }, { "@H@0001" }, { "A@@0001" } } }, 17262306a36Sopenharmony_ci { .id = "CMI0001", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } } }, 17362306a36Sopenharmony_ci { .id = "" } 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, snd_cmi8330_pnpids); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci#endif 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_cmi8330_controls[] = { 18262306a36Sopenharmony_ciWSS_DOUBLE("Master Playback Volume", 0, 18362306a36Sopenharmony_ci CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0), 18462306a36Sopenharmony_ciWSS_SINGLE("Loud Playback Switch", 0, 18562306a36Sopenharmony_ci CMI8330_MUTEMUX, 6, 1, 1), 18662306a36Sopenharmony_ciWSS_DOUBLE("PCM Playback Switch", 0, 18762306a36Sopenharmony_ci CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), 18862306a36Sopenharmony_ciWSS_DOUBLE("PCM Playback Volume", 0, 18962306a36Sopenharmony_ci CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), 19062306a36Sopenharmony_ciWSS_DOUBLE("Line Playback Switch", 0, 19162306a36Sopenharmony_ci CMI8330_MUTEMUX, CMI8330_MUTEMUX, 4, 3, 1, 0), 19262306a36Sopenharmony_ciWSS_DOUBLE("Line Playback Volume", 0, 19362306a36Sopenharmony_ci CMI8330_LINVOL, CMI8330_LINVOL, 4, 0, 15, 0), 19462306a36Sopenharmony_ciWSS_DOUBLE("Line Capture Switch", 0, 19562306a36Sopenharmony_ci CMI8330_RMUX3D, CMI8330_RMUX3D, 2, 1, 1, 0), 19662306a36Sopenharmony_ciWSS_DOUBLE("Line Capture Volume", 0, 19762306a36Sopenharmony_ci CMI8330_LINGAIN, CMI8330_LINGAIN, 4, 0, 15, 0), 19862306a36Sopenharmony_ciWSS_DOUBLE("CD Playback Switch", 0, 19962306a36Sopenharmony_ci CMI8330_MUTEMUX, CMI8330_MUTEMUX, 2, 1, 1, 0), 20062306a36Sopenharmony_ciWSS_DOUBLE("CD Capture Switch", 0, 20162306a36Sopenharmony_ci CMI8330_RMUX3D, CMI8330_RMUX3D, 4, 3, 1, 0), 20262306a36Sopenharmony_ciWSS_DOUBLE("CD Playback Volume", 0, 20362306a36Sopenharmony_ci CMI8330_CDINVOL, CMI8330_CDINVOL, 4, 0, 15, 0), 20462306a36Sopenharmony_ciWSS_DOUBLE("CD Capture Volume", 0, 20562306a36Sopenharmony_ci CMI8330_CDINGAIN, CMI8330_CDINGAIN, 4, 0, 15, 0), 20662306a36Sopenharmony_ciWSS_SINGLE("Mic Playback Switch", 0, 20762306a36Sopenharmony_ci CMI8330_MUTEMUX, 0, 1, 0), 20862306a36Sopenharmony_ciWSS_SINGLE("Mic Playback Volume", 0, 20962306a36Sopenharmony_ci CMI8330_OUTPUTVOL, 0, 7, 0), 21062306a36Sopenharmony_ciWSS_SINGLE("Mic Capture Switch", 0, 21162306a36Sopenharmony_ci CMI8330_RMUX3D, 0, 1, 0), 21262306a36Sopenharmony_ciWSS_SINGLE("Mic Capture Volume", 0, 21362306a36Sopenharmony_ci CMI8330_OUTPUTVOL, 5, 7, 0), 21462306a36Sopenharmony_ciWSS_DOUBLE("Wavetable Playback Switch", 0, 21562306a36Sopenharmony_ci CMI8330_RECMUX, CMI8330_RECMUX, 1, 0, 1, 0), 21662306a36Sopenharmony_ciWSS_DOUBLE("Wavetable Playback Volume", 0, 21762306a36Sopenharmony_ci CMI8330_WAVVOL, CMI8330_WAVVOL, 4, 0, 15, 0), 21862306a36Sopenharmony_ciWSS_DOUBLE("Wavetable Capture Switch", 0, 21962306a36Sopenharmony_ci CMI8330_RECMUX, CMI8330_RECMUX, 5, 4, 1, 0), 22062306a36Sopenharmony_ciWSS_DOUBLE("Wavetable Capture Volume", 0, 22162306a36Sopenharmony_ci CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0), 22262306a36Sopenharmony_ciWSS_SINGLE("3D Control - Switch", 0, 22362306a36Sopenharmony_ci CMI8330_RMUX3D, 5, 1, 1), 22462306a36Sopenharmony_ciWSS_SINGLE("Beep Playback Volume", 0, 22562306a36Sopenharmony_ci CMI8330_OUTPUTVOL, 3, 3, 0), 22662306a36Sopenharmony_ciWSS_DOUBLE("FM Playback Switch", 0, 22762306a36Sopenharmony_ci CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), 22862306a36Sopenharmony_ciWSS_DOUBLE("FM Playback Volume", 0, 22962306a36Sopenharmony_ci CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), 23062306a36Sopenharmony_ciWSS_SINGLE(SNDRV_CTL_NAME_IEC958("Input ", CAPTURE, SWITCH), 0, 23162306a36Sopenharmony_ci CMI8330_RMUX3D, 7, 1, 1), 23262306a36Sopenharmony_ciWSS_SINGLE(SNDRV_CTL_NAME_IEC958("Input ", PLAYBACK, SWITCH), 0, 23362306a36Sopenharmony_ci CMI8330_MUTEMUX, 7, 1, 1), 23462306a36Sopenharmony_ci}; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci#ifdef ENABLE_SB_MIXER 23762306a36Sopenharmony_cistatic const struct sbmix_elem cmi8330_sb_mixers[] = { 23862306a36Sopenharmony_ciSB_DOUBLE("SB Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31), 23962306a36Sopenharmony_ciSB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15), 24062306a36Sopenharmony_ciSB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15), 24162306a36Sopenharmony_ciSB_DOUBLE("SB PCM Playback Volume", SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31), 24262306a36Sopenharmony_ciSB_DOUBLE("SB Synth Playback Volume", SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31), 24362306a36Sopenharmony_ciSB_DOUBLE("SB CD Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1), 24462306a36Sopenharmony_ciSB_DOUBLE("SB CD Playback Volume", SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31), 24562306a36Sopenharmony_ciSB_DOUBLE("SB Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1), 24662306a36Sopenharmony_ciSB_DOUBLE("SB Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31), 24762306a36Sopenharmony_ciSB_SINGLE("SB Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1), 24862306a36Sopenharmony_ciSB_SINGLE("SB Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31), 24962306a36Sopenharmony_ciSB_SINGLE("SB Beep Volume", SB_DSP4_SPEAKER_DEV, 6, 3), 25062306a36Sopenharmony_ciSB_DOUBLE("SB Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3), 25162306a36Sopenharmony_ciSB_DOUBLE("SB Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3), 25262306a36Sopenharmony_ciSB_SINGLE("SB Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1), 25362306a36Sopenharmony_ci}; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic const unsigned char cmi8330_sb_init_values[][2] = { 25662306a36Sopenharmony_ci { SB_DSP4_MASTER_DEV + 0, 0 }, 25762306a36Sopenharmony_ci { SB_DSP4_MASTER_DEV + 1, 0 }, 25862306a36Sopenharmony_ci { SB_DSP4_PCM_DEV + 0, 0 }, 25962306a36Sopenharmony_ci { SB_DSP4_PCM_DEV + 1, 0 }, 26062306a36Sopenharmony_ci { SB_DSP4_SYNTH_DEV + 0, 0 }, 26162306a36Sopenharmony_ci { SB_DSP4_SYNTH_DEV + 1, 0 }, 26262306a36Sopenharmony_ci { SB_DSP4_INPUT_LEFT, 0 }, 26362306a36Sopenharmony_ci { SB_DSP4_INPUT_RIGHT, 0 }, 26462306a36Sopenharmony_ci { SB_DSP4_OUTPUT_SW, 0 }, 26562306a36Sopenharmony_ci { SB_DSP4_SPEAKER_DEV, 0 }, 26662306a36Sopenharmony_ci}; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int cmi8330_add_sb_mixers(struct snd_sb *chip) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci int idx, err; 27262306a36Sopenharmony_ci unsigned long flags; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci spin_lock_irqsave(&chip->mixer_lock, flags); 27562306a36Sopenharmony_ci snd_sbmixer_write(chip, 0x00, 0x00); /* mixer reset */ 27662306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->mixer_lock, flags); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* mute and zero volume channels */ 27962306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(cmi8330_sb_init_values); idx++) { 28062306a36Sopenharmony_ci spin_lock_irqsave(&chip->mixer_lock, flags); 28162306a36Sopenharmony_ci snd_sbmixer_write(chip, cmi8330_sb_init_values[idx][0], 28262306a36Sopenharmony_ci cmi8330_sb_init_values[idx][1]); 28362306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->mixer_lock, flags); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(cmi8330_sb_mixers); idx++) { 28762306a36Sopenharmony_ci err = snd_sbmixer_add_ctl_elem(chip, &cmi8330_sb_mixers[idx]); 28862306a36Sopenharmony_ci if (err < 0) 28962306a36Sopenharmony_ci return err; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci return 0; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci#endif 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic int snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330 *acard) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci unsigned int idx; 29862306a36Sopenharmony_ci int err; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci strcpy(card->mixername, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D"); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_cmi8330_controls); idx++) { 30362306a36Sopenharmony_ci err = snd_ctl_add(card, 30462306a36Sopenharmony_ci snd_ctl_new1(&snd_cmi8330_controls[idx], 30562306a36Sopenharmony_ci acard->wss)); 30662306a36Sopenharmony_ci if (err < 0) 30762306a36Sopenharmony_ci return err; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci#ifdef ENABLE_SB_MIXER 31162306a36Sopenharmony_ci err = cmi8330_add_sb_mixers(acard->sb); 31262306a36Sopenharmony_ci if (err < 0) 31362306a36Sopenharmony_ci return err; 31462306a36Sopenharmony_ci#endif 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci#ifdef CONFIG_PNP 31962306a36Sopenharmony_cistatic int snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard, 32062306a36Sopenharmony_ci struct pnp_card_link *card, 32162306a36Sopenharmony_ci const struct pnp_card_device_id *id) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct pnp_dev *pdev; 32462306a36Sopenharmony_ci int err; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* CMI8329 has a device with ID A@@0001, CMI8330 does not */ 32762306a36Sopenharmony_ci acard->type = (id->devs[3].id[0]) ? CMI8329 : CMI8330; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci acard->cap = pnp_request_card_device(card, id->devs[0].id, NULL); 33062306a36Sopenharmony_ci if (acard->cap == NULL) 33162306a36Sopenharmony_ci return -EBUSY; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci acard->play = pnp_request_card_device(card, id->devs[1].id, NULL); 33462306a36Sopenharmony_ci if (acard->play == NULL) 33562306a36Sopenharmony_ci return -EBUSY; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci acard->mpu = pnp_request_card_device(card, id->devs[2].id, NULL); 33862306a36Sopenharmony_ci if (acard->mpu == NULL) 33962306a36Sopenharmony_ci return -EBUSY; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci pdev = acard->cap; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci err = pnp_activate_dev(pdev); 34462306a36Sopenharmony_ci if (err < 0) { 34562306a36Sopenharmony_ci snd_printk(KERN_ERR "AD1848 PnP configure failure\n"); 34662306a36Sopenharmony_ci return -EBUSY; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci wssport[dev] = pnp_port_start(pdev, 0); 34962306a36Sopenharmony_ci wssdma[dev] = pnp_dma(pdev, 0); 35062306a36Sopenharmony_ci wssirq[dev] = pnp_irq(pdev, 0); 35162306a36Sopenharmony_ci if (pnp_port_start(pdev, 1)) 35262306a36Sopenharmony_ci fmport[dev] = pnp_port_start(pdev, 1); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* allocate SB16 resources */ 35562306a36Sopenharmony_ci pdev = acard->play; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci err = pnp_activate_dev(pdev); 35862306a36Sopenharmony_ci if (err < 0) { 35962306a36Sopenharmony_ci snd_printk(KERN_ERR "SB16 PnP configure failure\n"); 36062306a36Sopenharmony_ci return -EBUSY; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci sbport[dev] = pnp_port_start(pdev, 0); 36362306a36Sopenharmony_ci sbdma8[dev] = pnp_dma(pdev, 0); 36462306a36Sopenharmony_ci sbdma16[dev] = pnp_dma(pdev, 1); 36562306a36Sopenharmony_ci sbirq[dev] = pnp_irq(pdev, 0); 36662306a36Sopenharmony_ci /* On CMI8239, the OPL3 port might be present in SB16 PnP resources */ 36762306a36Sopenharmony_ci if (fmport[dev] == SNDRV_AUTO_PORT) { 36862306a36Sopenharmony_ci if (pnp_port_start(pdev, 1)) 36962306a36Sopenharmony_ci fmport[dev] = pnp_port_start(pdev, 1); 37062306a36Sopenharmony_ci else 37162306a36Sopenharmony_ci fmport[dev] = 0x388; /* Or hardwired */ 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* allocate MPU-401 resources */ 37562306a36Sopenharmony_ci pdev = acard->mpu; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci err = pnp_activate_dev(pdev); 37862306a36Sopenharmony_ci if (err < 0) 37962306a36Sopenharmony_ci snd_printk(KERN_ERR "MPU-401 PnP configure failure: will be disabled\n"); 38062306a36Sopenharmony_ci else { 38162306a36Sopenharmony_ci mpuport[dev] = pnp_port_start(pdev, 0); 38262306a36Sopenharmony_ci mpuirq[dev] = pnp_irq(pdev, 0); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci return 0; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci#endif 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci/* 38962306a36Sopenharmony_ci * PCM interface 39062306a36Sopenharmony_ci * 39162306a36Sopenharmony_ci * since we call the different chip interfaces for playback and capture 39262306a36Sopenharmony_ci * directions, we need a trick. 39362306a36Sopenharmony_ci * 39462306a36Sopenharmony_ci * - copy the ops for each direction into a local record. 39562306a36Sopenharmony_ci * - replace the open callback with the new one, which replaces the 39662306a36Sopenharmony_ci * substream->private_data with the corresponding chip instance 39762306a36Sopenharmony_ci * and calls again the original open callback of the chip. 39862306a36Sopenharmony_ci * 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci#ifdef PLAYBACK_ON_SB 40262306a36Sopenharmony_ci#define CMI_SB_STREAM SNDRV_PCM_STREAM_PLAYBACK 40362306a36Sopenharmony_ci#define CMI_AD_STREAM SNDRV_PCM_STREAM_CAPTURE 40462306a36Sopenharmony_ci#else 40562306a36Sopenharmony_ci#define CMI_SB_STREAM SNDRV_PCM_STREAM_CAPTURE 40662306a36Sopenharmony_ci#define CMI_AD_STREAM SNDRV_PCM_STREAM_PLAYBACK 40762306a36Sopenharmony_ci#endif 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic int snd_cmi8330_playback_open(struct snd_pcm_substream *substream) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct snd_cmi8330 *chip = snd_pcm_substream_chip(substream); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* replace the private_data and call the original open callback */ 41462306a36Sopenharmony_ci substream->private_data = chip->streams[SNDRV_PCM_STREAM_PLAYBACK].private_data; 41562306a36Sopenharmony_ci return chip->streams[SNDRV_PCM_STREAM_PLAYBACK].open(substream); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic int snd_cmi8330_capture_open(struct snd_pcm_substream *substream) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct snd_cmi8330 *chip = snd_pcm_substream_chip(substream); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* replace the private_data and call the original open callback */ 42362306a36Sopenharmony_ci substream->private_data = chip->streams[SNDRV_PCM_STREAM_CAPTURE].private_data; 42462306a36Sopenharmony_ci return chip->streams[SNDRV_PCM_STREAM_CAPTURE].open(substream); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 *chip) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct snd_pcm *pcm; 43062306a36Sopenharmony_ci const struct snd_pcm_ops *ops; 43162306a36Sopenharmony_ci int err; 43262306a36Sopenharmony_ci static const snd_pcm_open_callback_t cmi_open_callbacks[2] = { 43362306a36Sopenharmony_ci snd_cmi8330_playback_open, 43462306a36Sopenharmony_ci snd_cmi8330_capture_open 43562306a36Sopenharmony_ci }; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci err = snd_pcm_new(card, (chip->type == CMI8329) ? "CMI8329" : "CMI8330", 0, 1, 1, &pcm); 43862306a36Sopenharmony_ci if (err < 0) 43962306a36Sopenharmony_ci return err; 44062306a36Sopenharmony_ci strcpy(pcm->name, (chip->type == CMI8329) ? "CMI8329" : "CMI8330"); 44162306a36Sopenharmony_ci pcm->private_data = chip; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* SB16 */ 44462306a36Sopenharmony_ci ops = snd_sb16dsp_get_pcm_ops(CMI_SB_STREAM); 44562306a36Sopenharmony_ci chip->streams[CMI_SB_STREAM].ops = *ops; 44662306a36Sopenharmony_ci chip->streams[CMI_SB_STREAM].open = ops->open; 44762306a36Sopenharmony_ci chip->streams[CMI_SB_STREAM].ops.open = cmi_open_callbacks[CMI_SB_STREAM]; 44862306a36Sopenharmony_ci chip->streams[CMI_SB_STREAM].private_data = chip->sb; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* AD1848 */ 45162306a36Sopenharmony_ci ops = snd_wss_get_pcm_ops(CMI_AD_STREAM); 45262306a36Sopenharmony_ci chip->streams[CMI_AD_STREAM].ops = *ops; 45362306a36Sopenharmony_ci chip->streams[CMI_AD_STREAM].open = ops->open; 45462306a36Sopenharmony_ci chip->streams[CMI_AD_STREAM].ops.open = cmi_open_callbacks[CMI_AD_STREAM]; 45562306a36Sopenharmony_ci chip->streams[CMI_AD_STREAM].private_data = chip->wss; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK].ops); 45862306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &chip->streams[SNDRV_PCM_STREAM_CAPTURE].ops); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 46162306a36Sopenharmony_ci card->dev, 64*1024, 128*1024); 46262306a36Sopenharmony_ci chip->pcm = pcm; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci#ifdef CONFIG_PM 46962306a36Sopenharmony_cistatic int snd_cmi8330_suspend(struct snd_card *card) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci struct snd_cmi8330 *acard = card->private_data; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 47462306a36Sopenharmony_ci acard->wss->suspend(acard->wss); 47562306a36Sopenharmony_ci snd_sbmixer_suspend(acard->sb); 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic int snd_cmi8330_resume(struct snd_card *card) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct snd_cmi8330 *acard = card->private_data; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci snd_sbdsp_reset(acard->sb); 48462306a36Sopenharmony_ci snd_sbmixer_suspend(acard->sb); 48562306a36Sopenharmony_ci acard->wss->resume(acard->wss); 48662306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 48762306a36Sopenharmony_ci return 0; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci#endif 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci/* 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci#ifdef CONFIG_PNP 49662306a36Sopenharmony_ci#define is_isapnp_selected(dev) isapnp[dev] 49762306a36Sopenharmony_ci#else 49862306a36Sopenharmony_ci#define is_isapnp_selected(dev) 0 49962306a36Sopenharmony_ci#endif 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci#define PFX "cmi8330: " 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic int snd_cmi8330_card_new(struct device *pdev, int dev, 50462306a36Sopenharmony_ci struct snd_card **cardp) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct snd_card *card; 50762306a36Sopenharmony_ci struct snd_cmi8330 *acard; 50862306a36Sopenharmony_ci int err; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE, 51162306a36Sopenharmony_ci sizeof(struct snd_cmi8330), &card); 51262306a36Sopenharmony_ci if (err < 0) { 51362306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "could not get a new card\n"); 51462306a36Sopenharmony_ci return err; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci acard = card->private_data; 51762306a36Sopenharmony_ci acard->card = card; 51862306a36Sopenharmony_ci *cardp = card; 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic int snd_cmi8330_probe(struct snd_card *card, int dev) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct snd_cmi8330 *acard; 52562306a36Sopenharmony_ci int i, err; 52662306a36Sopenharmony_ci struct snd_opl3 *opl3; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci acard = card->private_data; 52962306a36Sopenharmony_ci err = snd_wss_create(card, wssport[dev] + 4, -1, 53062306a36Sopenharmony_ci wssirq[dev], 53162306a36Sopenharmony_ci wssdma[dev], -1, 53262306a36Sopenharmony_ci WSS_HW_DETECT, 0, &acard->wss); 53362306a36Sopenharmony_ci if (err < 0) { 53462306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "AD1848 device busy??\n"); 53562306a36Sopenharmony_ci return err; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci if (acard->wss->hardware != WSS_HW_CMI8330) { 53862306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "AD1848 not found during probe\n"); 53962306a36Sopenharmony_ci return -ENODEV; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci err = snd_sbdsp_create(card, sbport[dev], 54362306a36Sopenharmony_ci sbirq[dev], 54462306a36Sopenharmony_ci snd_sb16dsp_interrupt, 54562306a36Sopenharmony_ci sbdma8[dev], 54662306a36Sopenharmony_ci sbdma16[dev], 54762306a36Sopenharmony_ci SB_HW_AUTO, &acard->sb); 54862306a36Sopenharmony_ci if (err < 0) { 54962306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "SB16 device busy??\n"); 55062306a36Sopenharmony_ci return err; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci if (acard->sb->hardware != SB_HW_16) { 55362306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "SB16 not found during probe\n"); 55462306a36Sopenharmony_ci return -ENODEV; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci snd_wss_out(acard->wss, CS4231_MISC_INFO, 0x40); /* switch on MODE2 */ 55862306a36Sopenharmony_ci for (i = CMI8330_RMUX3D; i <= CMI8330_CDINGAIN; i++) 55962306a36Sopenharmony_ci snd_wss_out(acard->wss, i, 56062306a36Sopenharmony_ci snd_cmi8330_image[i - CMI8330_RMUX3D]); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci err = snd_cmi8330_mixer(card, acard); 56362306a36Sopenharmony_ci if (err < 0) { 56462306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "failed to create mixers\n"); 56562306a36Sopenharmony_ci return err; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci err = snd_cmi8330_pcm(card, acard); 56962306a36Sopenharmony_ci if (err < 0) { 57062306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "failed to create pcms\n"); 57162306a36Sopenharmony_ci return err; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci if (fmport[dev] != SNDRV_AUTO_PORT) { 57462306a36Sopenharmony_ci if (snd_opl3_create(card, 57562306a36Sopenharmony_ci fmport[dev], fmport[dev] + 2, 57662306a36Sopenharmony_ci OPL3_HW_AUTO, 0, &opl3) < 0) { 57762306a36Sopenharmony_ci snd_printk(KERN_ERR PFX 57862306a36Sopenharmony_ci "no OPL device at 0x%lx-0x%lx ?\n", 57962306a36Sopenharmony_ci fmport[dev], fmport[dev] + 2); 58062306a36Sopenharmony_ci } else { 58162306a36Sopenharmony_ci err = snd_opl3_hwdep_new(opl3, 0, 1, NULL); 58262306a36Sopenharmony_ci if (err < 0) 58362306a36Sopenharmony_ci return err; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (mpuport[dev] != SNDRV_AUTO_PORT) { 58862306a36Sopenharmony_ci if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, 58962306a36Sopenharmony_ci mpuport[dev], 0, mpuirq[dev], 59062306a36Sopenharmony_ci NULL) < 0) 59162306a36Sopenharmony_ci printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", 59262306a36Sopenharmony_ci mpuport[dev]); 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci strcpy(card->driver, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D"); 59662306a36Sopenharmony_ci strcpy(card->shortname, (acard->type == CMI8329) ? "C-Media CMI8329" : "C-Media CMI8330/C3D"); 59762306a36Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", 59862306a36Sopenharmony_ci card->shortname, 59962306a36Sopenharmony_ci acard->wss->port, 60062306a36Sopenharmony_ci wssirq[dev], 60162306a36Sopenharmony_ci wssdma[dev]); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci return snd_card_register(card); 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic int snd_cmi8330_isa_match(struct device *pdev, 60762306a36Sopenharmony_ci unsigned int dev) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci if (!enable[dev] || is_isapnp_selected(dev)) 61062306a36Sopenharmony_ci return 0; 61162306a36Sopenharmony_ci if (wssport[dev] == SNDRV_AUTO_PORT) { 61262306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "specify wssport\n"); 61362306a36Sopenharmony_ci return 0; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci if (sbport[dev] == SNDRV_AUTO_PORT) { 61662306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "specify sbport\n"); 61762306a36Sopenharmony_ci return 0; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci return 1; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic int snd_cmi8330_isa_probe(struct device *pdev, 62362306a36Sopenharmony_ci unsigned int dev) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct snd_card *card; 62662306a36Sopenharmony_ci int err; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci err = snd_cmi8330_card_new(pdev, dev, &card); 62962306a36Sopenharmony_ci if (err < 0) 63062306a36Sopenharmony_ci return err; 63162306a36Sopenharmony_ci err = snd_cmi8330_probe(card, dev); 63262306a36Sopenharmony_ci if (err < 0) 63362306a36Sopenharmony_ci return err; 63462306a36Sopenharmony_ci dev_set_drvdata(pdev, card); 63562306a36Sopenharmony_ci return 0; 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci#ifdef CONFIG_PM 63962306a36Sopenharmony_cistatic int snd_cmi8330_isa_suspend(struct device *dev, unsigned int n, 64062306a36Sopenharmony_ci pm_message_t state) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci return snd_cmi8330_suspend(dev_get_drvdata(dev)); 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic int snd_cmi8330_isa_resume(struct device *dev, unsigned int n) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci return snd_cmi8330_resume(dev_get_drvdata(dev)); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci#endif 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci#define DEV_NAME "cmi8330" 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic struct isa_driver snd_cmi8330_driver = { 65462306a36Sopenharmony_ci .match = snd_cmi8330_isa_match, 65562306a36Sopenharmony_ci .probe = snd_cmi8330_isa_probe, 65662306a36Sopenharmony_ci#ifdef CONFIG_PM 65762306a36Sopenharmony_ci .suspend = snd_cmi8330_isa_suspend, 65862306a36Sopenharmony_ci .resume = snd_cmi8330_isa_resume, 65962306a36Sopenharmony_ci#endif 66062306a36Sopenharmony_ci .driver = { 66162306a36Sopenharmony_ci .name = DEV_NAME 66262306a36Sopenharmony_ci }, 66362306a36Sopenharmony_ci}; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci#ifdef CONFIG_PNP 66762306a36Sopenharmony_cistatic int snd_cmi8330_pnp_detect(struct pnp_card_link *pcard, 66862306a36Sopenharmony_ci const struct pnp_card_device_id *pid) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci static int dev; 67162306a36Sopenharmony_ci struct snd_card *card; 67262306a36Sopenharmony_ci int res; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci for ( ; dev < SNDRV_CARDS; dev++) { 67562306a36Sopenharmony_ci if (enable[dev] && isapnp[dev]) 67662306a36Sopenharmony_ci break; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci if (dev >= SNDRV_CARDS) 67962306a36Sopenharmony_ci return -ENODEV; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci res = snd_cmi8330_card_new(&pcard->card->dev, dev, &card); 68262306a36Sopenharmony_ci if (res < 0) 68362306a36Sopenharmony_ci return res; 68462306a36Sopenharmony_ci res = snd_cmi8330_pnp(dev, card->private_data, pcard, pid); 68562306a36Sopenharmony_ci if (res < 0) { 68662306a36Sopenharmony_ci snd_printk(KERN_ERR PFX "PnP detection failed\n"); 68762306a36Sopenharmony_ci return res; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci res = snd_cmi8330_probe(card, dev); 69062306a36Sopenharmony_ci if (res < 0) 69162306a36Sopenharmony_ci return res; 69262306a36Sopenharmony_ci pnp_set_card_drvdata(pcard, card); 69362306a36Sopenharmony_ci dev++; 69462306a36Sopenharmony_ci return 0; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci#ifdef CONFIG_PM 69862306a36Sopenharmony_cistatic int snd_cmi8330_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci return snd_cmi8330_suspend(pnp_get_card_drvdata(pcard)); 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic int snd_cmi8330_pnp_resume(struct pnp_card_link *pcard) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci return snd_cmi8330_resume(pnp_get_card_drvdata(pcard)); 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci#endif 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic struct pnp_card_driver cmi8330_pnpc_driver = { 71062306a36Sopenharmony_ci .flags = PNP_DRIVER_RES_DISABLE, 71162306a36Sopenharmony_ci .name = "cmi8330", 71262306a36Sopenharmony_ci .id_table = snd_cmi8330_pnpids, 71362306a36Sopenharmony_ci .probe = snd_cmi8330_pnp_detect, 71462306a36Sopenharmony_ci#ifdef CONFIG_PM 71562306a36Sopenharmony_ci .suspend = snd_cmi8330_pnp_suspend, 71662306a36Sopenharmony_ci .resume = snd_cmi8330_pnp_resume, 71762306a36Sopenharmony_ci#endif 71862306a36Sopenharmony_ci}; 71962306a36Sopenharmony_ci#endif /* CONFIG_PNP */ 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic int __init alsa_card_cmi8330_init(void) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci int err; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci err = isa_register_driver(&snd_cmi8330_driver, SNDRV_CARDS); 72662306a36Sopenharmony_ci#ifdef CONFIG_PNP 72762306a36Sopenharmony_ci if (!err) 72862306a36Sopenharmony_ci isa_registered = 1; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci err = pnp_register_card_driver(&cmi8330_pnpc_driver); 73162306a36Sopenharmony_ci if (!err) 73262306a36Sopenharmony_ci pnp_registered = 1; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (isa_registered) 73562306a36Sopenharmony_ci err = 0; 73662306a36Sopenharmony_ci#endif 73762306a36Sopenharmony_ci return err; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic void __exit alsa_card_cmi8330_exit(void) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci#ifdef CONFIG_PNP 74362306a36Sopenharmony_ci if (pnp_registered) 74462306a36Sopenharmony_ci pnp_unregister_card_driver(&cmi8330_pnpc_driver); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (isa_registered) 74762306a36Sopenharmony_ci#endif 74862306a36Sopenharmony_ci isa_unregister_driver(&snd_cmi8330_driver); 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_cimodule_init(alsa_card_cmi8330_init) 75262306a36Sopenharmony_cimodule_exit(alsa_card_cmi8330_exit) 753