linux/sound/pci/ca0106/ca0106_main.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Copyright (c) 2004 James Courtier-Dutton <[email protected]>
 *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
 *  Version: 0.0.25
 *
 *  FEATURES currently supported:
 *    Front, Rear and Center/LFE.
 *    Surround40 and Surround51.
 *    Capture from MIC an LINE IN input.
 *    SPDIF digital playback of PCM stereo and AC3/DTS works.
 *    (One can use a standard mono mini-jack to one RCA plugs cable.
 *     or one can use a standard stereo mini-jack to two RCA plugs cable.
 *     Plug one of the RCA plugs into the Coax input of the external decoder/receiver.)
 *    ( In theory one could output 3 different AC3 streams at once, to 3 different SPDIF outputs. )
 *    Notes on how to capture sound:
 *      The AC97 is used in the PLAYBACK direction.
 *      The output from the AC97 chip, instead of reaching the speakers, is fed into the Philips 1361T ADC.
 *      So, to record from the MIC, set the MIC Playback volume to max,
 *      unmute the MIC and turn up the MASTER Playback volume.
 *      So, to prevent feedback when capturing, minimise the "Capture feedback into Playback" volume.
 *   
 *    The only playback controls that currently do anything are: -
 *    Analog Front
 *    Analog Rear
 *    Analog Center/LFE
 *    SPDIF Front
 *    SPDIF Rear
 *    SPDIF Center/LFE
 *   
 *    For capture from Mic in or Line in.
 *    Digital/Analog ( switch must be in Analog mode for CAPTURE. )
 * 
 *    CAPTURE feedback into PLAYBACK
 * 
 *  Changelog:
 *    Support interrupts per period.
 *    Removed noise from Center/LFE channel when in Analog mode.
 *    Rename and remove mixer controls.
 *  0.0.6
 *    Use separate card based DMA buffer for periods table list.
 *  0.0.7
 *    Change remove and rename ctrls into lists.
 *  0.0.8
 *    Try to fix capture sources.
 *  0.0.9
 *    Fix AC3 output.
 *    Enable S32_LE format support.
 *  0.0.10
 *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
 *  0.0.11
 *    Add Model name recognition.
 *  0.0.12
 *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
 *    Remove redundent "voice" handling.
 *  0.0.13
 *    Single trigger call for multi channels.
 *  0.0.14
 *    Set limits based on what the sound card hardware can do.
 *    playback periods_min=2, periods_max=8
 *    capture hw constraints require period_size = n * 64 bytes.
 *    playback hw constraints require period_size = n * 64 bytes.
 *  0.0.15
 *    Minor updates.
 *  0.0.16
 *    Implement 192000 sample rate.
 *  0.0.17
 *    Add support for SB0410 and SB0413.
 *  0.0.18
 *    Modified Copyright message.
 *  0.0.19
 *    Finally fix support for SB Live 24 bit. SB0410 and SB0413.
 *    The output codec needs resetting, otherwise all output is muted.
 *  0.0.20
 *    Merge "pci_disable_device(pci);" fixes.
 *  0.0.21
 *    Add 4 capture channels. (SPDIF only comes in on channel 0. )
 *    Add SPDIF capture using optional digital I/O module for SB Live 24bit. (Analog capture does not yet work.)
 *  0.0.22
 *    Add support for MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97. From kiksen, bug #901
 *  0.0.23
 *    Implement support for Line-in capture on SB Live 24bit.
 *  0.0.24
 *    Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
 *  0.0.25
 *    Powerdown SPI DAC channels when not in use
 *
 *  BUGS:
 *    Some stability problems when unloading the snd-ca0106 kernel module.
 *    --
 *
 *  TODO:
 *    4 Capture channels, only one implemented so far.
 *    Other capture rates apart from 48khz not implemented.
 *    MIDI
 *    --
 *  GENERAL INFO:
 *    Model: SB0310
 *    P17 Chip: CA0106-DAT
 *    AC97 Codec: STAC 9721
 *    ADC: Philips 1361T (Stereo 24bit)
 *    DAC: WM8746EDS (6-channel, 24bit, 192Khz)
 *
 *  GENERAL INFO:
 *    Model: SB0410
 *    P17 Chip: CA0106-DAT
 *    AC97 Codec: None
 *    ADC: WM8775EDS (4 Channel)
 *    DAC: CS4382 (114 dB, 24-Bit, 192 kHz, 8-Channel D/A Converter with DSD Support)
 *    SPDIF Out control switches between Mic in and SPDIF out.
 *    No sound out or mic input working yet.
 * 
 *  GENERAL INFO:
 *    Model: SB0413
 *    P17 Chip: CA0106-DAT
 *    AC97 Codec: None.
 *    ADC: Unknown
 *    DAC: Unknown
 *    Trying to handle it like the SB0410.
 *
 *  This code was initially based on code from ALSA's emu10k1x.c which is:
 *  Copyright (c) by Francisco Moraes <[email protected]>
 */
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/info.h>

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();

// module parameters (see "Module Parameters")
static int index[SNDRV_CARDS] =;
static char *id[SNDRV_CARDS] =;
static bool enable[SNDRV_CARDS] =;
static uint subsystem[SNDRV_CARDS]; /* Force card subsystem model */

module_param_array();
MODULE_PARM_DESC();
module_param_array();
MODULE_PARM_DESC();
module_param_array();
MODULE_PARM_DESC();
module_param_array();
MODULE_PARM_DESC();

#include "ca0106.h"

static const struct snd_ca0106_details ca0106_chip_details[] =;

/* hardware definition */
static const struct snd_pcm_hardware snd_ca0106_playback_hw =;

static const struct snd_pcm_hardware snd_ca0106_capture_hw =;

unsigned int snd_ca0106_ptr_read(struct snd_ca0106 * emu, 
					  unsigned int reg, 
					  unsigned int chn)
{}

void snd_ca0106_ptr_write(struct snd_ca0106 *emu, 
				   unsigned int reg, 
				   unsigned int chn, 
				   unsigned int data)
{}

int snd_ca0106_spi_write(struct snd_ca0106 * emu,
				   unsigned int data)
{}

/* The ADC does not support i2c read, so only write is implemented */
int snd_ca0106_i2c_write(struct snd_ca0106 *emu,
				u32 reg,
				u32 value)
{}


static void snd_ca0106_intr_enable(struct snd_ca0106 *emu, unsigned int intrenb)
{}

static void snd_ca0106_intr_disable(struct snd_ca0106 *emu, unsigned int intrenb)
{}


static void snd_ca0106_pcm_free_substream(struct snd_pcm_runtime *runtime)
{}

static const int spi_dacd_reg[] =;
static const int spi_dacd_bit[] =;

static void restore_spdif_bits(struct snd_ca0106 *chip, int idx)
{}

static int snd_ca0106_channel_dac(struct snd_ca0106 *chip,
				  const struct snd_ca0106_details *details,
				  int channel_id)
{}

static int snd_ca0106_pcm_power_dac(struct snd_ca0106 *chip, int channel_id,
				    int power)
{}

/* open_playback callback */
static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream,
						int channel_id)
{}

/* close callback */
static int snd_ca0106_pcm_close_playback(struct snd_pcm_substream *substream)
{}

static int snd_ca0106_pcm_open_playback_front(struct snd_pcm_substream *substream)
{}

static int snd_ca0106_pcm_open_playback_center_lfe(struct snd_pcm_substream *substream)
{}

static int snd_ca0106_pcm_open_playback_unknown(struct snd_pcm_substream *substream)
{}

static int snd_ca0106_pcm_open_playback_rear(struct snd_pcm_substream *substream)
{}

/* open_capture callback */
static int snd_ca0106_pcm_open_capture_channel(struct snd_pcm_substream *substream,
					       int channel_id)
{}

/* close callback */
static int snd_ca0106_pcm_close_capture(struct snd_pcm_substream *substream)
{}

static int snd_ca0106_pcm_open_0_capture(struct snd_pcm_substream *substream)
{}

static int snd_ca0106_pcm_open_1_capture(struct snd_pcm_substream *substream)
{}

static int snd_ca0106_pcm_open_2_capture(struct snd_pcm_substream *substream)
{}

static int snd_ca0106_pcm_open_3_capture(struct snd_pcm_substream *substream)
{}

/* prepare playback callback */
static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
{}

/* prepare capture callback */
static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream)
{}

/* trigger_playback callback */
static int snd_ca0106_pcm_trigger_playback(struct snd_pcm_substream *substream,
				    int cmd)
{}

/* trigger_capture callback */
static int snd_ca0106_pcm_trigger_capture(struct snd_pcm_substream *substream,
				    int cmd)
{}

/* pointer_playback callback */
static snd_pcm_uframes_t
snd_ca0106_pcm_pointer_playback(struct snd_pcm_substream *substream)
{}

/* pointer_capture callback */
static snd_pcm_uframes_t
snd_ca0106_pcm_pointer_capture(struct snd_pcm_substream *substream)
{}

/* operators */
static const struct snd_pcm_ops snd_ca0106_playback_front_ops =;

static const struct snd_pcm_ops snd_ca0106_capture_0_ops =;

static const struct snd_pcm_ops snd_ca0106_capture_1_ops =;

static const struct snd_pcm_ops snd_ca0106_capture_2_ops =;

static const struct snd_pcm_ops snd_ca0106_capture_3_ops =;

static const struct snd_pcm_ops snd_ca0106_playback_center_lfe_ops =;

static const struct snd_pcm_ops snd_ca0106_playback_unknown_ops =;

static const struct snd_pcm_ops snd_ca0106_playback_rear_ops =;


static unsigned short snd_ca0106_ac97_read(struct snd_ac97 *ac97,
					     unsigned short reg)
{}

static void snd_ca0106_ac97_write(struct snd_ac97 *ac97,
				    unsigned short reg, unsigned short val)
{}

static int snd_ca0106_ac97(struct snd_ca0106 *chip)
{}

static void ca0106_stop_chip(struct snd_ca0106 *chip);

static void snd_ca0106_free(struct snd_card *card)
{}

static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
{}

static const struct snd_pcm_chmap_elem surround_map[] =;

static const struct snd_pcm_chmap_elem clfe_map[] =;

static const struct snd_pcm_chmap_elem side_map[] =;

static int snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
{}

#define SPI_REG(reg, value)
static const unsigned int spi_dac_init[] =;

static const unsigned int i2c_adc_init[][2] =;

static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
{}

static void ca0106_stop_chip(struct snd_ca0106 *chip)
{}

static int snd_ca0106_create(int dev, struct snd_card *card,
			     struct pci_dev *pci)
{}


static void ca0106_midi_interrupt_enable(struct snd_ca_midi *midi, int intr)
{}

static void ca0106_midi_interrupt_disable(struct snd_ca_midi *midi, int intr)
{}

static unsigned char ca0106_midi_read(struct snd_ca_midi *midi, int idx)
{}

static void ca0106_midi_write(struct snd_ca_midi *midi, int data, int idx)
{}

static struct snd_card *ca0106_dev_id_card(void *dev_id)
{}

static int ca0106_dev_id_port(void *dev_id)
{}

static int snd_ca0106_midi(struct snd_ca0106 *chip, unsigned int channel)
{}


static int __snd_ca0106_probe(struct pci_dev *pci,
			      const struct pci_device_id *pci_id)
{}

static int snd_ca0106_probe(struct pci_dev *pci,
			    const struct pci_device_id *pci_id)
{}

#ifdef CONFIG_PM_SLEEP
static int snd_ca0106_suspend(struct device *dev)
{}

static int snd_ca0106_resume(struct device *dev)
{}

static SIMPLE_DEV_PM_OPS(snd_ca0106_pm, snd_ca0106_suspend, snd_ca0106_resume);
#define SND_CA0106_PM_OPS
#else
#define SND_CA0106_PM_OPS
#endif

// PCI IDs
static const struct pci_device_id snd_ca0106_ids[] =;
MODULE_DEVICE_TABLE(pci, snd_ca0106_ids);

// pci_driver definition
static struct pci_driver ca0106_driver =;

module_pci_driver();