162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
462306a36Sopenharmony_ci *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
562306a36Sopenharmony_ci *  Version: 0.0.25
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  FEATURES currently supported:
862306a36Sopenharmony_ci *    Front, Rear and Center/LFE.
962306a36Sopenharmony_ci *    Surround40 and Surround51.
1062306a36Sopenharmony_ci *    Capture from MIC an LINE IN input.
1162306a36Sopenharmony_ci *    SPDIF digital playback of PCM stereo and AC3/DTS works.
1262306a36Sopenharmony_ci *    (One can use a standard mono mini-jack to one RCA plugs cable.
1362306a36Sopenharmony_ci *     or one can use a standard stereo mini-jack to two RCA plugs cable.
1462306a36Sopenharmony_ci *     Plug one of the RCA plugs into the Coax input of the external decoder/receiver.)
1562306a36Sopenharmony_ci *    ( In theory one could output 3 different AC3 streams at once, to 3 different SPDIF outputs. )
1662306a36Sopenharmony_ci *    Notes on how to capture sound:
1762306a36Sopenharmony_ci *      The AC97 is used in the PLAYBACK direction.
1862306a36Sopenharmony_ci *      The output from the AC97 chip, instead of reaching the speakers, is fed into the Philips 1361T ADC.
1962306a36Sopenharmony_ci *      So, to record from the MIC, set the MIC Playback volume to max,
2062306a36Sopenharmony_ci *      unmute the MIC and turn up the MASTER Playback volume.
2162306a36Sopenharmony_ci *      So, to prevent feedback when capturing, minimise the "Capture feedback into Playback" volume.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci *    The only playback controls that currently do anything are: -
2462306a36Sopenharmony_ci *    Analog Front
2562306a36Sopenharmony_ci *    Analog Rear
2662306a36Sopenharmony_ci *    Analog Center/LFE
2762306a36Sopenharmony_ci *    SPDIF Front
2862306a36Sopenharmony_ci *    SPDIF Rear
2962306a36Sopenharmony_ci *    SPDIF Center/LFE
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci *    For capture from Mic in or Line in.
3262306a36Sopenharmony_ci *    Digital/Analog ( switch must be in Analog mode for CAPTURE. )
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci *    CAPTURE feedback into PLAYBACK
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci *  Changelog:
3762306a36Sopenharmony_ci *    Support interrupts per period.
3862306a36Sopenharmony_ci *    Removed noise from Center/LFE channel when in Analog mode.
3962306a36Sopenharmony_ci *    Rename and remove mixer controls.
4062306a36Sopenharmony_ci *  0.0.6
4162306a36Sopenharmony_ci *    Use separate card based DMA buffer for periods table list.
4262306a36Sopenharmony_ci *  0.0.7
4362306a36Sopenharmony_ci *    Change remove and rename ctrls into lists.
4462306a36Sopenharmony_ci *  0.0.8
4562306a36Sopenharmony_ci *    Try to fix capture sources.
4662306a36Sopenharmony_ci *  0.0.9
4762306a36Sopenharmony_ci *    Fix AC3 output.
4862306a36Sopenharmony_ci *    Enable S32_LE format support.
4962306a36Sopenharmony_ci *  0.0.10
5062306a36Sopenharmony_ci *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
5162306a36Sopenharmony_ci *  0.0.11
5262306a36Sopenharmony_ci *    Add Model name recognition.
5362306a36Sopenharmony_ci *  0.0.12
5462306a36Sopenharmony_ci *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
5562306a36Sopenharmony_ci *    Remove redundent "voice" handling.
5662306a36Sopenharmony_ci *  0.0.13
5762306a36Sopenharmony_ci *    Single trigger call for multi channels.
5862306a36Sopenharmony_ci *  0.0.14
5962306a36Sopenharmony_ci *    Set limits based on what the sound card hardware can do.
6062306a36Sopenharmony_ci *    playback periods_min=2, periods_max=8
6162306a36Sopenharmony_ci *    capture hw constraints require period_size = n * 64 bytes.
6262306a36Sopenharmony_ci *    playback hw constraints require period_size = n * 64 bytes.
6362306a36Sopenharmony_ci *  0.0.15
6462306a36Sopenharmony_ci *    Minor updates.
6562306a36Sopenharmony_ci *  0.0.16
6662306a36Sopenharmony_ci *    Implement 192000 sample rate.
6762306a36Sopenharmony_ci *  0.0.17
6862306a36Sopenharmony_ci *    Add support for SB0410 and SB0413.
6962306a36Sopenharmony_ci *  0.0.18
7062306a36Sopenharmony_ci *    Modified Copyright message.
7162306a36Sopenharmony_ci *  0.0.19
7262306a36Sopenharmony_ci *    Finally fix support for SB Live 24 bit. SB0410 and SB0413.
7362306a36Sopenharmony_ci *    The output codec needs resetting, otherwise all output is muted.
7462306a36Sopenharmony_ci *  0.0.20
7562306a36Sopenharmony_ci *    Merge "pci_disable_device(pci);" fixes.
7662306a36Sopenharmony_ci *  0.0.21
7762306a36Sopenharmony_ci *    Add 4 capture channels. (SPDIF only comes in on channel 0. )
7862306a36Sopenharmony_ci *    Add SPDIF capture using optional digital I/O module for SB Live 24bit. (Analog capture does not yet work.)
7962306a36Sopenharmony_ci *  0.0.22
8062306a36Sopenharmony_ci *    Add support for MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97. From kiksen, bug #901
8162306a36Sopenharmony_ci *  0.0.23
8262306a36Sopenharmony_ci *    Implement support for Line-in capture on SB Live 24bit.
8362306a36Sopenharmony_ci *  0.0.24
8462306a36Sopenharmony_ci *    Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
8562306a36Sopenharmony_ci *  0.0.25
8662306a36Sopenharmony_ci *    Powerdown SPI DAC channels when not in use
8762306a36Sopenharmony_ci *
8862306a36Sopenharmony_ci *  BUGS:
8962306a36Sopenharmony_ci *    Some stability problems when unloading the snd-ca0106 kernel module.
9062306a36Sopenharmony_ci *    --
9162306a36Sopenharmony_ci *
9262306a36Sopenharmony_ci *  TODO:
9362306a36Sopenharmony_ci *    4 Capture channels, only one implemented so far.
9462306a36Sopenharmony_ci *    Other capture rates apart from 48khz not implemented.
9562306a36Sopenharmony_ci *    MIDI
9662306a36Sopenharmony_ci *    --
9762306a36Sopenharmony_ci *  GENERAL INFO:
9862306a36Sopenharmony_ci *    Model: SB0310
9962306a36Sopenharmony_ci *    P17 Chip: CA0106-DAT
10062306a36Sopenharmony_ci *    AC97 Codec: STAC 9721
10162306a36Sopenharmony_ci *    ADC: Philips 1361T (Stereo 24bit)
10262306a36Sopenharmony_ci *    DAC: WM8746EDS (6-channel, 24bit, 192Khz)
10362306a36Sopenharmony_ci *
10462306a36Sopenharmony_ci *  GENERAL INFO:
10562306a36Sopenharmony_ci *    Model: SB0410
10662306a36Sopenharmony_ci *    P17 Chip: CA0106-DAT
10762306a36Sopenharmony_ci *    AC97 Codec: None
10862306a36Sopenharmony_ci *    ADC: WM8775EDS (4 Channel)
10962306a36Sopenharmony_ci *    DAC: CS4382 (114 dB, 24-Bit, 192 kHz, 8-Channel D/A Converter with DSD Support)
11062306a36Sopenharmony_ci *    SPDIF Out control switches between Mic in and SPDIF out.
11162306a36Sopenharmony_ci *    No sound out or mic input working yet.
11262306a36Sopenharmony_ci *
11362306a36Sopenharmony_ci *  GENERAL INFO:
11462306a36Sopenharmony_ci *    Model: SB0413
11562306a36Sopenharmony_ci *    P17 Chip: CA0106-DAT
11662306a36Sopenharmony_ci *    AC97 Codec: None.
11762306a36Sopenharmony_ci *    ADC: Unknown
11862306a36Sopenharmony_ci *    DAC: Unknown
11962306a36Sopenharmony_ci *    Trying to handle it like the SB0410.
12062306a36Sopenharmony_ci *
12162306a36Sopenharmony_ci *  This code was initially based on code from ALSA's emu10k1x.c which is:
12262306a36Sopenharmony_ci *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
12362306a36Sopenharmony_ci */
12462306a36Sopenharmony_ci#include <linux/delay.h>
12562306a36Sopenharmony_ci#include <linux/init.h>
12662306a36Sopenharmony_ci#include <linux/interrupt.h>
12762306a36Sopenharmony_ci#include <linux/pci.h>
12862306a36Sopenharmony_ci#include <linux/slab.h>
12962306a36Sopenharmony_ci#include <linux/module.h>
13062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
13162306a36Sopenharmony_ci#include <sound/core.h>
13262306a36Sopenharmony_ci#include <sound/initval.h>
13362306a36Sopenharmony_ci#include <sound/pcm.h>
13462306a36Sopenharmony_ci#include <sound/ac97_codec.h>
13562306a36Sopenharmony_ci#include <sound/info.h>
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ciMODULE_AUTHOR("James Courtier-Dutton <James@superbug.demon.co.uk>");
13862306a36Sopenharmony_ciMODULE_DESCRIPTION("CA0106");
13962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci// module parameters (see "Module Parameters")
14262306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
14362306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
14462306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
14562306a36Sopenharmony_cistatic uint subsystem[SNDRV_CARDS]; /* Force card subsystem model */
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
14862306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for the CA0106 soundcard.");
14962306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
15062306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for the CA0106 soundcard.");
15162306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
15262306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable the CA0106 soundcard.");
15362306a36Sopenharmony_cimodule_param_array(subsystem, uint, NULL, 0444);
15462306a36Sopenharmony_ciMODULE_PARM_DESC(subsystem, "Force card subsystem model.");
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci#include "ca0106.h"
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic const struct snd_ca0106_details ca0106_chip_details[] = {
15962306a36Sopenharmony_ci	 /* Sound Blaster X-Fi Extreme Audio. This does not have an AC97. 53SB079000000 */
16062306a36Sopenharmony_ci	 /* It is really just a normal SB Live 24bit. */
16162306a36Sopenharmony_ci	 /* Tested:
16262306a36Sopenharmony_ci	  * See ALSA bug#3251
16362306a36Sopenharmony_ci	  */
16462306a36Sopenharmony_ci	 { .serial = 0x10131102,
16562306a36Sopenharmony_ci	   .name   = "X-Fi Extreme Audio [SBxxxx]",
16662306a36Sopenharmony_ci	   .gpio_type = 1,
16762306a36Sopenharmony_ci	   .i2c_adc = 1 } ,
16862306a36Sopenharmony_ci	 /* Sound Blaster X-Fi Extreme Audio. This does not have an AC97. 53SB079000000 */
16962306a36Sopenharmony_ci	 /* It is really just a normal SB Live 24bit. */
17062306a36Sopenharmony_ci	 /*
17162306a36Sopenharmony_ci 	  * CTRL:CA0111-WTLF
17262306a36Sopenharmony_ci	  * ADC: WM8775SEDS
17362306a36Sopenharmony_ci	  * DAC: CS4382-KQZ
17462306a36Sopenharmony_ci	  */
17562306a36Sopenharmony_ci	 /* Tested:
17662306a36Sopenharmony_ci	  * Playback on front, rear, center/lfe speakers
17762306a36Sopenharmony_ci	  * Capture from Mic in.
17862306a36Sopenharmony_ci	  * Not-Tested:
17962306a36Sopenharmony_ci	  * Capture from Line in.
18062306a36Sopenharmony_ci	  * Playback to digital out.
18162306a36Sopenharmony_ci	  */
18262306a36Sopenharmony_ci	 { .serial = 0x10121102,
18362306a36Sopenharmony_ci	   .name   = "X-Fi Extreme Audio [SB0790]",
18462306a36Sopenharmony_ci	   .gpio_type = 1,
18562306a36Sopenharmony_ci	   .i2c_adc = 1 } ,
18662306a36Sopenharmony_ci	 /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97.  */
18762306a36Sopenharmony_ci	 /* AudigyLS[SB0310] */
18862306a36Sopenharmony_ci	 { .serial = 0x10021102,
18962306a36Sopenharmony_ci	   .name   = "AudigyLS [SB0310]",
19062306a36Sopenharmony_ci	   .ac97   = 1 } ,
19162306a36Sopenharmony_ci	 /* Unknown AudigyLS that also says SB0310 on it */
19262306a36Sopenharmony_ci	 { .serial = 0x10051102,
19362306a36Sopenharmony_ci	   .name   = "AudigyLS [SB0310b]",
19462306a36Sopenharmony_ci	   .ac97   = 1 } ,
19562306a36Sopenharmony_ci	 /* New Sound Blaster Live! 7.1 24bit. This does not have an AC97. 53SB041000001 */
19662306a36Sopenharmony_ci	 { .serial = 0x10061102,
19762306a36Sopenharmony_ci	   .name   = "Live! 7.1 24bit [SB0410]",
19862306a36Sopenharmony_ci	   .gpio_type = 1,
19962306a36Sopenharmony_ci	   .i2c_adc = 1 } ,
20062306a36Sopenharmony_ci	 /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97.  */
20162306a36Sopenharmony_ci	 { .serial = 0x10071102,
20262306a36Sopenharmony_ci	   .name   = "Live! 7.1 24bit [SB0413]",
20362306a36Sopenharmony_ci	   .gpio_type = 1,
20462306a36Sopenharmony_ci	   .i2c_adc = 1 } ,
20562306a36Sopenharmony_ci	 /* New Audigy SE. Has a different DAC. */
20662306a36Sopenharmony_ci	 /* SB0570:
20762306a36Sopenharmony_ci	  * CTRL:CA0106-DAT
20862306a36Sopenharmony_ci	  * ADC: WM8775EDS
20962306a36Sopenharmony_ci	  * DAC: WM8768GEDS
21062306a36Sopenharmony_ci	  */
21162306a36Sopenharmony_ci	 { .serial = 0x100a1102,
21262306a36Sopenharmony_ci	   .name   = "Audigy SE [SB0570]",
21362306a36Sopenharmony_ci	   .gpio_type = 1,
21462306a36Sopenharmony_ci	   .i2c_adc = 1,
21562306a36Sopenharmony_ci	   .spi_dac = 0x4021 } ,
21662306a36Sopenharmony_ci	 /* New Audigy LS. Has a different DAC. */
21762306a36Sopenharmony_ci	 /* SB0570:
21862306a36Sopenharmony_ci	  * CTRL:CA0106-DAT
21962306a36Sopenharmony_ci	  * ADC: WM8775EDS
22062306a36Sopenharmony_ci	  * DAC: WM8768GEDS
22162306a36Sopenharmony_ci	  */
22262306a36Sopenharmony_ci	 { .serial = 0x10111102,
22362306a36Sopenharmony_ci	   .name   = "Audigy SE OEM [SB0570a]",
22462306a36Sopenharmony_ci	   .gpio_type = 1,
22562306a36Sopenharmony_ci	   .i2c_adc = 1,
22662306a36Sopenharmony_ci	   .spi_dac = 0x4021 } ,
22762306a36Sopenharmony_ci	/* Sound Blaster 5.1vx
22862306a36Sopenharmony_ci	 * Tested: Playback on front, rear, center/lfe speakers
22962306a36Sopenharmony_ci	 * Not-Tested: Capture
23062306a36Sopenharmony_ci	 */
23162306a36Sopenharmony_ci	{ .serial = 0x10041102,
23262306a36Sopenharmony_ci	  .name   = "Sound Blaster 5.1vx [SB1070]",
23362306a36Sopenharmony_ci	  .gpio_type = 1,
23462306a36Sopenharmony_ci	  .i2c_adc = 0,
23562306a36Sopenharmony_ci	  .spi_dac = 0x0124
23662306a36Sopenharmony_ci	 } ,
23762306a36Sopenharmony_ci	 /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */
23862306a36Sopenharmony_ci	 /* SB0438
23962306a36Sopenharmony_ci	  * CTRL:CA0106-DAT
24062306a36Sopenharmony_ci	  * ADC: WM8775SEDS
24162306a36Sopenharmony_ci	  * DAC: CS4382-KQZ
24262306a36Sopenharmony_ci	  */
24362306a36Sopenharmony_ci	 { .serial = 0x10091462,
24462306a36Sopenharmony_ci	   .name   = "MSI K8N Diamond MB [SB0438]",
24562306a36Sopenharmony_ci	   .gpio_type = 2,
24662306a36Sopenharmony_ci	   .i2c_adc = 1 } ,
24762306a36Sopenharmony_ci	 /* MSI K8N Diamond PLUS MB */
24862306a36Sopenharmony_ci	 { .serial = 0x10091102,
24962306a36Sopenharmony_ci	   .name   = "MSI K8N Diamond MB",
25062306a36Sopenharmony_ci	   .gpio_type = 2,
25162306a36Sopenharmony_ci	   .i2c_adc = 1,
25262306a36Sopenharmony_ci	   .spi_dac = 0x4021 } ,
25362306a36Sopenharmony_ci	/* Giga-byte GA-G1975X mobo
25462306a36Sopenharmony_ci	 * Novell bnc#395807
25562306a36Sopenharmony_ci	 */
25662306a36Sopenharmony_ci	/* FIXME: the GPIO and I2C setting aren't tested well */
25762306a36Sopenharmony_ci	{ .serial = 0x1458a006,
25862306a36Sopenharmony_ci	  .name = "Giga-byte GA-G1975X",
25962306a36Sopenharmony_ci	  .gpio_type = 1,
26062306a36Sopenharmony_ci	  .i2c_adc = 1 },
26162306a36Sopenharmony_ci	 /* Shuttle XPC SD31P which has an onboard Creative Labs
26262306a36Sopenharmony_ci	  * Sound Blaster Live! 24-bit EAX
26362306a36Sopenharmony_ci	  * high-definition 7.1 audio processor".
26462306a36Sopenharmony_ci	  * Added using info from andrewvegan in alsa bug #1298
26562306a36Sopenharmony_ci	  */
26662306a36Sopenharmony_ci	 { .serial = 0x30381297,
26762306a36Sopenharmony_ci	   .name   = "Shuttle XPC SD31P [SD31P]",
26862306a36Sopenharmony_ci	   .gpio_type = 1,
26962306a36Sopenharmony_ci	   .i2c_adc = 1 } ,
27062306a36Sopenharmony_ci	/* Shuttle XPC SD11G5 which has an onboard Creative Labs
27162306a36Sopenharmony_ci	 * Sound Blaster Live! 24-bit EAX
27262306a36Sopenharmony_ci	 * high-definition 7.1 audio processor".
27362306a36Sopenharmony_ci	 * Fixes ALSA bug#1600
27462306a36Sopenharmony_ci         */
27562306a36Sopenharmony_ci	{ .serial = 0x30411297,
27662306a36Sopenharmony_ci	  .name = "Shuttle XPC SD11G5 [SD11G5]",
27762306a36Sopenharmony_ci	  .gpio_type = 1,
27862306a36Sopenharmony_ci	  .i2c_adc = 1 } ,
27962306a36Sopenharmony_ci	 { .serial = 0,
28062306a36Sopenharmony_ci	   .name   = "AudigyLS [Unknown]" }
28162306a36Sopenharmony_ci};
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci/* hardware definition */
28462306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_ca0106_playback_hw = {
28562306a36Sopenharmony_ci	.info =			SNDRV_PCM_INFO_MMAP |
28662306a36Sopenharmony_ci				SNDRV_PCM_INFO_INTERLEAVED |
28762306a36Sopenharmony_ci				SNDRV_PCM_INFO_BLOCK_TRANSFER |
28862306a36Sopenharmony_ci				SNDRV_PCM_INFO_MMAP_VALID |
28962306a36Sopenharmony_ci				SNDRV_PCM_INFO_SYNC_START,
29062306a36Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
29162306a36Sopenharmony_ci	.rates =		(SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
29262306a36Sopenharmony_ci				 SNDRV_PCM_RATE_192000),
29362306a36Sopenharmony_ci	.rate_min =		48000,
29462306a36Sopenharmony_ci	.rate_max =		192000,
29562306a36Sopenharmony_ci	.channels_min =		2,  //1,
29662306a36Sopenharmony_ci	.channels_max =		2,  //6,
29762306a36Sopenharmony_ci	.buffer_bytes_max =	((65536 - 64) * 8),
29862306a36Sopenharmony_ci	.period_bytes_min =	64,
29962306a36Sopenharmony_ci	.period_bytes_max =	(65536 - 64),
30062306a36Sopenharmony_ci	.periods_min =		2,
30162306a36Sopenharmony_ci	.periods_max =		8,
30262306a36Sopenharmony_ci	.fifo_size =		0,
30362306a36Sopenharmony_ci};
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_ca0106_capture_hw = {
30662306a36Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP |
30762306a36Sopenharmony_ci				 SNDRV_PCM_INFO_INTERLEAVED |
30862306a36Sopenharmony_ci				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
30962306a36Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID),
31062306a36Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
31162306a36Sopenharmony_ci#if 0 /* FIXME: looks like 44.1kHz capture causes noisy output on 48kHz */
31262306a36Sopenharmony_ci	.rates =		(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
31362306a36Sopenharmony_ci				 SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000),
31462306a36Sopenharmony_ci	.rate_min =		44100,
31562306a36Sopenharmony_ci#else
31662306a36Sopenharmony_ci	.rates =		(SNDRV_PCM_RATE_48000 |
31762306a36Sopenharmony_ci				 SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000),
31862306a36Sopenharmony_ci	.rate_min =		48000,
31962306a36Sopenharmony_ci#endif /* FIXME */
32062306a36Sopenharmony_ci	.rate_max =		192000,
32162306a36Sopenharmony_ci	.channels_min =		2,
32262306a36Sopenharmony_ci	.channels_max =		2,
32362306a36Sopenharmony_ci	.buffer_bytes_max =	65536 - 128,
32462306a36Sopenharmony_ci	.period_bytes_min =	64,
32562306a36Sopenharmony_ci	.period_bytes_max =	32768 - 64,
32662306a36Sopenharmony_ci	.periods_min =		2,
32762306a36Sopenharmony_ci	.periods_max =		2,
32862306a36Sopenharmony_ci	.fifo_size =		0,
32962306a36Sopenharmony_ci};
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ciunsigned int snd_ca0106_ptr_read(struct snd_ca0106 * emu,
33262306a36Sopenharmony_ci					  unsigned int reg,
33362306a36Sopenharmony_ci					  unsigned int chn)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	unsigned long flags;
33662306a36Sopenharmony_ci	unsigned int regptr, val;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	regptr = (reg << 16) | chn;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
34162306a36Sopenharmony_ci	outl(regptr, emu->port + CA0106_PTR);
34262306a36Sopenharmony_ci	val = inl(emu->port + CA0106_DATA);
34362306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
34462306a36Sopenharmony_ci	return val;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_civoid snd_ca0106_ptr_write(struct snd_ca0106 *emu,
34862306a36Sopenharmony_ci				   unsigned int reg,
34962306a36Sopenharmony_ci				   unsigned int chn,
35062306a36Sopenharmony_ci				   unsigned int data)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	unsigned int regptr;
35362306a36Sopenharmony_ci	unsigned long flags;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	regptr = (reg << 16) | chn;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
35862306a36Sopenharmony_ci	outl(regptr, emu->port + CA0106_PTR);
35962306a36Sopenharmony_ci	outl(data, emu->port + CA0106_DATA);
36062306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ciint snd_ca0106_spi_write(struct snd_ca0106 * emu,
36462306a36Sopenharmony_ci				   unsigned int data)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	unsigned int reset, set;
36762306a36Sopenharmony_ci	unsigned int reg, tmp;
36862306a36Sopenharmony_ci	int n, result;
36962306a36Sopenharmony_ci	reg = SPI;
37062306a36Sopenharmony_ci	if (data > 0xffff) /* Only 16bit values allowed */
37162306a36Sopenharmony_ci		return 1;
37262306a36Sopenharmony_ci	tmp = snd_ca0106_ptr_read(emu, reg, 0);
37362306a36Sopenharmony_ci	reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
37462306a36Sopenharmony_ci	set = reset | 0x10000; /* Set xxx1xxxx */
37562306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, reg, 0, reset | data);
37662306a36Sopenharmony_ci	tmp = snd_ca0106_ptr_read(emu, reg, 0); /* write post */
37762306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, reg, 0, set | data);
37862306a36Sopenharmony_ci	result = 1;
37962306a36Sopenharmony_ci	/* Wait for status bit to return to 0 */
38062306a36Sopenharmony_ci	for (n = 0; n < 100; n++) {
38162306a36Sopenharmony_ci		udelay(10);
38262306a36Sopenharmony_ci		tmp = snd_ca0106_ptr_read(emu, reg, 0);
38362306a36Sopenharmony_ci		if (!(tmp & 0x10000)) {
38462306a36Sopenharmony_ci			result = 0;
38562306a36Sopenharmony_ci			break;
38662306a36Sopenharmony_ci		}
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci	if (result) /* Timed out */
38962306a36Sopenharmony_ci		return 1;
39062306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, reg, 0, reset | data);
39162306a36Sopenharmony_ci	tmp = snd_ca0106_ptr_read(emu, reg, 0); /* Write post */
39262306a36Sopenharmony_ci	return 0;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci/* The ADC does not support i2c read, so only write is implemented */
39662306a36Sopenharmony_ciint snd_ca0106_i2c_write(struct snd_ca0106 *emu,
39762306a36Sopenharmony_ci				u32 reg,
39862306a36Sopenharmony_ci				u32 value)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	u32 tmp;
40162306a36Sopenharmony_ci	int timeout = 0;
40262306a36Sopenharmony_ci	int status;
40362306a36Sopenharmony_ci	int retry;
40462306a36Sopenharmony_ci	if ((reg > 0x7f) || (value > 0x1ff)) {
40562306a36Sopenharmony_ci		dev_err(emu->card->dev, "i2c_write: invalid values.\n");
40662306a36Sopenharmony_ci		return -EINVAL;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	tmp = reg << 25 | value << 16;
41062306a36Sopenharmony_ci	/*
41162306a36Sopenharmony_ci	dev_dbg(emu->card->dev, "I2C-write:reg=0x%x, value=0x%x\n", reg, value);
41262306a36Sopenharmony_ci	*/
41362306a36Sopenharmony_ci	/* Not sure what this I2C channel controls. */
41462306a36Sopenharmony_ci	/* snd_ca0106_ptr_write(emu, I2C_D0, 0, tmp); */
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	/* This controls the I2C connected to the WM8775 ADC Codec */
41762306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, I2C_D1, 0, tmp);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	for (retry = 0; retry < 10; retry++) {
42062306a36Sopenharmony_ci		/* Send the data to i2c */
42162306a36Sopenharmony_ci		//tmp = snd_ca0106_ptr_read(emu, I2C_A, 0);
42262306a36Sopenharmony_ci		//tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK);
42362306a36Sopenharmony_ci		tmp = 0;
42462306a36Sopenharmony_ci		tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
42562306a36Sopenharmony_ci		snd_ca0106_ptr_write(emu, I2C_A, 0, tmp);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci		/* Wait till the transaction ends */
42862306a36Sopenharmony_ci		while (1) {
42962306a36Sopenharmony_ci			status = snd_ca0106_ptr_read(emu, I2C_A, 0);
43062306a36Sopenharmony_ci			/*dev_dbg(emu->card->dev, "I2C:status=0x%x\n", status);*/
43162306a36Sopenharmony_ci			timeout++;
43262306a36Sopenharmony_ci			if ((status & I2C_A_ADC_START) == 0)
43362306a36Sopenharmony_ci				break;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci			if (timeout > 1000)
43662306a36Sopenharmony_ci				break;
43762306a36Sopenharmony_ci		}
43862306a36Sopenharmony_ci		//Read back and see if the transaction is successful
43962306a36Sopenharmony_ci		if ((status & I2C_A_ADC_ABORT) == 0)
44062306a36Sopenharmony_ci			break;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (retry == 10) {
44462306a36Sopenharmony_ci		dev_err(emu->card->dev, "Writing to ADC failed!\n");
44562306a36Sopenharmony_ci		return -EINVAL;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci    	return 0;
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic void snd_ca0106_intr_enable(struct snd_ca0106 *emu, unsigned int intrenb)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	unsigned long flags;
45562306a36Sopenharmony_ci	unsigned int intr_enable;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
45862306a36Sopenharmony_ci	intr_enable = inl(emu->port + CA0106_INTE) | intrenb;
45962306a36Sopenharmony_ci	outl(intr_enable, emu->port + CA0106_INTE);
46062306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic void snd_ca0106_intr_disable(struct snd_ca0106 *emu, unsigned int intrenb)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	unsigned long flags;
46662306a36Sopenharmony_ci	unsigned int intr_enable;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
46962306a36Sopenharmony_ci	intr_enable = inl(emu->port + CA0106_INTE) & ~intrenb;
47062306a36Sopenharmony_ci	outl(intr_enable, emu->port + CA0106_INTE);
47162306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic void snd_ca0106_pcm_free_substream(struct snd_pcm_runtime *runtime)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	kfree(runtime->private_data);
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic const int spi_dacd_reg[] = {
48162306a36Sopenharmony_ci	SPI_DACD0_REG,
48262306a36Sopenharmony_ci	SPI_DACD1_REG,
48362306a36Sopenharmony_ci	SPI_DACD2_REG,
48462306a36Sopenharmony_ci	0,
48562306a36Sopenharmony_ci	SPI_DACD4_REG,
48662306a36Sopenharmony_ci};
48762306a36Sopenharmony_cistatic const int spi_dacd_bit[] = {
48862306a36Sopenharmony_ci	SPI_DACD0_BIT,
48962306a36Sopenharmony_ci	SPI_DACD1_BIT,
49062306a36Sopenharmony_ci	SPI_DACD2_BIT,
49162306a36Sopenharmony_ci	0,
49262306a36Sopenharmony_ci	SPI_DACD4_BIT,
49362306a36Sopenharmony_ci};
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic void restore_spdif_bits(struct snd_ca0106 *chip, int idx)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	if (chip->spdif_str_bits[idx] != chip->spdif_bits[idx]) {
49862306a36Sopenharmony_ci		chip->spdif_str_bits[idx] = chip->spdif_bits[idx];
49962306a36Sopenharmony_ci		snd_ca0106_ptr_write(chip, SPCS0 + idx, 0,
50062306a36Sopenharmony_ci				     chip->spdif_str_bits[idx]);
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic int snd_ca0106_channel_dac(struct snd_ca0106 *chip,
50562306a36Sopenharmony_ci				  const struct snd_ca0106_details *details,
50662306a36Sopenharmony_ci				  int channel_id)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	switch (channel_id) {
50962306a36Sopenharmony_ci	case PCM_FRONT_CHANNEL:
51062306a36Sopenharmony_ci		return (details->spi_dac & 0xf000) >> (4 * 3);
51162306a36Sopenharmony_ci	case PCM_REAR_CHANNEL:
51262306a36Sopenharmony_ci		return (details->spi_dac & 0x0f00) >> (4 * 2);
51362306a36Sopenharmony_ci	case PCM_CENTER_LFE_CHANNEL:
51462306a36Sopenharmony_ci		return (details->spi_dac & 0x00f0) >> (4 * 1);
51562306a36Sopenharmony_ci	case PCM_UNKNOWN_CHANNEL:
51662306a36Sopenharmony_ci		return (details->spi_dac & 0x000f) >> (4 * 0);
51762306a36Sopenharmony_ci	default:
51862306a36Sopenharmony_ci		dev_dbg(chip->card->dev, "ca0106: unknown channel_id %d\n",
51962306a36Sopenharmony_ci			   channel_id);
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci	return 0;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic int snd_ca0106_pcm_power_dac(struct snd_ca0106 *chip, int channel_id,
52562306a36Sopenharmony_ci				    int power)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	if (chip->details->spi_dac) {
52862306a36Sopenharmony_ci		const int dac = snd_ca0106_channel_dac(chip, chip->details,
52962306a36Sopenharmony_ci						       channel_id);
53062306a36Sopenharmony_ci		const int reg = spi_dacd_reg[dac];
53162306a36Sopenharmony_ci		const int bit = spi_dacd_bit[dac];
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci		if (power)
53462306a36Sopenharmony_ci			/* Power up */
53562306a36Sopenharmony_ci			chip->spi_dac_reg[reg] &= ~bit;
53662306a36Sopenharmony_ci		else
53762306a36Sopenharmony_ci			/* Power down */
53862306a36Sopenharmony_ci			chip->spi_dac_reg[reg] |= bit;
53962306a36Sopenharmony_ci		if (snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]) != 0)
54062306a36Sopenharmony_ci			return -ENXIO;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci	return 0;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci/* open_playback callback */
54662306a36Sopenharmony_cistatic int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream,
54762306a36Sopenharmony_ci						int channel_id)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct snd_ca0106 *chip = snd_pcm_substream_chip(substream);
55062306a36Sopenharmony_ci        struct snd_ca0106_channel *channel = &(chip->playback_channels[channel_id]);
55162306a36Sopenharmony_ci	struct snd_ca0106_pcm *epcm;
55262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
55362306a36Sopenharmony_ci	int err;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	if (epcm == NULL)
55862306a36Sopenharmony_ci		return -ENOMEM;
55962306a36Sopenharmony_ci	epcm->emu = chip;
56062306a36Sopenharmony_ci	epcm->substream = substream;
56162306a36Sopenharmony_ci        epcm->channel_id=channel_id;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	runtime->private_data = epcm;
56462306a36Sopenharmony_ci	runtime->private_free = snd_ca0106_pcm_free_substream;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	runtime->hw = snd_ca0106_playback_hw;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci        channel->emu = chip;
56962306a36Sopenharmony_ci        channel->number = channel_id;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	channel->use = 1;
57262306a36Sopenharmony_ci	/*
57362306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "open:channel_id=%d, chip=%p, channel=%p\n",
57462306a36Sopenharmony_ci	       channel_id, chip, channel);
57562306a36Sopenharmony_ci	*/
57662306a36Sopenharmony_ci        //channel->interrupt = snd_ca0106_pcm_channel_interrupt;
57762306a36Sopenharmony_ci	channel->epcm = epcm;
57862306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
57962306a36Sopenharmony_ci	if (err < 0)
58062306a36Sopenharmony_ci                return err;
58162306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
58262306a36Sopenharmony_ci	if (err < 0)
58362306a36Sopenharmony_ci                return err;
58462306a36Sopenharmony_ci	snd_pcm_set_sync(substream);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	/* Front channel dac should already be on */
58762306a36Sopenharmony_ci	if (channel_id != PCM_FRONT_CHANNEL) {
58862306a36Sopenharmony_ci		err = snd_ca0106_pcm_power_dac(chip, channel_id, 1);
58962306a36Sopenharmony_ci		if (err < 0)
59062306a36Sopenharmony_ci			return err;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	restore_spdif_bits(chip, channel_id);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	return 0;
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci/* close callback */
59962306a36Sopenharmony_cistatic int snd_ca0106_pcm_close_playback(struct snd_pcm_substream *substream)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	struct snd_ca0106 *chip = snd_pcm_substream_chip(substream);
60262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
60362306a36Sopenharmony_ci        struct snd_ca0106_pcm *epcm = runtime->private_data;
60462306a36Sopenharmony_ci	chip->playback_channels[epcm->channel_id].use = 0;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	restore_spdif_bits(chip, epcm->channel_id);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	/* Front channel dac should stay on */
60962306a36Sopenharmony_ci	if (epcm->channel_id != PCM_FRONT_CHANNEL) {
61062306a36Sopenharmony_ci		int err;
61162306a36Sopenharmony_ci		err = snd_ca0106_pcm_power_dac(chip, epcm->channel_id, 0);
61262306a36Sopenharmony_ci		if (err < 0)
61362306a36Sopenharmony_ci			return err;
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	/* FIXME: maybe zero others */
61762306a36Sopenharmony_ci	return 0;
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic int snd_ca0106_pcm_open_playback_front(struct snd_pcm_substream *substream)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	return snd_ca0106_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL);
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic int snd_ca0106_pcm_open_playback_center_lfe(struct snd_pcm_substream *substream)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	return snd_ca0106_pcm_open_playback_channel(substream, PCM_CENTER_LFE_CHANNEL);
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic int snd_ca0106_pcm_open_playback_unknown(struct snd_pcm_substream *substream)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	return snd_ca0106_pcm_open_playback_channel(substream, PCM_UNKNOWN_CHANNEL);
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic int snd_ca0106_pcm_open_playback_rear(struct snd_pcm_substream *substream)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	return snd_ca0106_pcm_open_playback_channel(substream, PCM_REAR_CHANNEL);
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci/* open_capture callback */
64162306a36Sopenharmony_cistatic int snd_ca0106_pcm_open_capture_channel(struct snd_pcm_substream *substream,
64262306a36Sopenharmony_ci					       int channel_id)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	struct snd_ca0106 *chip = snd_pcm_substream_chip(substream);
64562306a36Sopenharmony_ci        struct snd_ca0106_channel *channel = &(chip->capture_channels[channel_id]);
64662306a36Sopenharmony_ci	struct snd_ca0106_pcm *epcm;
64762306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
64862306a36Sopenharmony_ci	int err;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
65162306a36Sopenharmony_ci	if (!epcm)
65262306a36Sopenharmony_ci		return -ENOMEM;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	epcm->emu = chip;
65562306a36Sopenharmony_ci	epcm->substream = substream;
65662306a36Sopenharmony_ci        epcm->channel_id=channel_id;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	runtime->private_data = epcm;
65962306a36Sopenharmony_ci	runtime->private_free = snd_ca0106_pcm_free_substream;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	runtime->hw = snd_ca0106_capture_hw;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci        channel->emu = chip;
66462306a36Sopenharmony_ci        channel->number = channel_id;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	channel->use = 1;
66762306a36Sopenharmony_ci	/*
66862306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "open:channel_id=%d, chip=%p, channel=%p\n",
66962306a36Sopenharmony_ci	       channel_id, chip, channel);
67062306a36Sopenharmony_ci	*/
67162306a36Sopenharmony_ci        //channel->interrupt = snd_ca0106_pcm_channel_interrupt;
67262306a36Sopenharmony_ci        channel->epcm = epcm;
67362306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
67462306a36Sopenharmony_ci	if (err < 0)
67562306a36Sopenharmony_ci                return err;
67662306a36Sopenharmony_ci	//snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes);
67762306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
67862306a36Sopenharmony_ci	if (err < 0)
67962306a36Sopenharmony_ci                return err;
68062306a36Sopenharmony_ci	return 0;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci/* close callback */
68462306a36Sopenharmony_cistatic int snd_ca0106_pcm_close_capture(struct snd_pcm_substream *substream)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct snd_ca0106 *chip = snd_pcm_substream_chip(substream);
68762306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
68862306a36Sopenharmony_ci        struct snd_ca0106_pcm *epcm = runtime->private_data;
68962306a36Sopenharmony_ci	chip->capture_channels[epcm->channel_id].use = 0;
69062306a36Sopenharmony_ci	/* FIXME: maybe zero others */
69162306a36Sopenharmony_ci	return 0;
69262306a36Sopenharmony_ci}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistatic int snd_ca0106_pcm_open_0_capture(struct snd_pcm_substream *substream)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	return snd_ca0106_pcm_open_capture_channel(substream, 0);
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic int snd_ca0106_pcm_open_1_capture(struct snd_pcm_substream *substream)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	return snd_ca0106_pcm_open_capture_channel(substream, 1);
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic int snd_ca0106_pcm_open_2_capture(struct snd_pcm_substream *substream)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	return snd_ca0106_pcm_open_capture_channel(substream, 2);
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_cistatic int snd_ca0106_pcm_open_3_capture(struct snd_pcm_substream *substream)
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	return snd_ca0106_pcm_open_capture_channel(substream, 3);
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci/* prepare playback callback */
71562306a36Sopenharmony_cistatic int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
71862306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
71962306a36Sopenharmony_ci	struct snd_ca0106_pcm *epcm = runtime->private_data;
72062306a36Sopenharmony_ci	int channel = epcm->channel_id;
72162306a36Sopenharmony_ci	u32 *table_base = (u32 *)(emu->buffer->area+(8*16*channel));
72262306a36Sopenharmony_ci	u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
72362306a36Sopenharmony_ci	u32 hcfg_mask = HCFG_PLAYBACK_S32_LE;
72462306a36Sopenharmony_ci	u32 hcfg_set = 0x00000000;
72562306a36Sopenharmony_ci	u32 hcfg;
72662306a36Sopenharmony_ci	u32 reg40_mask = 0x30000 << (channel<<1);
72762306a36Sopenharmony_ci	u32 reg40_set = 0;
72862306a36Sopenharmony_ci	u32 reg40;
72962306a36Sopenharmony_ci	/* FIXME: Depending on mixer selection of SPDIF out or not, select the spdif rate or the DAC rate. */
73062306a36Sopenharmony_ci	u32 reg71_mask = 0x03030000 ; /* Global. Set SPDIF rate. We only support 44100 to spdif, not to DAC. */
73162306a36Sopenharmony_ci	u32 reg71_set = 0;
73262306a36Sopenharmony_ci	u32 reg71;
73362306a36Sopenharmony_ci	int i;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci#if 0 /* debug */
73662306a36Sopenharmony_ci	dev_dbg(emu->card->dev,
73762306a36Sopenharmony_ci		   "prepare:channel_number=%d, rate=%d, format=0x%x, "
73862306a36Sopenharmony_ci		   "channels=%d, buffer_size=%ld, period_size=%ld, "
73962306a36Sopenharmony_ci		   "periods=%u, frames_to_bytes=%d\n",
74062306a36Sopenharmony_ci		   channel, runtime->rate, runtime->format,
74162306a36Sopenharmony_ci		   runtime->channels, runtime->buffer_size,
74262306a36Sopenharmony_ci		   runtime->period_size, runtime->periods,
74362306a36Sopenharmony_ci		   frames_to_bytes(runtime, 1));
74462306a36Sopenharmony_ci	dev_dbg(emu->card->dev,
74562306a36Sopenharmony_ci		"dma_addr=%x, dma_area=%p, table_base=%p\n",
74662306a36Sopenharmony_ci		   runtime->dma_addr, runtime->dma_area, table_base);
74762306a36Sopenharmony_ci	dev_dbg(emu->card->dev,
74862306a36Sopenharmony_ci		"dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
74962306a36Sopenharmony_ci		   emu->buffer->addr, emu->buffer->area, emu->buffer->bytes);
75062306a36Sopenharmony_ci#endif /* debug */
75162306a36Sopenharmony_ci	/* Rate can be set per channel. */
75262306a36Sopenharmony_ci	/* reg40 control host to fifo */
75362306a36Sopenharmony_ci	/* reg71 controls DAC rate. */
75462306a36Sopenharmony_ci	switch (runtime->rate) {
75562306a36Sopenharmony_ci	case 44100:
75662306a36Sopenharmony_ci		reg40_set = 0x10000 << (channel<<1);
75762306a36Sopenharmony_ci		reg71_set = 0x01010000;
75862306a36Sopenharmony_ci		break;
75962306a36Sopenharmony_ci        case 48000:
76062306a36Sopenharmony_ci		reg40_set = 0;
76162306a36Sopenharmony_ci		reg71_set = 0;
76262306a36Sopenharmony_ci		break;
76362306a36Sopenharmony_ci	case 96000:
76462306a36Sopenharmony_ci		reg40_set = 0x20000 << (channel<<1);
76562306a36Sopenharmony_ci		reg71_set = 0x02020000;
76662306a36Sopenharmony_ci		break;
76762306a36Sopenharmony_ci	case 192000:
76862306a36Sopenharmony_ci		reg40_set = 0x30000 << (channel<<1);
76962306a36Sopenharmony_ci		reg71_set = 0x03030000;
77062306a36Sopenharmony_ci		break;
77162306a36Sopenharmony_ci	default:
77262306a36Sopenharmony_ci		reg40_set = 0;
77362306a36Sopenharmony_ci		reg71_set = 0;
77462306a36Sopenharmony_ci		break;
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci	/* Format is a global setting */
77762306a36Sopenharmony_ci	/* FIXME: Only let the first channel accessed set this. */
77862306a36Sopenharmony_ci	switch (runtime->format) {
77962306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
78062306a36Sopenharmony_ci		hcfg_set = 0;
78162306a36Sopenharmony_ci		break;
78262306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S32_LE:
78362306a36Sopenharmony_ci		hcfg_set = HCFG_PLAYBACK_S32_LE;
78462306a36Sopenharmony_ci		break;
78562306a36Sopenharmony_ci	default:
78662306a36Sopenharmony_ci		hcfg_set = 0;
78762306a36Sopenharmony_ci		break;
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci	hcfg = inl(emu->port + CA0106_HCFG) ;
79062306a36Sopenharmony_ci	hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
79162306a36Sopenharmony_ci	outl(hcfg, emu->port + CA0106_HCFG);
79262306a36Sopenharmony_ci	reg40 = snd_ca0106_ptr_read(emu, 0x40, 0);
79362306a36Sopenharmony_ci	reg40 = (reg40 & ~reg40_mask) | reg40_set;
79462306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, 0x40, 0, reg40);
79562306a36Sopenharmony_ci	reg71 = snd_ca0106_ptr_read(emu, 0x71, 0);
79662306a36Sopenharmony_ci	reg71 = (reg71 & ~reg71_mask) | reg71_set;
79762306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, 0x71, 0, reg71);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	/* FIXME: Check emu->buffer->size before actually writing to it. */
80062306a36Sopenharmony_ci        for(i=0; i < runtime->periods; i++) {
80162306a36Sopenharmony_ci		table_base[i*2] = runtime->dma_addr + (i * period_size_bytes);
80262306a36Sopenharmony_ci		table_base[i*2+1] = period_size_bytes << 16;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, PLAYBACK_LIST_ADDR, channel, emu->buffer->addr+(8*16*channel));
80662306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
80762306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, PLAYBACK_LIST_PTR, channel, 0);
80862306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
80962306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
81062306a36Sopenharmony_ci	/* FIXME  test what 0 bytes does. */
81162306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, PLAYBACK_PERIOD_SIZE, channel, 0); // buffer size in bytes
81262306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, PLAYBACK_POINTER, channel, 0);
81362306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, 0x07, channel, 0x0);
81462306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, 0x08, channel, 0);
81562306a36Sopenharmony_ci        snd_ca0106_ptr_write(emu, PLAYBACK_MUTE, 0x0, 0x0); /* Unmute output */
81662306a36Sopenharmony_ci#if 0
81762306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, SPCS0, 0,
81862306a36Sopenharmony_ci			       SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
81962306a36Sopenharmony_ci			       SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
82062306a36Sopenharmony_ci			       SPCS_GENERATIONSTATUS | 0x00001200 |
82162306a36Sopenharmony_ci			       0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT );
82262306a36Sopenharmony_ci#endif
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	return 0;
82562306a36Sopenharmony_ci}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci/* prepare capture callback */
82862306a36Sopenharmony_cistatic int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
83162306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
83262306a36Sopenharmony_ci	struct snd_ca0106_pcm *epcm = runtime->private_data;
83362306a36Sopenharmony_ci	int channel = epcm->channel_id;
83462306a36Sopenharmony_ci	u32 hcfg_mask = HCFG_CAPTURE_S32_LE;
83562306a36Sopenharmony_ci	u32 hcfg_set = 0x00000000;
83662306a36Sopenharmony_ci	u32 hcfg;
83762306a36Sopenharmony_ci	u32 over_sampling=0x2;
83862306a36Sopenharmony_ci	u32 reg71_mask = 0x0000c000 ; /* Global. Set ADC rate. */
83962306a36Sopenharmony_ci	u32 reg71_set = 0;
84062306a36Sopenharmony_ci	u32 reg71;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci#if 0 /* debug */
84362306a36Sopenharmony_ci	dev_dbg(emu->card->dev,
84462306a36Sopenharmony_ci		   "prepare:channel_number=%d, rate=%d, format=0x%x, "
84562306a36Sopenharmony_ci		   "channels=%d, buffer_size=%ld, period_size=%ld, "
84662306a36Sopenharmony_ci		   "periods=%u, frames_to_bytes=%d\n",
84762306a36Sopenharmony_ci		   channel, runtime->rate, runtime->format,
84862306a36Sopenharmony_ci		   runtime->channels, runtime->buffer_size,
84962306a36Sopenharmony_ci		   runtime->period_size, runtime->periods,
85062306a36Sopenharmony_ci		   frames_to_bytes(runtime, 1));
85162306a36Sopenharmony_ci	dev_dbg(emu->card->dev,
85262306a36Sopenharmony_ci		"dma_addr=%x, dma_area=%p, table_base=%p\n",
85362306a36Sopenharmony_ci		   runtime->dma_addr, runtime->dma_area, table_base);
85462306a36Sopenharmony_ci	dev_dbg(emu->card->dev,
85562306a36Sopenharmony_ci		"dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
85662306a36Sopenharmony_ci		   emu->buffer->addr, emu->buffer->area, emu->buffer->bytes);
85762306a36Sopenharmony_ci#endif /* debug */
85862306a36Sopenharmony_ci	/* reg71 controls ADC rate. */
85962306a36Sopenharmony_ci	switch (runtime->rate) {
86062306a36Sopenharmony_ci	case 44100:
86162306a36Sopenharmony_ci		reg71_set = 0x00004000;
86262306a36Sopenharmony_ci		break;
86362306a36Sopenharmony_ci        case 48000:
86462306a36Sopenharmony_ci		reg71_set = 0;
86562306a36Sopenharmony_ci		break;
86662306a36Sopenharmony_ci	case 96000:
86762306a36Sopenharmony_ci		reg71_set = 0x00008000;
86862306a36Sopenharmony_ci		over_sampling=0xa;
86962306a36Sopenharmony_ci		break;
87062306a36Sopenharmony_ci	case 192000:
87162306a36Sopenharmony_ci		reg71_set = 0x0000c000;
87262306a36Sopenharmony_ci		over_sampling=0xa;
87362306a36Sopenharmony_ci		break;
87462306a36Sopenharmony_ci	default:
87562306a36Sopenharmony_ci		reg71_set = 0;
87662306a36Sopenharmony_ci		break;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci	/* Format is a global setting */
87962306a36Sopenharmony_ci	/* FIXME: Only let the first channel accessed set this. */
88062306a36Sopenharmony_ci	switch (runtime->format) {
88162306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
88262306a36Sopenharmony_ci		hcfg_set = 0;
88362306a36Sopenharmony_ci		break;
88462306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S32_LE:
88562306a36Sopenharmony_ci		hcfg_set = HCFG_CAPTURE_S32_LE;
88662306a36Sopenharmony_ci		break;
88762306a36Sopenharmony_ci	default:
88862306a36Sopenharmony_ci		hcfg_set = 0;
88962306a36Sopenharmony_ci		break;
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci	hcfg = inl(emu->port + CA0106_HCFG) ;
89262306a36Sopenharmony_ci	hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
89362306a36Sopenharmony_ci	outl(hcfg, emu->port + CA0106_HCFG);
89462306a36Sopenharmony_ci	reg71 = snd_ca0106_ptr_read(emu, 0x71, 0);
89562306a36Sopenharmony_ci	reg71 = (reg71 & ~reg71_mask) | reg71_set;
89662306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, 0x71, 0, reg71);
89762306a36Sopenharmony_ci        if (emu->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */
89862306a36Sopenharmony_ci	        snd_ca0106_i2c_write(emu, ADC_MASTER, over_sampling); /* Adjust the over sampler to better suit the capture rate. */
89962306a36Sopenharmony_ci	}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	/*
90362306a36Sopenharmony_ci	dev_dbg(emu->card->dev,
90462306a36Sopenharmony_ci	       "prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, "
90562306a36Sopenharmony_ci	       "buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",
90662306a36Sopenharmony_ci	       channel, runtime->rate, runtime->format, runtime->channels,
90762306a36Sopenharmony_ci	       runtime->buffer_size, runtime->period_size,
90862306a36Sopenharmony_ci	       frames_to_bytes(runtime, 1));
90962306a36Sopenharmony_ci	*/
91062306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, 0x13, channel, 0);
91162306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
91262306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
91362306a36Sopenharmony_ci	snd_ca0106_ptr_write(emu, CAPTURE_POINTER, channel, 0);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	return 0;
91662306a36Sopenharmony_ci}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci/* trigger_playback callback */
91962306a36Sopenharmony_cistatic int snd_ca0106_pcm_trigger_playback(struct snd_pcm_substream *substream,
92062306a36Sopenharmony_ci				    int cmd)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
92362306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime;
92462306a36Sopenharmony_ci	struct snd_ca0106_pcm *epcm;
92562306a36Sopenharmony_ci	int channel;
92662306a36Sopenharmony_ci	int result = 0;
92762306a36Sopenharmony_ci        struct snd_pcm_substream *s;
92862306a36Sopenharmony_ci	u32 basic = 0;
92962306a36Sopenharmony_ci	u32 extended = 0;
93062306a36Sopenharmony_ci	u32 bits;
93162306a36Sopenharmony_ci	int running = 0;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	switch (cmd) {
93462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
93562306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
93662306a36Sopenharmony_ci		running = 1;
93762306a36Sopenharmony_ci		break;
93862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
93962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
94062306a36Sopenharmony_ci	default:
94162306a36Sopenharmony_ci		running = 0;
94262306a36Sopenharmony_ci		break;
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci        snd_pcm_group_for_each_entry(s, substream) {
94562306a36Sopenharmony_ci		if (snd_pcm_substream_chip(s) != emu ||
94662306a36Sopenharmony_ci		    s->stream != SNDRV_PCM_STREAM_PLAYBACK)
94762306a36Sopenharmony_ci			continue;
94862306a36Sopenharmony_ci		runtime = s->runtime;
94962306a36Sopenharmony_ci		epcm = runtime->private_data;
95062306a36Sopenharmony_ci		channel = epcm->channel_id;
95162306a36Sopenharmony_ci		/* dev_dbg(emu->card->dev, "channel=%d\n", channel); */
95262306a36Sopenharmony_ci		epcm->running = running;
95362306a36Sopenharmony_ci		basic |= (0x1 << channel);
95462306a36Sopenharmony_ci		extended |= (0x10 << channel);
95562306a36Sopenharmony_ci                snd_pcm_trigger_done(s, substream);
95662306a36Sopenharmony_ci        }
95762306a36Sopenharmony_ci	/* dev_dbg(emu->card->dev, "basic=0x%x, extended=0x%x\n",basic, extended); */
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	switch (cmd) {
96062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
96162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
96262306a36Sopenharmony_ci		bits = snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0);
96362306a36Sopenharmony_ci		bits |= extended;
96462306a36Sopenharmony_ci		snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, bits);
96562306a36Sopenharmony_ci		bits = snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0);
96662306a36Sopenharmony_ci		bits |= basic;
96762306a36Sopenharmony_ci		snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, bits);
96862306a36Sopenharmony_ci		break;
96962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
97062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
97162306a36Sopenharmony_ci		bits = snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0);
97262306a36Sopenharmony_ci		bits &= ~basic;
97362306a36Sopenharmony_ci		snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, bits);
97462306a36Sopenharmony_ci		bits = snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0);
97562306a36Sopenharmony_ci		bits &= ~extended;
97662306a36Sopenharmony_ci		snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, bits);
97762306a36Sopenharmony_ci		break;
97862306a36Sopenharmony_ci	default:
97962306a36Sopenharmony_ci		result = -EINVAL;
98062306a36Sopenharmony_ci		break;
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci	return result;
98362306a36Sopenharmony_ci}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci/* trigger_capture callback */
98662306a36Sopenharmony_cistatic int snd_ca0106_pcm_trigger_capture(struct snd_pcm_substream *substream,
98762306a36Sopenharmony_ci				    int cmd)
98862306a36Sopenharmony_ci{
98962306a36Sopenharmony_ci	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
99062306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
99162306a36Sopenharmony_ci	struct snd_ca0106_pcm *epcm = runtime->private_data;
99262306a36Sopenharmony_ci	int channel = epcm->channel_id;
99362306a36Sopenharmony_ci	int result = 0;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	switch (cmd) {
99662306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
99762306a36Sopenharmony_ci		snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<<channel));
99862306a36Sopenharmony_ci		snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0)|(0x100<<channel));
99962306a36Sopenharmony_ci		epcm->running = 1;
100062306a36Sopenharmony_ci		break;
100162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
100262306a36Sopenharmony_ci		snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<<channel));
100362306a36Sopenharmony_ci		snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel));
100462306a36Sopenharmony_ci		epcm->running = 0;
100562306a36Sopenharmony_ci		break;
100662306a36Sopenharmony_ci	default:
100762306a36Sopenharmony_ci		result = -EINVAL;
100862306a36Sopenharmony_ci		break;
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci	return result;
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci/* pointer_playback callback */
101462306a36Sopenharmony_cistatic snd_pcm_uframes_t
101562306a36Sopenharmony_cisnd_ca0106_pcm_pointer_playback(struct snd_pcm_substream *substream)
101662306a36Sopenharmony_ci{
101762306a36Sopenharmony_ci	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
101862306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
101962306a36Sopenharmony_ci	struct snd_ca0106_pcm *epcm = runtime->private_data;
102062306a36Sopenharmony_ci	unsigned int ptr, prev_ptr;
102162306a36Sopenharmony_ci	int channel = epcm->channel_id;
102262306a36Sopenharmony_ci	int timeout = 10;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	if (!epcm->running)
102562306a36Sopenharmony_ci		return 0;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	prev_ptr = -1;
102862306a36Sopenharmony_ci	do {
102962306a36Sopenharmony_ci		ptr = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
103062306a36Sopenharmony_ci		ptr = (ptr >> 3) * runtime->period_size;
103162306a36Sopenharmony_ci		ptr += bytes_to_frames(runtime,
103262306a36Sopenharmony_ci			snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel));
103362306a36Sopenharmony_ci		if (ptr >= runtime->buffer_size)
103462306a36Sopenharmony_ci			ptr -= runtime->buffer_size;
103562306a36Sopenharmony_ci		if (prev_ptr == ptr)
103662306a36Sopenharmony_ci			return ptr;
103762306a36Sopenharmony_ci		prev_ptr = ptr;
103862306a36Sopenharmony_ci	} while (--timeout);
103962306a36Sopenharmony_ci	dev_warn(emu->card->dev, "ca0106: unstable DMA pointer!\n");
104062306a36Sopenharmony_ci	return 0;
104162306a36Sopenharmony_ci}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci/* pointer_capture callback */
104462306a36Sopenharmony_cistatic snd_pcm_uframes_t
104562306a36Sopenharmony_cisnd_ca0106_pcm_pointer_capture(struct snd_pcm_substream *substream)
104662306a36Sopenharmony_ci{
104762306a36Sopenharmony_ci	struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
104862306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
104962306a36Sopenharmony_ci	struct snd_ca0106_pcm *epcm = runtime->private_data;
105062306a36Sopenharmony_ci	snd_pcm_uframes_t ptr, ptr1, ptr2 = 0;
105162306a36Sopenharmony_ci	int channel = epcm->channel_id;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	if (!epcm->running)
105462306a36Sopenharmony_ci		return 0;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	ptr1 = snd_ca0106_ptr_read(emu, CAPTURE_POINTER, channel);
105762306a36Sopenharmony_ci	ptr2 = bytes_to_frames(runtime, ptr1);
105862306a36Sopenharmony_ci	ptr=ptr2;
105962306a36Sopenharmony_ci        if (ptr >= runtime->buffer_size)
106062306a36Sopenharmony_ci		ptr -= runtime->buffer_size;
106162306a36Sopenharmony_ci	/*
106262306a36Sopenharmony_ci	dev_dbg(emu->card->dev, "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
106362306a36Sopenharmony_ci	       "buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n",
106462306a36Sopenharmony_ci	       ptr1, ptr2, ptr, (int)runtime->buffer_size,
106562306a36Sopenharmony_ci	       (int)runtime->period_size, (int)runtime->frame_bits,
106662306a36Sopenharmony_ci	       (int)runtime->rate);
106762306a36Sopenharmony_ci	*/
106862306a36Sopenharmony_ci	return ptr;
106962306a36Sopenharmony_ci}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci/* operators */
107262306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_ca0106_playback_front_ops = {
107362306a36Sopenharmony_ci	.open =        snd_ca0106_pcm_open_playback_front,
107462306a36Sopenharmony_ci	.close =       snd_ca0106_pcm_close_playback,
107562306a36Sopenharmony_ci	.prepare =     snd_ca0106_pcm_prepare_playback,
107662306a36Sopenharmony_ci	.trigger =     snd_ca0106_pcm_trigger_playback,
107762306a36Sopenharmony_ci	.pointer =     snd_ca0106_pcm_pointer_playback,
107862306a36Sopenharmony_ci};
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_ca0106_capture_0_ops = {
108162306a36Sopenharmony_ci	.open =        snd_ca0106_pcm_open_0_capture,
108262306a36Sopenharmony_ci	.close =       snd_ca0106_pcm_close_capture,
108362306a36Sopenharmony_ci	.prepare =     snd_ca0106_pcm_prepare_capture,
108462306a36Sopenharmony_ci	.trigger =     snd_ca0106_pcm_trigger_capture,
108562306a36Sopenharmony_ci	.pointer =     snd_ca0106_pcm_pointer_capture,
108662306a36Sopenharmony_ci};
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_ca0106_capture_1_ops = {
108962306a36Sopenharmony_ci	.open =        snd_ca0106_pcm_open_1_capture,
109062306a36Sopenharmony_ci	.close =       snd_ca0106_pcm_close_capture,
109162306a36Sopenharmony_ci	.prepare =     snd_ca0106_pcm_prepare_capture,
109262306a36Sopenharmony_ci	.trigger =     snd_ca0106_pcm_trigger_capture,
109362306a36Sopenharmony_ci	.pointer =     snd_ca0106_pcm_pointer_capture,
109462306a36Sopenharmony_ci};
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_ca0106_capture_2_ops = {
109762306a36Sopenharmony_ci	.open =        snd_ca0106_pcm_open_2_capture,
109862306a36Sopenharmony_ci	.close =       snd_ca0106_pcm_close_capture,
109962306a36Sopenharmony_ci	.prepare =     snd_ca0106_pcm_prepare_capture,
110062306a36Sopenharmony_ci	.trigger =     snd_ca0106_pcm_trigger_capture,
110162306a36Sopenharmony_ci	.pointer =     snd_ca0106_pcm_pointer_capture,
110262306a36Sopenharmony_ci};
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_ca0106_capture_3_ops = {
110562306a36Sopenharmony_ci	.open =        snd_ca0106_pcm_open_3_capture,
110662306a36Sopenharmony_ci	.close =       snd_ca0106_pcm_close_capture,
110762306a36Sopenharmony_ci	.prepare =     snd_ca0106_pcm_prepare_capture,
110862306a36Sopenharmony_ci	.trigger =     snd_ca0106_pcm_trigger_capture,
110962306a36Sopenharmony_ci	.pointer =     snd_ca0106_pcm_pointer_capture,
111062306a36Sopenharmony_ci};
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_ca0106_playback_center_lfe_ops = {
111362306a36Sopenharmony_ci        .open =         snd_ca0106_pcm_open_playback_center_lfe,
111462306a36Sopenharmony_ci        .close =        snd_ca0106_pcm_close_playback,
111562306a36Sopenharmony_ci        .prepare =      snd_ca0106_pcm_prepare_playback,
111662306a36Sopenharmony_ci        .trigger =      snd_ca0106_pcm_trigger_playback,
111762306a36Sopenharmony_ci        .pointer =      snd_ca0106_pcm_pointer_playback,
111862306a36Sopenharmony_ci};
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_ca0106_playback_unknown_ops = {
112162306a36Sopenharmony_ci        .open =         snd_ca0106_pcm_open_playback_unknown,
112262306a36Sopenharmony_ci        .close =        snd_ca0106_pcm_close_playback,
112362306a36Sopenharmony_ci        .prepare =      snd_ca0106_pcm_prepare_playback,
112462306a36Sopenharmony_ci        .trigger =      snd_ca0106_pcm_trigger_playback,
112562306a36Sopenharmony_ci        .pointer =      snd_ca0106_pcm_pointer_playback,
112662306a36Sopenharmony_ci};
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_ca0106_playback_rear_ops = {
112962306a36Sopenharmony_ci        .open =         snd_ca0106_pcm_open_playback_rear,
113062306a36Sopenharmony_ci        .close =        snd_ca0106_pcm_close_playback,
113162306a36Sopenharmony_ci        .prepare =      snd_ca0106_pcm_prepare_playback,
113262306a36Sopenharmony_ci        .trigger =      snd_ca0106_pcm_trigger_playback,
113362306a36Sopenharmony_ci        .pointer =      snd_ca0106_pcm_pointer_playback,
113462306a36Sopenharmony_ci};
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_cistatic unsigned short snd_ca0106_ac97_read(struct snd_ac97 *ac97,
113862306a36Sopenharmony_ci					     unsigned short reg)
113962306a36Sopenharmony_ci{
114062306a36Sopenharmony_ci	struct snd_ca0106 *emu = ac97->private_data;
114162306a36Sopenharmony_ci	unsigned long flags;
114262306a36Sopenharmony_ci	unsigned short val;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
114562306a36Sopenharmony_ci	outb(reg, emu->port + CA0106_AC97ADDRESS);
114662306a36Sopenharmony_ci	val = inw(emu->port + CA0106_AC97DATA);
114762306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
114862306a36Sopenharmony_ci	return val;
114962306a36Sopenharmony_ci}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_cistatic void snd_ca0106_ac97_write(struct snd_ac97 *ac97,
115262306a36Sopenharmony_ci				    unsigned short reg, unsigned short val)
115362306a36Sopenharmony_ci{
115462306a36Sopenharmony_ci	struct snd_ca0106 *emu = ac97->private_data;
115562306a36Sopenharmony_ci	unsigned long flags;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	spin_lock_irqsave(&emu->emu_lock, flags);
115862306a36Sopenharmony_ci	outb(reg, emu->port + CA0106_AC97ADDRESS);
115962306a36Sopenharmony_ci	outw(val, emu->port + CA0106_AC97DATA);
116062306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->emu_lock, flags);
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_cistatic int snd_ca0106_ac97(struct snd_ca0106 *chip)
116462306a36Sopenharmony_ci{
116562306a36Sopenharmony_ci	struct snd_ac97_bus *pbus;
116662306a36Sopenharmony_ci	struct snd_ac97_template ac97;
116762306a36Sopenharmony_ci	int err;
116862306a36Sopenharmony_ci	static const struct snd_ac97_bus_ops ops = {
116962306a36Sopenharmony_ci		.write = snd_ca0106_ac97_write,
117062306a36Sopenharmony_ci		.read = snd_ca0106_ac97_read,
117162306a36Sopenharmony_ci	};
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus);
117462306a36Sopenharmony_ci	if (err < 0)
117562306a36Sopenharmony_ci		return err;
117662306a36Sopenharmony_ci	pbus->no_vra = 1; /* we don't need VRA */
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	memset(&ac97, 0, sizeof(ac97));
117962306a36Sopenharmony_ci	ac97.private_data = chip;
118062306a36Sopenharmony_ci	ac97.scaps = AC97_SCAP_NO_SPDIF;
118162306a36Sopenharmony_ci	return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
118262306a36Sopenharmony_ci}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_cistatic void ca0106_stop_chip(struct snd_ca0106 *chip);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_cistatic void snd_ca0106_free(struct snd_card *card)
118762306a36Sopenharmony_ci{
118862306a36Sopenharmony_ci	struct snd_ca0106 *chip = card->private_data;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	ca0106_stop_chip(chip);
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_cistatic irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
119462306a36Sopenharmony_ci{
119562306a36Sopenharmony_ci	unsigned int status;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	struct snd_ca0106 *chip = dev_id;
119862306a36Sopenharmony_ci	int i;
119962306a36Sopenharmony_ci	int mask;
120062306a36Sopenharmony_ci        unsigned int stat76;
120162306a36Sopenharmony_ci	struct snd_ca0106_channel *pchannel;
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	status = inl(chip->port + CA0106_IPR);
120462306a36Sopenharmony_ci	if (! status)
120562306a36Sopenharmony_ci		return IRQ_NONE;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci        stat76 = snd_ca0106_ptr_read(chip, EXTENDED_INT, 0);
120862306a36Sopenharmony_ci	/*
120962306a36Sopenharmony_ci	dev_dbg(emu->card->dev, "interrupt status = 0x%08x, stat76=0x%08x\n",
121062306a36Sopenharmony_ci		   status, stat76);
121162306a36Sopenharmony_ci	dev_dbg(emu->card->dev, "ptr=0x%08x\n",
121262306a36Sopenharmony_ci		   snd_ca0106_ptr_read(chip, PLAYBACK_POINTER, 0));
121362306a36Sopenharmony_ci	*/
121462306a36Sopenharmony_ci        mask = 0x11; /* 0x1 for one half, 0x10 for the other half period. */
121562306a36Sopenharmony_ci	for(i = 0; i < 4; i++) {
121662306a36Sopenharmony_ci		pchannel = &(chip->playback_channels[i]);
121762306a36Sopenharmony_ci		if (stat76 & mask) {
121862306a36Sopenharmony_ci/* FIXME: Select the correct substream for period elapsed */
121962306a36Sopenharmony_ci			if(pchannel->use) {
122062306a36Sopenharmony_ci				snd_pcm_period_elapsed(pchannel->epcm->substream);
122162306a36Sopenharmony_ci				/* dev_dbg(emu->card->dev, "interrupt [%d] used\n", i); */
122262306a36Sopenharmony_ci                        }
122362306a36Sopenharmony_ci		}
122462306a36Sopenharmony_ci		/*
122562306a36Sopenharmony_ci		dev_dbg(emu->card->dev, "channel=%p\n", pchannel);
122662306a36Sopenharmony_ci		dev_dbg(emu->card->dev, "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
122762306a36Sopenharmony_ci		*/
122862306a36Sopenharmony_ci		mask <<= 1;
122962306a36Sopenharmony_ci	}
123062306a36Sopenharmony_ci        mask = 0x110000; /* 0x1 for one half, 0x10 for the other half period. */
123162306a36Sopenharmony_ci	for(i = 0; i < 4; i++) {
123262306a36Sopenharmony_ci		pchannel = &(chip->capture_channels[i]);
123362306a36Sopenharmony_ci		if (stat76 & mask) {
123462306a36Sopenharmony_ci/* FIXME: Select the correct substream for period elapsed */
123562306a36Sopenharmony_ci			if(pchannel->use) {
123662306a36Sopenharmony_ci				snd_pcm_period_elapsed(pchannel->epcm->substream);
123762306a36Sopenharmony_ci				/* dev_dbg(emu->card->dev, "interrupt [%d] used\n", i); */
123862306a36Sopenharmony_ci                        }
123962306a36Sopenharmony_ci		}
124062306a36Sopenharmony_ci		/*
124162306a36Sopenharmony_ci		dev_dbg(emu->card->dev, "channel=%p\n", pchannel);
124262306a36Sopenharmony_ci		dev_dbg(emu->card->dev, "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
124362306a36Sopenharmony_ci		*/
124462306a36Sopenharmony_ci		mask <<= 1;
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci        snd_ca0106_ptr_write(chip, EXTENDED_INT, 0, stat76);
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	if (chip->midi.dev_id &&
125062306a36Sopenharmony_ci	    (status & (chip->midi.ipr_tx|chip->midi.ipr_rx))) {
125162306a36Sopenharmony_ci		if (chip->midi.interrupt)
125262306a36Sopenharmony_ci			chip->midi.interrupt(&chip->midi, status);
125362306a36Sopenharmony_ci		else
125462306a36Sopenharmony_ci			chip->midi.interrupt_disable(&chip->midi, chip->midi.tx_enable | chip->midi.rx_enable);
125562306a36Sopenharmony_ci	}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	// acknowledge the interrupt if necessary
125862306a36Sopenharmony_ci	outl(status, chip->port + CA0106_IPR);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	return IRQ_HANDLED;
126162306a36Sopenharmony_ci}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_cistatic const struct snd_pcm_chmap_elem surround_map[] = {
126462306a36Sopenharmony_ci	{ .channels = 2,
126562306a36Sopenharmony_ci	  .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
126662306a36Sopenharmony_ci	{ }
126762306a36Sopenharmony_ci};
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_cistatic const struct snd_pcm_chmap_elem clfe_map[] = {
127062306a36Sopenharmony_ci	{ .channels = 2,
127162306a36Sopenharmony_ci	  .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
127262306a36Sopenharmony_ci	{ }
127362306a36Sopenharmony_ci};
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_cistatic const struct snd_pcm_chmap_elem side_map[] = {
127662306a36Sopenharmony_ci	{ .channels = 2,
127762306a36Sopenharmony_ci	  .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
127862306a36Sopenharmony_ci	{ }
127962306a36Sopenharmony_ci};
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_cistatic int snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
128262306a36Sopenharmony_ci{
128362306a36Sopenharmony_ci	struct snd_pcm *pcm;
128462306a36Sopenharmony_ci	struct snd_pcm_substream *substream;
128562306a36Sopenharmony_ci	const struct snd_pcm_chmap_elem *map = NULL;
128662306a36Sopenharmony_ci	int err;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm);
128962306a36Sopenharmony_ci	if (err < 0)
129062306a36Sopenharmony_ci		return err;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	pcm->private_data = emu;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	switch (device) {
129562306a36Sopenharmony_ci	case 0:
129662306a36Sopenharmony_ci	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_front_ops);
129762306a36Sopenharmony_ci	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_0_ops);
129862306a36Sopenharmony_ci	  map = snd_pcm_std_chmaps;
129962306a36Sopenharmony_ci          break;
130062306a36Sopenharmony_ci	case 1:
130162306a36Sopenharmony_ci	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_rear_ops);
130262306a36Sopenharmony_ci	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_1_ops);
130362306a36Sopenharmony_ci	  map = surround_map;
130462306a36Sopenharmony_ci          break;
130562306a36Sopenharmony_ci	case 2:
130662306a36Sopenharmony_ci	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_center_lfe_ops);
130762306a36Sopenharmony_ci	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_2_ops);
130862306a36Sopenharmony_ci	  map = clfe_map;
130962306a36Sopenharmony_ci          break;
131062306a36Sopenharmony_ci	case 3:
131162306a36Sopenharmony_ci	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_unknown_ops);
131262306a36Sopenharmony_ci	  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_3_ops);
131362306a36Sopenharmony_ci	  map = side_map;
131462306a36Sopenharmony_ci          break;
131562306a36Sopenharmony_ci        }
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	pcm->info_flags = 0;
131862306a36Sopenharmony_ci	strcpy(pcm->name, "CA0106");
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
132162306a36Sopenharmony_ci	    substream;
132262306a36Sopenharmony_ci	    substream = substream->next) {
132362306a36Sopenharmony_ci		snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
132462306a36Sopenharmony_ci					   &emu->pci->dev,
132562306a36Sopenharmony_ci					   64*1024, 64*1024);
132662306a36Sopenharmony_ci	}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
132962306a36Sopenharmony_ci	      substream;
133062306a36Sopenharmony_ci	      substream = substream->next) {
133162306a36Sopenharmony_ci		snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
133262306a36Sopenharmony_ci					   &emu->pci->dev,
133362306a36Sopenharmony_ci					   64*1024, 64*1024);
133462306a36Sopenharmony_ci	}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2,
133762306a36Sopenharmony_ci				     1 << 2, NULL);
133862306a36Sopenharmony_ci	if (err < 0)
133962306a36Sopenharmony_ci		return err;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	emu->pcm[device] = pcm;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	return 0;
134462306a36Sopenharmony_ci}
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci#define SPI_REG(reg, value)	(((reg) << SPI_REG_SHIFT) | (value))
134762306a36Sopenharmony_cistatic const unsigned int spi_dac_init[] = {
134862306a36Sopenharmony_ci	SPI_REG(SPI_LDA1_REG,	SPI_DA_BIT_0dB), /* 0dB dig. attenuation */
134962306a36Sopenharmony_ci	SPI_REG(SPI_RDA1_REG,	SPI_DA_BIT_0dB),
135062306a36Sopenharmony_ci	SPI_REG(SPI_PL_REG,	SPI_PL_BIT_L_L | SPI_PL_BIT_R_R | SPI_IZD_BIT),
135162306a36Sopenharmony_ci	SPI_REG(SPI_FMT_REG,	SPI_FMT_BIT_I2S | SPI_IWL_BIT_24),
135262306a36Sopenharmony_ci	SPI_REG(SPI_LDA2_REG,	SPI_DA_BIT_0dB),
135362306a36Sopenharmony_ci	SPI_REG(SPI_RDA2_REG,	SPI_DA_BIT_0dB),
135462306a36Sopenharmony_ci	SPI_REG(SPI_LDA3_REG,	SPI_DA_BIT_0dB),
135562306a36Sopenharmony_ci	SPI_REG(SPI_RDA3_REG,	SPI_DA_BIT_0dB),
135662306a36Sopenharmony_ci	SPI_REG(SPI_MASTDA_REG,	SPI_DA_BIT_0dB),
135762306a36Sopenharmony_ci	SPI_REG(9,		0x00),
135862306a36Sopenharmony_ci	SPI_REG(SPI_MS_REG,	SPI_DACD0_BIT | SPI_DACD1_BIT | SPI_DACD2_BIT),
135962306a36Sopenharmony_ci	SPI_REG(12,		0x00),
136062306a36Sopenharmony_ci	SPI_REG(SPI_LDA4_REG,	SPI_DA_BIT_0dB),
136162306a36Sopenharmony_ci	SPI_REG(SPI_RDA4_REG,	SPI_DA_BIT_0dB | SPI_DA_BIT_UPDATE),
136262306a36Sopenharmony_ci	SPI_REG(SPI_DACD4_REG,	SPI_DACD4_BIT),
136362306a36Sopenharmony_ci};
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_cistatic const unsigned int i2c_adc_init[][2] = {
136662306a36Sopenharmony_ci	{ 0x17, 0x00 }, /* Reset */
136762306a36Sopenharmony_ci	{ 0x07, 0x00 }, /* Timeout */
136862306a36Sopenharmony_ci	{ 0x0b, 0x22 },  /* Interface control */
136962306a36Sopenharmony_ci	{ 0x0c, 0x22 },  /* Master mode control */
137062306a36Sopenharmony_ci	{ 0x0d, 0x08 },  /* Powerdown control */
137162306a36Sopenharmony_ci	{ 0x0e, 0xcf },  /* Attenuation Left  0x01 = -103dB, 0xff = 24dB */
137262306a36Sopenharmony_ci	{ 0x0f, 0xcf },  /* Attenuation Right 0.5dB steps */
137362306a36Sopenharmony_ci	{ 0x10, 0x7b },  /* ALC Control 1 */
137462306a36Sopenharmony_ci	{ 0x11, 0x00 },  /* ALC Control 2 */
137562306a36Sopenharmony_ci	{ 0x12, 0x32 },  /* ALC Control 3 */
137662306a36Sopenharmony_ci	{ 0x13, 0x00 },  /* Noise gate control */
137762306a36Sopenharmony_ci	{ 0x14, 0xa6 },  /* Limiter control */
137862306a36Sopenharmony_ci	{ 0x15, ADC_MUX_LINEIN },  /* ADC Mixer control */
137962306a36Sopenharmony_ci};
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_cistatic void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
138262306a36Sopenharmony_ci{
138362306a36Sopenharmony_ci	int ch;
138462306a36Sopenharmony_ci	unsigned int def_bits;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	outl(0, chip->port + CA0106_INTE);
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	/*
138962306a36Sopenharmony_ci	 *  Init to 0x02109204 :
139062306a36Sopenharmony_ci	 *  Clock accuracy    = 0     (1000ppm)
139162306a36Sopenharmony_ci	 *  Sample Rate       = 2     (48kHz)
139262306a36Sopenharmony_ci	 *  Audio Channel     = 1     (Left of 2)
139362306a36Sopenharmony_ci	 *  Source Number     = 0     (Unspecified)
139462306a36Sopenharmony_ci	 *  Generation Status = 1     (Original for Cat Code 12)
139562306a36Sopenharmony_ci	 *  Cat Code          = 12    (Digital Signal Mixer)
139662306a36Sopenharmony_ci	 *  Mode              = 0     (Mode 0)
139762306a36Sopenharmony_ci	 *  Emphasis          = 0     (None)
139862306a36Sopenharmony_ci	 *  CP                = 1     (Copyright unasserted)
139962306a36Sopenharmony_ci	 *  AN                = 0     (Audio data)
140062306a36Sopenharmony_ci	 *  P                 = 0     (Consumer)
140162306a36Sopenharmony_ci	 */
140262306a36Sopenharmony_ci	def_bits =
140362306a36Sopenharmony_ci		SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
140462306a36Sopenharmony_ci		SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
140562306a36Sopenharmony_ci		SPCS_GENERATIONSTATUS | 0x00001200 |
140662306a36Sopenharmony_ci		0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
140762306a36Sopenharmony_ci	if (!resume) {
140862306a36Sopenharmony_ci		chip->spdif_str_bits[0] = chip->spdif_bits[0] = def_bits;
140962306a36Sopenharmony_ci		chip->spdif_str_bits[1] = chip->spdif_bits[1] = def_bits;
141062306a36Sopenharmony_ci		chip->spdif_str_bits[2] = chip->spdif_bits[2] = def_bits;
141162306a36Sopenharmony_ci		chip->spdif_str_bits[3] = chip->spdif_bits[3] = def_bits;
141262306a36Sopenharmony_ci	}
141362306a36Sopenharmony_ci	/* Only SPCS1 has been tested */
141462306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, SPCS1, 0, chip->spdif_str_bits[1]);
141562306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, SPCS0, 0, chip->spdif_str_bits[0]);
141662306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, SPCS2, 0, chip->spdif_str_bits[2]);
141762306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, SPCS3, 0, chip->spdif_str_bits[3]);
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci        snd_ca0106_ptr_write(chip, PLAYBACK_MUTE, 0, 0x00fc0000);
142062306a36Sopenharmony_ci        snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000);
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci        /* Write 0x8000 to AC97_REC_GAIN to mute it. */
142362306a36Sopenharmony_ci        outb(AC97_REC_GAIN, chip->port + CA0106_AC97ADDRESS);
142462306a36Sopenharmony_ci        outw(0x8000, chip->port + CA0106_AC97DATA);
142562306a36Sopenharmony_ci#if 0 /* FIXME: what are these? */
142662306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006);
142762306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006);
142862306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, 0x43, 0, 0x2108006);
142962306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, 0x44, 0, 0x2108006);
143062306a36Sopenharmony_ci#endif
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	/* OSS drivers set this. */
143362306a36Sopenharmony_ci	/* snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); */
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	/* Analog or Digital output */
143662306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, SPDIF_SELECT1, 0, 0xf);
143762306a36Sopenharmony_ci	/* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers.
143862306a36Sopenharmony_ci	 * Use 0x000f0000 for surround71
143962306a36Sopenharmony_ci	 */
144062306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000);
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	chip->spdif_enable = 0; /* Set digital SPDIF output off */
144362306a36Sopenharmony_ci	/*snd_ca0106_ptr_write(chip, 0x45, 0, 0);*/ /* Analogue out */
144462306a36Sopenharmony_ci	/*snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00);*/ /* Digital out */
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	/* goes to 0x40c80000 when doing SPDIF IN/OUT */
144762306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 0, 0x40c81000);
144862306a36Sopenharmony_ci	/* (Mute) CAPTURE feedback into PLAYBACK volume.
144962306a36Sopenharmony_ci	 * Only lower 16 bits matter.
145062306a36Sopenharmony_ci	 */
145162306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 1, 0xffffffff);
145262306a36Sopenharmony_ci	/* SPDIF IN Volume */
145362306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 2, 0x30300000);
145462306a36Sopenharmony_ci	/* SPDIF IN Volume, 0x70 = (vol & 0x3f) | 0x40 */
145562306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 3, 0x00700000);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING1, 0, 0x32765410);
145862306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING2, 0, 0x76767676);
145962306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, CAPTURE_ROUTING1, 0, 0x32765410);
146062306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, CAPTURE_ROUTING2, 0, 0x76767676);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	for (ch = 0; ch < 4; ch++) {
146362306a36Sopenharmony_ci		/* Only high 16 bits matter */
146462306a36Sopenharmony_ci		snd_ca0106_ptr_write(chip, CAPTURE_VOLUME1, ch, 0x30303030);
146562306a36Sopenharmony_ci		snd_ca0106_ptr_write(chip, CAPTURE_VOLUME2, ch, 0x30303030);
146662306a36Sopenharmony_ci#if 0 /* Mute */
146762306a36Sopenharmony_ci		snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0x40404040);
146862306a36Sopenharmony_ci		snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0x40404040);
146962306a36Sopenharmony_ci		snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff);
147062306a36Sopenharmony_ci		snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff);
147162306a36Sopenharmony_ci#endif
147262306a36Sopenharmony_ci	}
147362306a36Sopenharmony_ci	if (chip->details->i2c_adc == 1) {
147462306a36Sopenharmony_ci	        /* Select MIC, Line in, TAD in, AUX in */
147562306a36Sopenharmony_ci	        snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4);
147662306a36Sopenharmony_ci		/* Default to CAPTURE_SOURCE to i2s in */
147762306a36Sopenharmony_ci		if (!resume)
147862306a36Sopenharmony_ci			chip->capture_source = 3;
147962306a36Sopenharmony_ci	} else if (chip->details->ac97 == 1) {
148062306a36Sopenharmony_ci	        /* Default to AC97 in */
148162306a36Sopenharmony_ci	        snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x444400e4);
148262306a36Sopenharmony_ci		/* Default to CAPTURE_SOURCE to AC97 in */
148362306a36Sopenharmony_ci		if (!resume)
148462306a36Sopenharmony_ci			chip->capture_source = 4;
148562306a36Sopenharmony_ci	} else {
148662306a36Sopenharmony_ci	        /* Select MIC, Line in, TAD in, AUX in */
148762306a36Sopenharmony_ci	        snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4);
148862306a36Sopenharmony_ci		/* Default to Set CAPTURE_SOURCE to i2s in */
148962306a36Sopenharmony_ci		if (!resume)
149062306a36Sopenharmony_ci			chip->capture_source = 3;
149162306a36Sopenharmony_ci	}
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	if (chip->details->gpio_type == 2) {
149462306a36Sopenharmony_ci		/* The SB0438 use GPIO differently. */
149562306a36Sopenharmony_ci		/* FIXME: Still need to find out what the other GPIO bits do.
149662306a36Sopenharmony_ci		 * E.g. For digital spdif out.
149762306a36Sopenharmony_ci		 */
149862306a36Sopenharmony_ci		outl(0x0, chip->port + CA0106_GPIO);
149962306a36Sopenharmony_ci		/* outl(0x00f0e000, chip->port + CA0106_GPIO); */ /* Analog */
150062306a36Sopenharmony_ci		outl(0x005f5301, chip->port + CA0106_GPIO); /* Analog */
150162306a36Sopenharmony_ci	} else if (chip->details->gpio_type == 1) {
150262306a36Sopenharmony_ci		/* The SB0410 and SB0413 use GPIO differently. */
150362306a36Sopenharmony_ci		/* FIXME: Still need to find out what the other GPIO bits do.
150462306a36Sopenharmony_ci		 * E.g. For digital spdif out.
150562306a36Sopenharmony_ci		 */
150662306a36Sopenharmony_ci		outl(0x0, chip->port + CA0106_GPIO);
150762306a36Sopenharmony_ci		/* outl(0x00f0e000, chip->port + CA0106_GPIO); */ /* Analog */
150862306a36Sopenharmony_ci		outl(0x005f5301, chip->port + CA0106_GPIO); /* Analog */
150962306a36Sopenharmony_ci	} else {
151062306a36Sopenharmony_ci		outl(0x0, chip->port + CA0106_GPIO);
151162306a36Sopenharmony_ci		outl(0x005f03a3, chip->port + CA0106_GPIO); /* Analog */
151262306a36Sopenharmony_ci		/* outl(0x005f02a2, chip->port + CA0106_GPIO); */ /* SPDIF */
151362306a36Sopenharmony_ci	}
151462306a36Sopenharmony_ci	snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	/* outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG); */
151762306a36Sopenharmony_ci	/* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
151862306a36Sopenharmony_ci	/* outl(0x00001409, chip->port + CA0106_HCFG); */
151962306a36Sopenharmony_ci	/* outl(0x00000009, chip->port + CA0106_HCFG); */
152062306a36Sopenharmony_ci	/* AC97 2.0, Enable outputs. */
152162306a36Sopenharmony_ci	outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port + CA0106_HCFG);
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	if (chip->details->i2c_adc == 1) {
152462306a36Sopenharmony_ci		/* The SB0410 and SB0413 use I2C to control ADC. */
152562306a36Sopenharmony_ci		int size, n;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci		size = ARRAY_SIZE(i2c_adc_init);
152862306a36Sopenharmony_ci		/* dev_dbg(emu->card->dev, "I2C:array size=0x%x\n", size); */
152962306a36Sopenharmony_ci		for (n = 0; n < size; n++)
153062306a36Sopenharmony_ci			snd_ca0106_i2c_write(chip, i2c_adc_init[n][0],
153162306a36Sopenharmony_ci					     i2c_adc_init[n][1]);
153262306a36Sopenharmony_ci		for (n = 0; n < 4; n++) {
153362306a36Sopenharmony_ci			chip->i2c_capture_volume[n][0] = 0xcf;
153462306a36Sopenharmony_ci			chip->i2c_capture_volume[n][1] = 0xcf;
153562306a36Sopenharmony_ci		}
153662306a36Sopenharmony_ci		chip->i2c_capture_source = 2; /* Line in */
153762306a36Sopenharmony_ci		/* Enable Line-in capture. MIC in currently untested. */
153862306a36Sopenharmony_ci		/* snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); */
153962306a36Sopenharmony_ci	}
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	if (chip->details->spi_dac) {
154262306a36Sopenharmony_ci		/* The SB0570 use SPI to control DAC. */
154362306a36Sopenharmony_ci		int size, n;
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci		size = ARRAY_SIZE(spi_dac_init);
154662306a36Sopenharmony_ci		for (n = 0; n < size; n++) {
154762306a36Sopenharmony_ci			int reg = spi_dac_init[n] >> SPI_REG_SHIFT;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci			snd_ca0106_spi_write(chip, spi_dac_init[n]);
155062306a36Sopenharmony_ci			if (reg < ARRAY_SIZE(chip->spi_dac_reg))
155162306a36Sopenharmony_ci				chip->spi_dac_reg[reg] = spi_dac_init[n];
155262306a36Sopenharmony_ci		}
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci		/* Enable front dac only */
155562306a36Sopenharmony_ci		snd_ca0106_pcm_power_dac(chip, PCM_FRONT_CHANNEL, 1);
155662306a36Sopenharmony_ci	}
155762306a36Sopenharmony_ci}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_cistatic void ca0106_stop_chip(struct snd_ca0106 *chip)
156062306a36Sopenharmony_ci{
156162306a36Sopenharmony_ci	/* disable interrupts */
156262306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
156362306a36Sopenharmony_ci	outl(0, chip->port + CA0106_INTE);
156462306a36Sopenharmony_ci	snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
156562306a36Sopenharmony_ci	udelay(1000);
156662306a36Sopenharmony_ci	/* disable audio */
156762306a36Sopenharmony_ci	/* outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); */
156862306a36Sopenharmony_ci	outl(0, chip->port + CA0106_HCFG);
156962306a36Sopenharmony_ci	/* FIXME: We need to stop and DMA transfers here.
157062306a36Sopenharmony_ci	 *        But as I am not sure how yet, we cannot from the dma pages.
157162306a36Sopenharmony_ci	 * So we can fix: snd-malloc: Memory leak?  pages not freed = 8
157262306a36Sopenharmony_ci	 */
157362306a36Sopenharmony_ci}
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_cistatic int snd_ca0106_create(int dev, struct snd_card *card,
157662306a36Sopenharmony_ci			     struct pci_dev *pci)
157762306a36Sopenharmony_ci{
157862306a36Sopenharmony_ci	struct snd_ca0106 *chip = card->private_data;
157962306a36Sopenharmony_ci	const struct snd_ca0106_details *c;
158062306a36Sopenharmony_ci	int err;
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	err = pcim_enable_device(pci);
158362306a36Sopenharmony_ci	if (err < 0)
158462306a36Sopenharmony_ci		return err;
158562306a36Sopenharmony_ci	if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
158662306a36Sopenharmony_ci		dev_err(card->dev, "error to set 32bit mask DMA\n");
158762306a36Sopenharmony_ci		return -ENXIO;
158862306a36Sopenharmony_ci	}
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	chip->card = card;
159162306a36Sopenharmony_ci	chip->pci = pci;
159262306a36Sopenharmony_ci	chip->irq = -1;
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	spin_lock_init(&chip->emu_lock);
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	err = pci_request_regions(pci, "snd_ca0106");
159762306a36Sopenharmony_ci	if (err < 0)
159862306a36Sopenharmony_ci		return err;
159962306a36Sopenharmony_ci	chip->port = pci_resource_start(pci, 0);
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	if (devm_request_irq(&pci->dev, pci->irq, snd_ca0106_interrupt,
160262306a36Sopenharmony_ci			     IRQF_SHARED, KBUILD_MODNAME, chip)) {
160362306a36Sopenharmony_ci		dev_err(card->dev, "cannot grab irq\n");
160462306a36Sopenharmony_ci		return -EBUSY;
160562306a36Sopenharmony_ci	}
160662306a36Sopenharmony_ci	chip->irq = pci->irq;
160762306a36Sopenharmony_ci	card->sync_irq = chip->irq;
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	/* This stores the periods table. */
161062306a36Sopenharmony_ci	chip->buffer = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV, 1024);
161162306a36Sopenharmony_ci	if (!chip->buffer)
161262306a36Sopenharmony_ci		return -ENOMEM;
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	pci_set_master(pci);
161562306a36Sopenharmony_ci	/* read serial */
161662306a36Sopenharmony_ci	pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
161762306a36Sopenharmony_ci	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
161862306a36Sopenharmony_ci	dev_info(card->dev, "Model %04x Rev %08x Serial %08x\n",
161962306a36Sopenharmony_ci	       chip->model, pci->revision, chip->serial);
162062306a36Sopenharmony_ci	strcpy(card->driver, "CA0106");
162162306a36Sopenharmony_ci	strcpy(card->shortname, "CA0106");
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	for (c = ca0106_chip_details; c->serial; c++) {
162462306a36Sopenharmony_ci		if (subsystem[dev]) {
162562306a36Sopenharmony_ci			if (c->serial == subsystem[dev])
162662306a36Sopenharmony_ci				break;
162762306a36Sopenharmony_ci		} else if (c->serial == chip->serial)
162862306a36Sopenharmony_ci			break;
162962306a36Sopenharmony_ci	}
163062306a36Sopenharmony_ci	chip->details = c;
163162306a36Sopenharmony_ci	if (subsystem[dev]) {
163262306a36Sopenharmony_ci		dev_info(card->dev, "Sound card name=%s, "
163362306a36Sopenharmony_ci		       "subsystem=0x%x. Forced to subsystem=0x%x\n",
163462306a36Sopenharmony_ci		       c->name, chip->serial, subsystem[dev]);
163562306a36Sopenharmony_ci	}
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	sprintf(card->longname, "%s at 0x%lx irq %i",
163862306a36Sopenharmony_ci		c->name, chip->port, chip->irq);
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	ca0106_init_chip(chip, 0);
164162306a36Sopenharmony_ci	return 0;
164262306a36Sopenharmony_ci}
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_cistatic void ca0106_midi_interrupt_enable(struct snd_ca_midi *midi, int intr)
164662306a36Sopenharmony_ci{
164762306a36Sopenharmony_ci	snd_ca0106_intr_enable((struct snd_ca0106 *)(midi->dev_id), intr);
164862306a36Sopenharmony_ci}
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_cistatic void ca0106_midi_interrupt_disable(struct snd_ca_midi *midi, int intr)
165162306a36Sopenharmony_ci{
165262306a36Sopenharmony_ci	snd_ca0106_intr_disable((struct snd_ca0106 *)(midi->dev_id), intr);
165362306a36Sopenharmony_ci}
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_cistatic unsigned char ca0106_midi_read(struct snd_ca_midi *midi, int idx)
165662306a36Sopenharmony_ci{
165762306a36Sopenharmony_ci	return (unsigned char)snd_ca0106_ptr_read((struct snd_ca0106 *)(midi->dev_id),
165862306a36Sopenharmony_ci						  midi->port + idx, 0);
165962306a36Sopenharmony_ci}
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_cistatic void ca0106_midi_write(struct snd_ca_midi *midi, int data, int idx)
166262306a36Sopenharmony_ci{
166362306a36Sopenharmony_ci	snd_ca0106_ptr_write((struct snd_ca0106 *)(midi->dev_id), midi->port + idx, 0, data);
166462306a36Sopenharmony_ci}
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_cistatic struct snd_card *ca0106_dev_id_card(void *dev_id)
166762306a36Sopenharmony_ci{
166862306a36Sopenharmony_ci	return ((struct snd_ca0106 *)dev_id)->card;
166962306a36Sopenharmony_ci}
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_cistatic int ca0106_dev_id_port(void *dev_id)
167262306a36Sopenharmony_ci{
167362306a36Sopenharmony_ci	return ((struct snd_ca0106 *)dev_id)->port;
167462306a36Sopenharmony_ci}
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_cistatic int snd_ca0106_midi(struct snd_ca0106 *chip, unsigned int channel)
167762306a36Sopenharmony_ci{
167862306a36Sopenharmony_ci	struct snd_ca_midi *midi;
167962306a36Sopenharmony_ci	char *name;
168062306a36Sopenharmony_ci	int err;
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	if (channel == CA0106_MIDI_CHAN_B) {
168362306a36Sopenharmony_ci		name = "CA0106 MPU-401 (UART) B";
168462306a36Sopenharmony_ci		midi =  &chip->midi2;
168562306a36Sopenharmony_ci		midi->tx_enable = INTE_MIDI_TX_B;
168662306a36Sopenharmony_ci		midi->rx_enable = INTE_MIDI_RX_B;
168762306a36Sopenharmony_ci		midi->ipr_tx = IPR_MIDI_TX_B;
168862306a36Sopenharmony_ci		midi->ipr_rx = IPR_MIDI_RX_B;
168962306a36Sopenharmony_ci		midi->port = MIDI_UART_B_DATA;
169062306a36Sopenharmony_ci	} else {
169162306a36Sopenharmony_ci		name = "CA0106 MPU-401 (UART)";
169262306a36Sopenharmony_ci		midi =  &chip->midi;
169362306a36Sopenharmony_ci		midi->tx_enable = INTE_MIDI_TX_A;
169462306a36Sopenharmony_ci		midi->rx_enable = INTE_MIDI_TX_B;
169562306a36Sopenharmony_ci		midi->ipr_tx = IPR_MIDI_TX_A;
169662306a36Sopenharmony_ci		midi->ipr_rx = IPR_MIDI_RX_A;
169762306a36Sopenharmony_ci		midi->port = MIDI_UART_A_DATA;
169862306a36Sopenharmony_ci	}
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	midi->reset = CA0106_MPU401_RESET;
170162306a36Sopenharmony_ci	midi->enter_uart = CA0106_MPU401_ENTER_UART;
170262306a36Sopenharmony_ci	midi->ack = CA0106_MPU401_ACK;
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	midi->input_avail = CA0106_MIDI_INPUT_AVAIL;
170562306a36Sopenharmony_ci	midi->output_ready = CA0106_MIDI_OUTPUT_READY;
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	midi->channel = channel;
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	midi->interrupt_enable = ca0106_midi_interrupt_enable;
171062306a36Sopenharmony_ci	midi->interrupt_disable = ca0106_midi_interrupt_disable;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	midi->read = ca0106_midi_read;
171362306a36Sopenharmony_ci	midi->write = ca0106_midi_write;
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	midi->get_dev_id_card = ca0106_dev_id_card;
171662306a36Sopenharmony_ci	midi->get_dev_id_port = ca0106_dev_id_port;
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	midi->dev_id = chip;
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	err = ca_midi_init(chip, midi, 0, name);
172162306a36Sopenharmony_ci	if (err < 0)
172262306a36Sopenharmony_ci		return err;
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	return 0;
172562306a36Sopenharmony_ci}
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_cistatic int __snd_ca0106_probe(struct pci_dev *pci,
172962306a36Sopenharmony_ci			      const struct pci_device_id *pci_id)
173062306a36Sopenharmony_ci{
173162306a36Sopenharmony_ci	static int dev;
173262306a36Sopenharmony_ci	struct snd_card *card;
173362306a36Sopenharmony_ci	struct snd_ca0106 *chip;
173462306a36Sopenharmony_ci	int i, err;
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	if (dev >= SNDRV_CARDS)
173762306a36Sopenharmony_ci		return -ENODEV;
173862306a36Sopenharmony_ci	if (!enable[dev]) {
173962306a36Sopenharmony_ci		dev++;
174062306a36Sopenharmony_ci		return -ENOENT;
174162306a36Sopenharmony_ci	}
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
174462306a36Sopenharmony_ci				sizeof(*chip), &card);
174562306a36Sopenharmony_ci	if (err < 0)
174662306a36Sopenharmony_ci		return err;
174762306a36Sopenharmony_ci	chip = card->private_data;
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	err = snd_ca0106_create(dev, card, pci);
175062306a36Sopenharmony_ci	if (err < 0)
175162306a36Sopenharmony_ci		return err;
175262306a36Sopenharmony_ci	card->private_free = snd_ca0106_free;
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
175562306a36Sopenharmony_ci		err = snd_ca0106_pcm(chip, i);
175662306a36Sopenharmony_ci		if (err < 0)
175762306a36Sopenharmony_ci			return err;
175862306a36Sopenharmony_ci	}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	if (chip->details->ac97 == 1) {
176162306a36Sopenharmony_ci		/* The SB0410 and SB0413 do not have an AC97 chip. */
176262306a36Sopenharmony_ci		err = snd_ca0106_ac97(chip);
176362306a36Sopenharmony_ci		if (err < 0)
176462306a36Sopenharmony_ci			return err;
176562306a36Sopenharmony_ci	}
176662306a36Sopenharmony_ci	err = snd_ca0106_mixer(chip);
176762306a36Sopenharmony_ci	if (err < 0)
176862306a36Sopenharmony_ci		return err;
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	dev_dbg(card->dev, "probe for MIDI channel A ...");
177162306a36Sopenharmony_ci	err = snd_ca0106_midi(chip, CA0106_MIDI_CHAN_A);
177262306a36Sopenharmony_ci	if (err < 0)
177362306a36Sopenharmony_ci		return err;
177462306a36Sopenharmony_ci	dev_dbg(card->dev, " done.\n");
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci#ifdef CONFIG_SND_PROC_FS
177762306a36Sopenharmony_ci	snd_ca0106_proc_init(chip);
177862306a36Sopenharmony_ci#endif
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	err = snd_card_register(card);
178162306a36Sopenharmony_ci	if (err < 0)
178262306a36Sopenharmony_ci		return err;
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	pci_set_drvdata(pci, card);
178562306a36Sopenharmony_ci	dev++;
178662306a36Sopenharmony_ci	return 0;
178762306a36Sopenharmony_ci}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_cistatic int snd_ca0106_probe(struct pci_dev *pci,
179062306a36Sopenharmony_ci			    const struct pci_device_id *pci_id)
179162306a36Sopenharmony_ci{
179262306a36Sopenharmony_ci	return snd_card_free_on_error(&pci->dev, __snd_ca0106_probe(pci, pci_id));
179362306a36Sopenharmony_ci}
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
179662306a36Sopenharmony_cistatic int snd_ca0106_suspend(struct device *dev)
179762306a36Sopenharmony_ci{
179862306a36Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
179962306a36Sopenharmony_ci	struct snd_ca0106 *chip = card->private_data;
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
180262306a36Sopenharmony_ci	if (chip->details->ac97)
180362306a36Sopenharmony_ci		snd_ac97_suspend(chip->ac97);
180462306a36Sopenharmony_ci	snd_ca0106_mixer_suspend(chip);
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	ca0106_stop_chip(chip);
180762306a36Sopenharmony_ci	return 0;
180862306a36Sopenharmony_ci}
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_cistatic int snd_ca0106_resume(struct device *dev)
181162306a36Sopenharmony_ci{
181262306a36Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
181362306a36Sopenharmony_ci	struct snd_ca0106 *chip = card->private_data;
181462306a36Sopenharmony_ci	int i;
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	ca0106_init_chip(chip, 1);
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	if (chip->details->ac97)
181962306a36Sopenharmony_ci		snd_ac97_resume(chip->ac97);
182062306a36Sopenharmony_ci	snd_ca0106_mixer_resume(chip);
182162306a36Sopenharmony_ci	if (chip->details->spi_dac) {
182262306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(chip->spi_dac_reg); i++)
182362306a36Sopenharmony_ci			snd_ca0106_spi_write(chip, chip->spi_dac_reg[i]);
182462306a36Sopenharmony_ci	}
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
182762306a36Sopenharmony_ci	return 0;
182862306a36Sopenharmony_ci}
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(snd_ca0106_pm, snd_ca0106_suspend, snd_ca0106_resume);
183162306a36Sopenharmony_ci#define SND_CA0106_PM_OPS	&snd_ca0106_pm
183262306a36Sopenharmony_ci#else
183362306a36Sopenharmony_ci#define SND_CA0106_PM_OPS	NULL
183462306a36Sopenharmony_ci#endif
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci// PCI IDs
183762306a36Sopenharmony_cistatic const struct pci_device_id snd_ca0106_ids[] = {
183862306a36Sopenharmony_ci	{ PCI_VDEVICE(CREATIVE, 0x0007), 0 },	/* Audigy LS or Live 24bit */
183962306a36Sopenharmony_ci	{ 0, }
184062306a36Sopenharmony_ci};
184162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_ca0106_ids);
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci// pci_driver definition
184462306a36Sopenharmony_cistatic struct pci_driver ca0106_driver = {
184562306a36Sopenharmony_ci	.name = KBUILD_MODNAME,
184662306a36Sopenharmony_ci	.id_table = snd_ca0106_ids,
184762306a36Sopenharmony_ci	.probe = snd_ca0106_probe,
184862306a36Sopenharmony_ci	.driver = {
184962306a36Sopenharmony_ci		.pm = SND_CA0106_PM_OPS,
185062306a36Sopenharmony_ci	},
185162306a36Sopenharmony_ci};
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_cimodule_pci_driver(ca0106_driver);
1854