xref: /OK3568_Linux_fs/kernel/sound/pci/emu10k1/p16v.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
4*4882a593Smuzhiyun  *  Driver p16v chips
5*4882a593Smuzhiyun  *  Version: 0.25
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *  FEATURES currently supported:
8*4882a593Smuzhiyun  *    Output fixed at S32_LE, 2 channel to hw:0,0
9*4882a593Smuzhiyun  *    Rates: 44.1, 48, 96, 192.
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  *  Changelog:
12*4882a593Smuzhiyun  *  0.8
13*4882a593Smuzhiyun  *    Use separate card based buffer for periods table.
14*4882a593Smuzhiyun  *  0.9
15*4882a593Smuzhiyun  *    Use 2 channel output streams instead of 8 channel.
16*4882a593Smuzhiyun  *       (8 channel output streams might be good for ASIO type output)
17*4882a593Smuzhiyun  *    Corrected speaker output, so Front -> Front etc.
18*4882a593Smuzhiyun  *  0.10
19*4882a593Smuzhiyun  *    Fixed missed interrupts.
20*4882a593Smuzhiyun  *  0.11
21*4882a593Smuzhiyun  *    Add Sound card model number and names.
22*4882a593Smuzhiyun  *    Add Analog volume controls.
23*4882a593Smuzhiyun  *  0.12
24*4882a593Smuzhiyun  *    Corrected playback interrupts. Now interrupt per period, instead of half period.
25*4882a593Smuzhiyun  *  0.13
26*4882a593Smuzhiyun  *    Use single trigger for multichannel.
27*4882a593Smuzhiyun  *  0.14
28*4882a593Smuzhiyun  *    Mic capture now works at fixed: S32_LE, 96000Hz, Stereo.
29*4882a593Smuzhiyun  *  0.15
30*4882a593Smuzhiyun  *    Force buffer_size / period_size == INTEGER.
31*4882a593Smuzhiyun  *  0.16
32*4882a593Smuzhiyun  *    Update p16v.c to work with changed alsa api.
33*4882a593Smuzhiyun  *  0.17
34*4882a593Smuzhiyun  *    Update p16v.c to work with changed alsa api. Removed boot_devs.
35*4882a593Smuzhiyun  *  0.18
36*4882a593Smuzhiyun  *    Merging with snd-emu10k1 driver.
37*4882a593Smuzhiyun  *  0.19
38*4882a593Smuzhiyun  *    One stereo channel at 24bit now works.
39*4882a593Smuzhiyun  *  0.20
40*4882a593Smuzhiyun  *    Added better register defines.
41*4882a593Smuzhiyun  *  0.21
42*4882a593Smuzhiyun  *    Integrated with snd-emu10k1 driver.
43*4882a593Smuzhiyun  *  0.22
44*4882a593Smuzhiyun  *    Removed #if 0 ... #endif
45*4882a593Smuzhiyun  *  0.23
46*4882a593Smuzhiyun  *    Implement different capture rates.
47*4882a593Smuzhiyun  *  0.24
48*4882a593Smuzhiyun  *    Implement different capture source channels.
49*4882a593Smuzhiyun  *    e.g. When HD Capture source is set to SPDIF,
50*4882a593Smuzhiyun  *    setting HD Capture channel to 0 captures from CDROM digital input.
51*4882a593Smuzhiyun  *    setting HD Capture channel to 1 captures from SPDIF in.
52*4882a593Smuzhiyun  *  0.25
53*4882a593Smuzhiyun  *    Include capture buffer sizes.
54*4882a593Smuzhiyun  *
55*4882a593Smuzhiyun  *  BUGS:
56*4882a593Smuzhiyun  *    Some stability problems when unloading the snd-p16v kernel module.
57*4882a593Smuzhiyun  *    --
58*4882a593Smuzhiyun  *
59*4882a593Smuzhiyun  *  TODO:
60*4882a593Smuzhiyun  *    SPDIF out.
61*4882a593Smuzhiyun  *    Find out how to change capture sample rates. E.g. To record SPDIF at 48000Hz.
62*4882a593Smuzhiyun  *    Currently capture fixed at 48000Hz.
63*4882a593Smuzhiyun  *
64*4882a593Smuzhiyun  *    --
65*4882a593Smuzhiyun  *  GENERAL INFO:
66*4882a593Smuzhiyun  *    Model: SB0240
67*4882a593Smuzhiyun  *    P16V Chip: CA0151-DBS
68*4882a593Smuzhiyun  *    Audigy 2 Chip: CA0102-IAT
69*4882a593Smuzhiyun  *    AC97 Codec: STAC 9721
70*4882a593Smuzhiyun  *    ADC: Philips 1361T (Stereo 24bit)
71*4882a593Smuzhiyun  *    DAC: CS4382-K (8-channel, 24bit, 192Khz)
72*4882a593Smuzhiyun  *
73*4882a593Smuzhiyun  *  This code was initially based on code from ALSA's emu10k1x.c which is:
74*4882a593Smuzhiyun  *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
75*4882a593Smuzhiyun  */
76*4882a593Smuzhiyun #include <linux/delay.h>
77*4882a593Smuzhiyun #include <linux/init.h>
78*4882a593Smuzhiyun #include <linux/interrupt.h>
79*4882a593Smuzhiyun #include <linux/pci.h>
80*4882a593Smuzhiyun #include <linux/slab.h>
81*4882a593Smuzhiyun #include <linux/vmalloc.h>
82*4882a593Smuzhiyun #include <linux/moduleparam.h>
83*4882a593Smuzhiyun #include <sound/core.h>
84*4882a593Smuzhiyun #include <sound/initval.h>
85*4882a593Smuzhiyun #include <sound/pcm.h>
86*4882a593Smuzhiyun #include <sound/ac97_codec.h>
87*4882a593Smuzhiyun #include <sound/info.h>
88*4882a593Smuzhiyun #include <sound/tlv.h>
89*4882a593Smuzhiyun #include <sound/emu10k1.h>
90*4882a593Smuzhiyun #include "p16v.h"
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun #define SET_CHANNEL 0  /* Testing channel outputs 0=Front, 1=Center/LFE, 2=Unknown, 3=Rear */
93*4882a593Smuzhiyun #define PCM_FRONT_CHANNEL 0
94*4882a593Smuzhiyun #define PCM_REAR_CHANNEL 1
95*4882a593Smuzhiyun #define PCM_CENTER_LFE_CHANNEL 2
96*4882a593Smuzhiyun #define PCM_SIDE_CHANNEL 3
97*4882a593Smuzhiyun #define CONTROL_FRONT_CHANNEL 0
98*4882a593Smuzhiyun #define CONTROL_REAR_CHANNEL 3
99*4882a593Smuzhiyun #define CONTROL_CENTER_LFE_CHANNEL 1
100*4882a593Smuzhiyun #define CONTROL_SIDE_CHANNEL 2
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun /* Card IDs:
103*4882a593Smuzhiyun  * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:2002 -> Audigy2 ZS 7.1 Model:SB0350
104*4882a593Smuzhiyun  * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:1007 -> Audigy2 6.1    Model:SB0240
105*4882a593Smuzhiyun  * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:1002 -> Audigy2 Platinum  Model:SB msb0240230009266
106*4882a593Smuzhiyun  * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:2007 -> Audigy4 Pro Model:SB0380 M1SB0380472001901E
107*4882a593Smuzhiyun  *
108*4882a593Smuzhiyun  */
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun  /* hardware definition */
111*4882a593Smuzhiyun static const struct snd_pcm_hardware snd_p16v_playback_hw = {
112*4882a593Smuzhiyun 	.info =			SNDRV_PCM_INFO_MMAP |
113*4882a593Smuzhiyun 				SNDRV_PCM_INFO_INTERLEAVED |
114*4882a593Smuzhiyun 				SNDRV_PCM_INFO_BLOCK_TRANSFER |
115*4882a593Smuzhiyun 				SNDRV_PCM_INFO_RESUME |
116*4882a593Smuzhiyun 				SNDRV_PCM_INFO_MMAP_VALID |
117*4882a593Smuzhiyun 				SNDRV_PCM_INFO_SYNC_START,
118*4882a593Smuzhiyun 	.formats =		SNDRV_PCM_FMTBIT_S32_LE, /* Only supports 24-bit samples padded to 32 bits. */
119*4882a593Smuzhiyun 	.rates =		SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100,
120*4882a593Smuzhiyun 	.rate_min =		44100,
121*4882a593Smuzhiyun 	.rate_max =		192000,
122*4882a593Smuzhiyun 	.channels_min =		8,
123*4882a593Smuzhiyun 	.channels_max =		8,
124*4882a593Smuzhiyun 	.buffer_bytes_max =	((65536 - 64) * 8),
125*4882a593Smuzhiyun 	.period_bytes_min =	64,
126*4882a593Smuzhiyun 	.period_bytes_max =	(65536 - 64),
127*4882a593Smuzhiyun 	.periods_min =		2,
128*4882a593Smuzhiyun 	.periods_max =		8,
129*4882a593Smuzhiyun 	.fifo_size =		0,
130*4882a593Smuzhiyun };
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun static const struct snd_pcm_hardware snd_p16v_capture_hw = {
133*4882a593Smuzhiyun 	.info =			(SNDRV_PCM_INFO_MMAP |
134*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_INTERLEAVED |
135*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
136*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_RESUME |
137*4882a593Smuzhiyun 				 SNDRV_PCM_INFO_MMAP_VALID),
138*4882a593Smuzhiyun 	.formats =		SNDRV_PCM_FMTBIT_S32_LE,
139*4882a593Smuzhiyun 	.rates =		SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100,
140*4882a593Smuzhiyun 	.rate_min =		44100,
141*4882a593Smuzhiyun 	.rate_max =		192000,
142*4882a593Smuzhiyun 	.channels_min =		2,
143*4882a593Smuzhiyun 	.channels_max =		2,
144*4882a593Smuzhiyun 	.buffer_bytes_max =	(65536 - 64),
145*4882a593Smuzhiyun 	.period_bytes_min =	64,
146*4882a593Smuzhiyun 	.period_bytes_max =	(65536 - 128) >> 1,  /* size has to be N*64 bytes */
147*4882a593Smuzhiyun 	.periods_min =		2,
148*4882a593Smuzhiyun 	.periods_max =		2,
149*4882a593Smuzhiyun 	.fifo_size =		0,
150*4882a593Smuzhiyun };
151*4882a593Smuzhiyun 
snd_p16v_pcm_free_substream(struct snd_pcm_runtime * runtime)152*4882a593Smuzhiyun static void snd_p16v_pcm_free_substream(struct snd_pcm_runtime *runtime)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun 	struct snd_emu10k1_pcm *epcm = runtime->private_data;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	kfree(epcm);
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun /* open_playback callback */
snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream * substream,int channel_id)160*4882a593Smuzhiyun static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substream, int channel_id)
161*4882a593Smuzhiyun {
162*4882a593Smuzhiyun 	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
163*4882a593Smuzhiyun         struct snd_emu10k1_voice *channel = &(emu->p16v_voices[channel_id]);
164*4882a593Smuzhiyun 	struct snd_emu10k1_pcm *epcm;
165*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
166*4882a593Smuzhiyun 	int err;
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
169*4882a593Smuzhiyun 	/* dev_dbg(emu->card->dev, "epcm kcalloc: %p\n", epcm); */
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	if (epcm == NULL)
172*4882a593Smuzhiyun 		return -ENOMEM;
173*4882a593Smuzhiyun 	epcm->emu = emu;
174*4882a593Smuzhiyun 	epcm->substream = substream;
175*4882a593Smuzhiyun 	/*
176*4882a593Smuzhiyun 	dev_dbg(emu->card->dev, "epcm device=%d, channel_id=%d\n",
177*4882a593Smuzhiyun 		   substream->pcm->device, channel_id);
178*4882a593Smuzhiyun 	*/
179*4882a593Smuzhiyun 	runtime->private_data = epcm;
180*4882a593Smuzhiyun 	runtime->private_free = snd_p16v_pcm_free_substream;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	runtime->hw = snd_p16v_playback_hw;
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun         channel->emu = emu;
185*4882a593Smuzhiyun         channel->number = channel_id;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun         channel->use=1;
188*4882a593Smuzhiyun #if 0 /* debug */
189*4882a593Smuzhiyun 	dev_dbg(emu->card->dev,
190*4882a593Smuzhiyun 		   "p16v: open channel_id=%d, channel=%p, use=0x%x\n",
191*4882a593Smuzhiyun 		   channel_id, channel, channel->use);
192*4882a593Smuzhiyun 	dev_dbg(emu->card->dev, "open:channel_id=%d, chip=%p, channel=%p\n",
193*4882a593Smuzhiyun 	       channel_id, chip, channel);
194*4882a593Smuzhiyun #endif /* debug */
195*4882a593Smuzhiyun 	/* channel->interrupt = snd_p16v_pcm_channel_interrupt; */
196*4882a593Smuzhiyun 	channel->epcm = epcm;
197*4882a593Smuzhiyun 	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
198*4882a593Smuzhiyun                 return err;
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	runtime->sync.id32[0] = substream->pcm->card->number;
201*4882a593Smuzhiyun 	runtime->sync.id32[1] = 'P';
202*4882a593Smuzhiyun 	runtime->sync.id32[2] = 16;
203*4882a593Smuzhiyun 	runtime->sync.id32[3] = 'V';
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	return 0;
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun /* open_capture callback */
snd_p16v_pcm_open_capture_channel(struct snd_pcm_substream * substream,int channel_id)208*4882a593Smuzhiyun static int snd_p16v_pcm_open_capture_channel(struct snd_pcm_substream *substream, int channel_id)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun 	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
211*4882a593Smuzhiyun 	struct snd_emu10k1_voice *channel = &(emu->p16v_capture_voice);
212*4882a593Smuzhiyun 	struct snd_emu10k1_pcm *epcm;
213*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
214*4882a593Smuzhiyun 	int err;
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
217*4882a593Smuzhiyun 	/* dev_dbg(emu->card->dev, "epcm kcalloc: %p\n", epcm); */
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	if (epcm == NULL)
220*4882a593Smuzhiyun 		return -ENOMEM;
221*4882a593Smuzhiyun 	epcm->emu = emu;
222*4882a593Smuzhiyun 	epcm->substream = substream;
223*4882a593Smuzhiyun 	/*
224*4882a593Smuzhiyun 	dev_dbg(emu->card->dev, "epcm device=%d, channel_id=%d\n",
225*4882a593Smuzhiyun 		   substream->pcm->device, channel_id);
226*4882a593Smuzhiyun 	*/
227*4882a593Smuzhiyun 	runtime->private_data = epcm;
228*4882a593Smuzhiyun 	runtime->private_free = snd_p16v_pcm_free_substream;
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	runtime->hw = snd_p16v_capture_hw;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	channel->emu = emu;
233*4882a593Smuzhiyun 	channel->number = channel_id;
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	channel->use=1;
236*4882a593Smuzhiyun #if 0 /* debug */
237*4882a593Smuzhiyun 	dev_dbg(emu->card->dev,
238*4882a593Smuzhiyun 		   "p16v: open channel_id=%d, channel=%p, use=0x%x\n",
239*4882a593Smuzhiyun 		   channel_id, channel, channel->use);
240*4882a593Smuzhiyun 	dev_dbg(emu->card->dev, "open:channel_id=%d, chip=%p, channel=%p\n",
241*4882a593Smuzhiyun 	       channel_id, chip, channel);
242*4882a593Smuzhiyun #endif /* debug */
243*4882a593Smuzhiyun 	/* channel->interrupt = snd_p16v_pcm_channel_interrupt; */
244*4882a593Smuzhiyun 	channel->epcm = epcm;
245*4882a593Smuzhiyun 	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
246*4882a593Smuzhiyun 		return err;
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	return 0;
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun /* close callback */
snd_p16v_pcm_close_playback(struct snd_pcm_substream * substream)253*4882a593Smuzhiyun static int snd_p16v_pcm_close_playback(struct snd_pcm_substream *substream)
254*4882a593Smuzhiyun {
255*4882a593Smuzhiyun 	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
256*4882a593Smuzhiyun 	//struct snd_pcm_runtime *runtime = substream->runtime;
257*4882a593Smuzhiyun 	//struct snd_emu10k1_pcm *epcm = runtime->private_data;
258*4882a593Smuzhiyun 	emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use = 0;
259*4882a593Smuzhiyun 	/* FIXME: maybe zero others */
260*4882a593Smuzhiyun 	return 0;
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun /* close callback */
snd_p16v_pcm_close_capture(struct snd_pcm_substream * substream)264*4882a593Smuzhiyun static int snd_p16v_pcm_close_capture(struct snd_pcm_substream *substream)
265*4882a593Smuzhiyun {
266*4882a593Smuzhiyun 	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
267*4882a593Smuzhiyun 	//struct snd_pcm_runtime *runtime = substream->runtime;
268*4882a593Smuzhiyun 	//struct snd_emu10k1_pcm *epcm = runtime->private_data;
269*4882a593Smuzhiyun 	emu->p16v_capture_voice.use = 0;
270*4882a593Smuzhiyun 	/* FIXME: maybe zero others */
271*4882a593Smuzhiyun 	return 0;
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun 
snd_p16v_pcm_open_playback_front(struct snd_pcm_substream * substream)274*4882a593Smuzhiyun static int snd_p16v_pcm_open_playback_front(struct snd_pcm_substream *substream)
275*4882a593Smuzhiyun {
276*4882a593Smuzhiyun 	return snd_p16v_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL);
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun 
snd_p16v_pcm_open_capture(struct snd_pcm_substream * substream)279*4882a593Smuzhiyun static int snd_p16v_pcm_open_capture(struct snd_pcm_substream *substream)
280*4882a593Smuzhiyun {
281*4882a593Smuzhiyun 	// Only using channel 0 for now, but the card has 2 channels.
282*4882a593Smuzhiyun 	return snd_p16v_pcm_open_capture_channel(substream, 0);
283*4882a593Smuzhiyun }
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun /* prepare playback callback */
snd_p16v_pcm_prepare_playback(struct snd_pcm_substream * substream)286*4882a593Smuzhiyun static int snd_p16v_pcm_prepare_playback(struct snd_pcm_substream *substream)
287*4882a593Smuzhiyun {
288*4882a593Smuzhiyun 	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
289*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
290*4882a593Smuzhiyun 	int channel = substream->pcm->device - emu->p16v_device_offset;
291*4882a593Smuzhiyun 	u32 *table_base = (u32 *)(emu->p16v_buffer.area+(8*16*channel));
292*4882a593Smuzhiyun 	u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
293*4882a593Smuzhiyun 	int i;
294*4882a593Smuzhiyun 	u32 tmp;
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun #if 0 /* debug */
297*4882a593Smuzhiyun 	dev_dbg(emu->card->dev,
298*4882a593Smuzhiyun 		"prepare:channel_number=%d, rate=%d, "
299*4882a593Smuzhiyun 		   "format=0x%x, channels=%d, buffer_size=%ld, "
300*4882a593Smuzhiyun 		   "period_size=%ld, periods=%u, frames_to_bytes=%d\n",
301*4882a593Smuzhiyun 		   channel, runtime->rate, runtime->format, runtime->channels,
302*4882a593Smuzhiyun 		   runtime->buffer_size, runtime->period_size,
303*4882a593Smuzhiyun 		   runtime->periods, frames_to_bytes(runtime, 1));
304*4882a593Smuzhiyun 	dev_dbg(emu->card->dev,
305*4882a593Smuzhiyun 		"dma_addr=%x, dma_area=%p, table_base=%p\n",
306*4882a593Smuzhiyun 		   runtime->dma_addr, runtime->dma_area, table_base);
307*4882a593Smuzhiyun 	dev_dbg(emu->card->dev,
308*4882a593Smuzhiyun 		"dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
309*4882a593Smuzhiyun 		   emu->p16v_buffer.addr, emu->p16v_buffer.area,
310*4882a593Smuzhiyun 		   emu->p16v_buffer.bytes);
311*4882a593Smuzhiyun #endif /* debug */
312*4882a593Smuzhiyun 	tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
313*4882a593Smuzhiyun         switch (runtime->rate) {
314*4882a593Smuzhiyun 	case 44100:
315*4882a593Smuzhiyun 	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x8080);
316*4882a593Smuzhiyun 	  break;
317*4882a593Smuzhiyun 	case 96000:
318*4882a593Smuzhiyun 	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x4040);
319*4882a593Smuzhiyun 	  break;
320*4882a593Smuzhiyun 	case 192000:
321*4882a593Smuzhiyun 	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x2020);
322*4882a593Smuzhiyun 	  break;
323*4882a593Smuzhiyun 	case 48000:
324*4882a593Smuzhiyun 	default:
325*4882a593Smuzhiyun 	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x0000);
326*4882a593Smuzhiyun 	  break;
327*4882a593Smuzhiyun 	}
328*4882a593Smuzhiyun 	/* FIXME: Check emu->buffer.size before actually writing to it. */
329*4882a593Smuzhiyun 	for(i = 0; i < runtime->periods; i++) {
330*4882a593Smuzhiyun 		table_base[i*2]=runtime->dma_addr+(i*period_size_bytes);
331*4882a593Smuzhiyun 		table_base[(i*2)+1]=period_size_bytes<<16;
332*4882a593Smuzhiyun 	}
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_ADDR, channel, emu->p16v_buffer.addr+(8*16*channel));
335*4882a593Smuzhiyun 	snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
336*4882a593Smuzhiyun 	snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_PTR, channel, 0);
337*4882a593Smuzhiyun 	snd_emu10k1_ptr20_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
338*4882a593Smuzhiyun 	//snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
339*4882a593Smuzhiyun 	snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, 0); // buffer size in bytes
340*4882a593Smuzhiyun 	snd_emu10k1_ptr20_write(emu, PLAYBACK_POINTER, channel, 0);
341*4882a593Smuzhiyun 	snd_emu10k1_ptr20_write(emu, 0x07, channel, 0x0);
342*4882a593Smuzhiyun 	snd_emu10k1_ptr20_write(emu, 0x08, channel, 0);
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	return 0;
345*4882a593Smuzhiyun }
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun /* prepare capture callback */
snd_p16v_pcm_prepare_capture(struct snd_pcm_substream * substream)348*4882a593Smuzhiyun static int snd_p16v_pcm_prepare_capture(struct snd_pcm_substream *substream)
349*4882a593Smuzhiyun {
350*4882a593Smuzhiyun 	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
351*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
352*4882a593Smuzhiyun 	int channel = substream->pcm->device - emu->p16v_device_offset;
353*4882a593Smuzhiyun 	u32 tmp;
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	/*
356*4882a593Smuzhiyun 	dev_dbg(emu->card->dev, "prepare capture:channel_number=%d, rate=%d, "
357*4882a593Smuzhiyun 	       "format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, "
358*4882a593Smuzhiyun 	       "frames_to_bytes=%d\n",
359*4882a593Smuzhiyun 	       channel, runtime->rate, runtime->format, runtime->channels,
360*4882a593Smuzhiyun 	       runtime->buffer_size, runtime->period_size,
361*4882a593Smuzhiyun 	       frames_to_bytes(runtime, 1));
362*4882a593Smuzhiyun 	*/
363*4882a593Smuzhiyun 	tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
364*4882a593Smuzhiyun         switch (runtime->rate) {
365*4882a593Smuzhiyun 	case 44100:
366*4882a593Smuzhiyun 	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0800);
367*4882a593Smuzhiyun 	  break;
368*4882a593Smuzhiyun 	case 96000:
369*4882a593Smuzhiyun 	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0400);
370*4882a593Smuzhiyun 	  break;
371*4882a593Smuzhiyun 	case 192000:
372*4882a593Smuzhiyun 	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0200);
373*4882a593Smuzhiyun 	  break;
374*4882a593Smuzhiyun 	case 48000:
375*4882a593Smuzhiyun 	default:
376*4882a593Smuzhiyun 	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0000);
377*4882a593Smuzhiyun 	  break;
378*4882a593Smuzhiyun 	}
379*4882a593Smuzhiyun 	/* FIXME: Check emu->buffer.size before actually writing to it. */
380*4882a593Smuzhiyun 	snd_emu10k1_ptr20_write(emu, 0x13, channel, 0);
381*4882a593Smuzhiyun 	snd_emu10k1_ptr20_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
382*4882a593Smuzhiyun 	snd_emu10k1_ptr20_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size) << 16); // buffer size in bytes
383*4882a593Smuzhiyun 	snd_emu10k1_ptr20_write(emu, CAPTURE_POINTER, channel, 0);
384*4882a593Smuzhiyun 	//snd_emu10k1_ptr20_write(emu, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC or Line in */
385*4882a593Smuzhiyun 	//snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<<channel));
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun 	return 0;
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun 
snd_p16v_intr_enable(struct snd_emu10k1 * emu,unsigned int intrenb)390*4882a593Smuzhiyun static void snd_p16v_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
391*4882a593Smuzhiyun {
392*4882a593Smuzhiyun 	unsigned long flags;
393*4882a593Smuzhiyun 	unsigned int enable;
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	spin_lock_irqsave(&emu->emu_lock, flags);
396*4882a593Smuzhiyun 	enable = inl(emu->port + INTE2) | intrenb;
397*4882a593Smuzhiyun 	outl(enable, emu->port + INTE2);
398*4882a593Smuzhiyun 	spin_unlock_irqrestore(&emu->emu_lock, flags);
399*4882a593Smuzhiyun }
400*4882a593Smuzhiyun 
snd_p16v_intr_disable(struct snd_emu10k1 * emu,unsigned int intrenb)401*4882a593Smuzhiyun static void snd_p16v_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
402*4882a593Smuzhiyun {
403*4882a593Smuzhiyun 	unsigned long flags;
404*4882a593Smuzhiyun 	unsigned int disable;
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 	spin_lock_irqsave(&emu->emu_lock, flags);
407*4882a593Smuzhiyun 	disable = inl(emu->port + INTE2) & (~intrenb);
408*4882a593Smuzhiyun 	outl(disable, emu->port + INTE2);
409*4882a593Smuzhiyun 	spin_unlock_irqrestore(&emu->emu_lock, flags);
410*4882a593Smuzhiyun }
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun /* trigger_playback callback */
snd_p16v_pcm_trigger_playback(struct snd_pcm_substream * substream,int cmd)413*4882a593Smuzhiyun static int snd_p16v_pcm_trigger_playback(struct snd_pcm_substream *substream,
414*4882a593Smuzhiyun 				    int cmd)
415*4882a593Smuzhiyun {
416*4882a593Smuzhiyun 	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
417*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime;
418*4882a593Smuzhiyun 	struct snd_emu10k1_pcm *epcm;
419*4882a593Smuzhiyun 	int channel;
420*4882a593Smuzhiyun 	int result = 0;
421*4882a593Smuzhiyun         struct snd_pcm_substream *s;
422*4882a593Smuzhiyun 	u32 basic = 0;
423*4882a593Smuzhiyun 	u32 inte = 0;
424*4882a593Smuzhiyun 	int running = 0;
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun 	switch (cmd) {
427*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_START:
428*4882a593Smuzhiyun 		running=1;
429*4882a593Smuzhiyun 		break;
430*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_STOP:
431*4882a593Smuzhiyun 	default:
432*4882a593Smuzhiyun 		running = 0;
433*4882a593Smuzhiyun 		break;
434*4882a593Smuzhiyun 	}
435*4882a593Smuzhiyun         snd_pcm_group_for_each_entry(s, substream) {
436*4882a593Smuzhiyun 		if (snd_pcm_substream_chip(s) != emu ||
437*4882a593Smuzhiyun 		    s->stream != SNDRV_PCM_STREAM_PLAYBACK)
438*4882a593Smuzhiyun 			continue;
439*4882a593Smuzhiyun 		runtime = s->runtime;
440*4882a593Smuzhiyun 		epcm = runtime->private_data;
441*4882a593Smuzhiyun 		channel = substream->pcm->device-emu->p16v_device_offset;
442*4882a593Smuzhiyun 		/* dev_dbg(emu->card->dev, "p16v channel=%d\n", channel); */
443*4882a593Smuzhiyun 		epcm->running = running;
444*4882a593Smuzhiyun 		basic |= (0x1<<channel);
445*4882a593Smuzhiyun 		inte |= (INTE2_PLAYBACK_CH_0_LOOP<<channel);
446*4882a593Smuzhiyun                 snd_pcm_trigger_done(s, substream);
447*4882a593Smuzhiyun         }
448*4882a593Smuzhiyun 	/* dev_dbg(emu->card->dev, "basic=0x%x, inte=0x%x\n", basic, inte); */
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 	switch (cmd) {
451*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_START:
452*4882a593Smuzhiyun 		snd_p16v_intr_enable(emu, inte);
453*4882a593Smuzhiyun 		snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)| (basic));
454*4882a593Smuzhiyun 		break;
455*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_STOP:
456*4882a593Smuzhiyun 		snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(basic));
457*4882a593Smuzhiyun 		snd_p16v_intr_disable(emu, inte);
458*4882a593Smuzhiyun 		break;
459*4882a593Smuzhiyun 	default:
460*4882a593Smuzhiyun 		result = -EINVAL;
461*4882a593Smuzhiyun 		break;
462*4882a593Smuzhiyun 	}
463*4882a593Smuzhiyun 	return result;
464*4882a593Smuzhiyun }
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun /* trigger_capture callback */
snd_p16v_pcm_trigger_capture(struct snd_pcm_substream * substream,int cmd)467*4882a593Smuzhiyun static int snd_p16v_pcm_trigger_capture(struct snd_pcm_substream *substream,
468*4882a593Smuzhiyun                                    int cmd)
469*4882a593Smuzhiyun {
470*4882a593Smuzhiyun 	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
471*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
472*4882a593Smuzhiyun 	struct snd_emu10k1_pcm *epcm = runtime->private_data;
473*4882a593Smuzhiyun 	int channel = 0;
474*4882a593Smuzhiyun 	int result = 0;
475*4882a593Smuzhiyun 	u32 inte = INTE2_CAPTURE_CH_0_LOOP | INTE2_CAPTURE_CH_0_HALF_LOOP;
476*4882a593Smuzhiyun 
477*4882a593Smuzhiyun 	switch (cmd) {
478*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_START:
479*4882a593Smuzhiyun 		snd_p16v_intr_enable(emu, inte);
480*4882a593Smuzhiyun 		snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)|(0x100<<channel));
481*4882a593Smuzhiyun 		epcm->running = 1;
482*4882a593Smuzhiyun 		break;
483*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_STOP:
484*4882a593Smuzhiyun 		snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<<channel));
485*4882a593Smuzhiyun 		snd_p16v_intr_disable(emu, inte);
486*4882a593Smuzhiyun 		//snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel));
487*4882a593Smuzhiyun 		epcm->running = 0;
488*4882a593Smuzhiyun 		break;
489*4882a593Smuzhiyun 	default:
490*4882a593Smuzhiyun 		result = -EINVAL;
491*4882a593Smuzhiyun 		break;
492*4882a593Smuzhiyun 	}
493*4882a593Smuzhiyun 	return result;
494*4882a593Smuzhiyun }
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun /* pointer_playback callback */
497*4882a593Smuzhiyun static snd_pcm_uframes_t
snd_p16v_pcm_pointer_playback(struct snd_pcm_substream * substream)498*4882a593Smuzhiyun snd_p16v_pcm_pointer_playback(struct snd_pcm_substream *substream)
499*4882a593Smuzhiyun {
500*4882a593Smuzhiyun 	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
501*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
502*4882a593Smuzhiyun 	struct snd_emu10k1_pcm *epcm = runtime->private_data;
503*4882a593Smuzhiyun 	snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0;
504*4882a593Smuzhiyun 	int channel = substream->pcm->device - emu->p16v_device_offset;
505*4882a593Smuzhiyun 	if (!epcm->running)
506*4882a593Smuzhiyun 		return 0;
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 	ptr3 = snd_emu10k1_ptr20_read(emu, PLAYBACK_LIST_PTR, channel);
509*4882a593Smuzhiyun 	ptr1 = snd_emu10k1_ptr20_read(emu, PLAYBACK_POINTER, channel);
510*4882a593Smuzhiyun 	ptr4 = snd_emu10k1_ptr20_read(emu, PLAYBACK_LIST_PTR, channel);
511*4882a593Smuzhiyun 	if (ptr3 != ptr4) ptr1 = snd_emu10k1_ptr20_read(emu, PLAYBACK_POINTER, channel);
512*4882a593Smuzhiyun 	ptr2 = bytes_to_frames(runtime, ptr1);
513*4882a593Smuzhiyun 	ptr2+= (ptr4 >> 3) * runtime->period_size;
514*4882a593Smuzhiyun 	ptr=ptr2;
515*4882a593Smuzhiyun         if (ptr >= runtime->buffer_size)
516*4882a593Smuzhiyun 		ptr -= runtime->buffer_size;
517*4882a593Smuzhiyun 
518*4882a593Smuzhiyun 	return ptr;
519*4882a593Smuzhiyun }
520*4882a593Smuzhiyun 
521*4882a593Smuzhiyun /* pointer_capture callback */
522*4882a593Smuzhiyun static snd_pcm_uframes_t
snd_p16v_pcm_pointer_capture(struct snd_pcm_substream * substream)523*4882a593Smuzhiyun snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream)
524*4882a593Smuzhiyun {
525*4882a593Smuzhiyun 	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
526*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
527*4882a593Smuzhiyun 	struct snd_emu10k1_pcm *epcm = runtime->private_data;
528*4882a593Smuzhiyun 	snd_pcm_uframes_t ptr, ptr1, ptr2 = 0;
529*4882a593Smuzhiyun 	int channel = 0;
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun 	if (!epcm->running)
532*4882a593Smuzhiyun 		return 0;
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	ptr1 = snd_emu10k1_ptr20_read(emu, CAPTURE_POINTER, channel);
535*4882a593Smuzhiyun 	ptr2 = bytes_to_frames(runtime, ptr1);
536*4882a593Smuzhiyun 	ptr=ptr2;
537*4882a593Smuzhiyun 	if (ptr >= runtime->buffer_size) {
538*4882a593Smuzhiyun 		ptr -= runtime->buffer_size;
539*4882a593Smuzhiyun 		dev_warn(emu->card->dev, "buffer capture limited!\n");
540*4882a593Smuzhiyun 	}
541*4882a593Smuzhiyun 	/*
542*4882a593Smuzhiyun 	dev_dbg(emu->card->dev, "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
543*4882a593Smuzhiyun 	       "buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n",
544*4882a593Smuzhiyun 	       ptr1, ptr2, ptr, (int)runtime->buffer_size,
545*4882a593Smuzhiyun 	       (int)runtime->period_size, (int)runtime->frame_bits,
546*4882a593Smuzhiyun 	       (int)runtime->rate);
547*4882a593Smuzhiyun 	*/
548*4882a593Smuzhiyun 	return ptr;
549*4882a593Smuzhiyun }
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun /* operators */
552*4882a593Smuzhiyun static const struct snd_pcm_ops snd_p16v_playback_front_ops = {
553*4882a593Smuzhiyun 	.open =        snd_p16v_pcm_open_playback_front,
554*4882a593Smuzhiyun 	.close =       snd_p16v_pcm_close_playback,
555*4882a593Smuzhiyun 	.prepare =     snd_p16v_pcm_prepare_playback,
556*4882a593Smuzhiyun 	.trigger =     snd_p16v_pcm_trigger_playback,
557*4882a593Smuzhiyun 	.pointer =     snd_p16v_pcm_pointer_playback,
558*4882a593Smuzhiyun };
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun static const struct snd_pcm_ops snd_p16v_capture_ops = {
561*4882a593Smuzhiyun 	.open =        snd_p16v_pcm_open_capture,
562*4882a593Smuzhiyun 	.close =       snd_p16v_pcm_close_capture,
563*4882a593Smuzhiyun 	.prepare =     snd_p16v_pcm_prepare_capture,
564*4882a593Smuzhiyun 	.trigger =     snd_p16v_pcm_trigger_capture,
565*4882a593Smuzhiyun 	.pointer =     snd_p16v_pcm_pointer_capture,
566*4882a593Smuzhiyun };
567*4882a593Smuzhiyun 
568*4882a593Smuzhiyun 
snd_p16v_free(struct snd_emu10k1 * chip)569*4882a593Smuzhiyun int snd_p16v_free(struct snd_emu10k1 *chip)
570*4882a593Smuzhiyun {
571*4882a593Smuzhiyun 	// release the data
572*4882a593Smuzhiyun 	if (chip->p16v_buffer.area) {
573*4882a593Smuzhiyun 		snd_dma_free_pages(&chip->p16v_buffer);
574*4882a593Smuzhiyun 		/*
575*4882a593Smuzhiyun 		dev_dbg(chip->card->dev, "period lables free: %p\n",
576*4882a593Smuzhiyun 			   &chip->p16v_buffer);
577*4882a593Smuzhiyun 		*/
578*4882a593Smuzhiyun 	}
579*4882a593Smuzhiyun 	return 0;
580*4882a593Smuzhiyun }
581*4882a593Smuzhiyun 
snd_p16v_pcm(struct snd_emu10k1 * emu,int device)582*4882a593Smuzhiyun int snd_p16v_pcm(struct snd_emu10k1 *emu, int device)
583*4882a593Smuzhiyun {
584*4882a593Smuzhiyun 	struct snd_pcm *pcm;
585*4882a593Smuzhiyun 	struct snd_pcm_substream *substream;
586*4882a593Smuzhiyun 	int err;
587*4882a593Smuzhiyun         int capture=1;
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	/* dev_dbg(emu->card->dev, "snd_p16v_pcm called. device=%d\n", device); */
590*4882a593Smuzhiyun 	emu->p16v_device_offset = device;
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun 	if ((err = snd_pcm_new(emu->card, "p16v", device, 1, capture, &pcm)) < 0)
593*4882a593Smuzhiyun 		return err;
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun 	pcm->private_data = emu;
596*4882a593Smuzhiyun 	// Single playback 8 channel device.
597*4882a593Smuzhiyun 	// Single capture 2 channel device.
598*4882a593Smuzhiyun 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_p16v_playback_front_ops);
599*4882a593Smuzhiyun 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_p16v_capture_ops);
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	pcm->info_flags = 0;
602*4882a593Smuzhiyun 	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
603*4882a593Smuzhiyun 	strcpy(pcm->name, "p16v");
604*4882a593Smuzhiyun 	emu->pcm_p16v = pcm;
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
607*4882a593Smuzhiyun 	    substream;
608*4882a593Smuzhiyun 	    substream = substream->next) {
609*4882a593Smuzhiyun 		snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
610*4882a593Smuzhiyun 					   &emu->pci->dev,
611*4882a593Smuzhiyun 					   (65536 - 64) * 8,
612*4882a593Smuzhiyun 					   (65536 - 64) * 8);
613*4882a593Smuzhiyun 		/*
614*4882a593Smuzhiyun 		dev_dbg(emu->card->dev,
615*4882a593Smuzhiyun 			   "preallocate playback substream: err=%d\n", err);
616*4882a593Smuzhiyun 		*/
617*4882a593Smuzhiyun 	}
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 	for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
620*4882a593Smuzhiyun 	      substream;
621*4882a593Smuzhiyun 	      substream = substream->next) {
622*4882a593Smuzhiyun 		snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
623*4882a593Smuzhiyun 					   &emu->pci->dev,
624*4882a593Smuzhiyun 					   65536 - 64, 65536 - 64);
625*4882a593Smuzhiyun 		/*
626*4882a593Smuzhiyun 		dev_dbg(emu->card->dev,
627*4882a593Smuzhiyun 			   "preallocate capture substream: err=%d\n", err);
628*4882a593Smuzhiyun 		*/
629*4882a593Smuzhiyun 	}
630*4882a593Smuzhiyun 
631*4882a593Smuzhiyun 	return 0;
632*4882a593Smuzhiyun }
633*4882a593Smuzhiyun 
snd_p16v_volume_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)634*4882a593Smuzhiyun static int snd_p16v_volume_info(struct snd_kcontrol *kcontrol,
635*4882a593Smuzhiyun 				struct snd_ctl_elem_info *uinfo)
636*4882a593Smuzhiyun {
637*4882a593Smuzhiyun         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
638*4882a593Smuzhiyun         uinfo->count = 2;
639*4882a593Smuzhiyun         uinfo->value.integer.min = 0;
640*4882a593Smuzhiyun         uinfo->value.integer.max = 255;
641*4882a593Smuzhiyun         return 0;
642*4882a593Smuzhiyun }
643*4882a593Smuzhiyun 
snd_p16v_volume_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)644*4882a593Smuzhiyun static int snd_p16v_volume_get(struct snd_kcontrol *kcontrol,
645*4882a593Smuzhiyun 			       struct snd_ctl_elem_value *ucontrol)
646*4882a593Smuzhiyun {
647*4882a593Smuzhiyun         struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
648*4882a593Smuzhiyun 	int high_low = (kcontrol->private_value >> 8) & 0xff;
649*4882a593Smuzhiyun 	int reg = kcontrol->private_value & 0xff;
650*4882a593Smuzhiyun 	u32 value;
651*4882a593Smuzhiyun 
652*4882a593Smuzhiyun 	value = snd_emu10k1_ptr20_read(emu, reg, high_low);
653*4882a593Smuzhiyun 	if (high_low) {
654*4882a593Smuzhiyun 		ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
655*4882a593Smuzhiyun 		ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
656*4882a593Smuzhiyun 	} else {
657*4882a593Smuzhiyun 		ucontrol->value.integer.value[0] = 0xff - ((value >> 8) & 0xff); /* Left */
658*4882a593Smuzhiyun 		ucontrol->value.integer.value[1] = 0xff - ((value >> 0) & 0xff); /* Right */
659*4882a593Smuzhiyun 	}
660*4882a593Smuzhiyun 	return 0;
661*4882a593Smuzhiyun }
662*4882a593Smuzhiyun 
snd_p16v_volume_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)663*4882a593Smuzhiyun static int snd_p16v_volume_put(struct snd_kcontrol *kcontrol,
664*4882a593Smuzhiyun 			       struct snd_ctl_elem_value *ucontrol)
665*4882a593Smuzhiyun {
666*4882a593Smuzhiyun         struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
667*4882a593Smuzhiyun 	int high_low = (kcontrol->private_value >> 8) & 0xff;
668*4882a593Smuzhiyun 	int reg = kcontrol->private_value & 0xff;
669*4882a593Smuzhiyun         u32 value, oval;
670*4882a593Smuzhiyun 
671*4882a593Smuzhiyun 	oval = value = snd_emu10k1_ptr20_read(emu, reg, 0);
672*4882a593Smuzhiyun 	if (high_low == 1) {
673*4882a593Smuzhiyun 		value &= 0xffff;
674*4882a593Smuzhiyun 		value |= ((0xff - ucontrol->value.integer.value[0]) << 24) |
675*4882a593Smuzhiyun 			((0xff - ucontrol->value.integer.value[1]) << 16);
676*4882a593Smuzhiyun 	} else {
677*4882a593Smuzhiyun 		value &= 0xffff0000;
678*4882a593Smuzhiyun 		value |= ((0xff - ucontrol->value.integer.value[0]) << 8) |
679*4882a593Smuzhiyun 			((0xff - ucontrol->value.integer.value[1]) );
680*4882a593Smuzhiyun 	}
681*4882a593Smuzhiyun 	if (value != oval) {
682*4882a593Smuzhiyun 		snd_emu10k1_ptr20_write(emu, reg, 0, value);
683*4882a593Smuzhiyun 		return 1;
684*4882a593Smuzhiyun 	}
685*4882a593Smuzhiyun 	return 0;
686*4882a593Smuzhiyun }
687*4882a593Smuzhiyun 
snd_p16v_capture_source_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)688*4882a593Smuzhiyun static int snd_p16v_capture_source_info(struct snd_kcontrol *kcontrol,
689*4882a593Smuzhiyun 					struct snd_ctl_elem_info *uinfo)
690*4882a593Smuzhiyun {
691*4882a593Smuzhiyun 	static const char * const texts[8] = {
692*4882a593Smuzhiyun 		"SPDIF", "I2S", "SRC48", "SRCMulti_SPDIF", "SRCMulti_I2S",
693*4882a593Smuzhiyun 		"CDIF", "FX", "AC97"
694*4882a593Smuzhiyun 	};
695*4882a593Smuzhiyun 
696*4882a593Smuzhiyun 	return snd_ctl_enum_info(uinfo, 1, 8, texts);
697*4882a593Smuzhiyun }
698*4882a593Smuzhiyun 
snd_p16v_capture_source_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)699*4882a593Smuzhiyun static int snd_p16v_capture_source_get(struct snd_kcontrol *kcontrol,
700*4882a593Smuzhiyun 					struct snd_ctl_elem_value *ucontrol)
701*4882a593Smuzhiyun {
702*4882a593Smuzhiyun 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
703*4882a593Smuzhiyun 
704*4882a593Smuzhiyun 	ucontrol->value.enumerated.item[0] = emu->p16v_capture_source;
705*4882a593Smuzhiyun 	return 0;
706*4882a593Smuzhiyun }
707*4882a593Smuzhiyun 
snd_p16v_capture_source_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)708*4882a593Smuzhiyun static int snd_p16v_capture_source_put(struct snd_kcontrol *kcontrol,
709*4882a593Smuzhiyun 					struct snd_ctl_elem_value *ucontrol)
710*4882a593Smuzhiyun {
711*4882a593Smuzhiyun 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
712*4882a593Smuzhiyun 	unsigned int val;
713*4882a593Smuzhiyun 	int change = 0;
714*4882a593Smuzhiyun 	u32 mask;
715*4882a593Smuzhiyun 	u32 source;
716*4882a593Smuzhiyun 
717*4882a593Smuzhiyun 	val = ucontrol->value.enumerated.item[0] ;
718*4882a593Smuzhiyun 	if (val > 7)
719*4882a593Smuzhiyun 		return -EINVAL;
720*4882a593Smuzhiyun 	change = (emu->p16v_capture_source != val);
721*4882a593Smuzhiyun 	if (change) {
722*4882a593Smuzhiyun 		emu->p16v_capture_source = val;
723*4882a593Smuzhiyun 		source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
724*4882a593Smuzhiyun 		mask = snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & 0xffff;
725*4882a593Smuzhiyun 		snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, source | mask);
726*4882a593Smuzhiyun 	}
727*4882a593Smuzhiyun         return change;
728*4882a593Smuzhiyun }
729*4882a593Smuzhiyun 
snd_p16v_capture_channel_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)730*4882a593Smuzhiyun static int snd_p16v_capture_channel_info(struct snd_kcontrol *kcontrol,
731*4882a593Smuzhiyun 					 struct snd_ctl_elem_info *uinfo)
732*4882a593Smuzhiyun {
733*4882a593Smuzhiyun 	static const char * const texts[4] = { "0", "1", "2", "3", };
734*4882a593Smuzhiyun 
735*4882a593Smuzhiyun 	return snd_ctl_enum_info(uinfo, 1, 4, texts);
736*4882a593Smuzhiyun }
737*4882a593Smuzhiyun 
snd_p16v_capture_channel_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)738*4882a593Smuzhiyun static int snd_p16v_capture_channel_get(struct snd_kcontrol *kcontrol,
739*4882a593Smuzhiyun 					struct snd_ctl_elem_value *ucontrol)
740*4882a593Smuzhiyun {
741*4882a593Smuzhiyun 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
742*4882a593Smuzhiyun 
743*4882a593Smuzhiyun 	ucontrol->value.enumerated.item[0] = emu->p16v_capture_channel;
744*4882a593Smuzhiyun 	return 0;
745*4882a593Smuzhiyun }
746*4882a593Smuzhiyun 
snd_p16v_capture_channel_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)747*4882a593Smuzhiyun static int snd_p16v_capture_channel_put(struct snd_kcontrol *kcontrol,
748*4882a593Smuzhiyun 					struct snd_ctl_elem_value *ucontrol)
749*4882a593Smuzhiyun {
750*4882a593Smuzhiyun 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
751*4882a593Smuzhiyun 	unsigned int val;
752*4882a593Smuzhiyun 	int change = 0;
753*4882a593Smuzhiyun 	u32 tmp;
754*4882a593Smuzhiyun 
755*4882a593Smuzhiyun 	val = ucontrol->value.enumerated.item[0] ;
756*4882a593Smuzhiyun 	if (val > 3)
757*4882a593Smuzhiyun 		return -EINVAL;
758*4882a593Smuzhiyun 	change = (emu->p16v_capture_channel != val);
759*4882a593Smuzhiyun 	if (change) {
760*4882a593Smuzhiyun 		emu->p16v_capture_channel = val;
761*4882a593Smuzhiyun 		tmp = snd_emu10k1_ptr20_read(emu, CAPTURE_P16V_SOURCE, 0) & 0xfffc;
762*4882a593Smuzhiyun 		snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, tmp | val);
763*4882a593Smuzhiyun 	}
764*4882a593Smuzhiyun         return change;
765*4882a593Smuzhiyun }
766*4882a593Smuzhiyun static const DECLARE_TLV_DB_SCALE(snd_p16v_db_scale1, -5175, 25, 1);
767*4882a593Smuzhiyun 
768*4882a593Smuzhiyun #define P16V_VOL(xname,xreg,xhl) { \
769*4882a593Smuzhiyun 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
770*4882a593Smuzhiyun         .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |             \
771*4882a593Smuzhiyun                   SNDRV_CTL_ELEM_ACCESS_TLV_READ,               \
772*4882a593Smuzhiyun 	.info = snd_p16v_volume_info, \
773*4882a593Smuzhiyun 	.get = snd_p16v_volume_get, \
774*4882a593Smuzhiyun 	.put = snd_p16v_volume_put, \
775*4882a593Smuzhiyun 	.tlv = { .p = snd_p16v_db_scale1 },	\
776*4882a593Smuzhiyun 	.private_value = ((xreg) | ((xhl) << 8)) \
777*4882a593Smuzhiyun }
778*4882a593Smuzhiyun 
779*4882a593Smuzhiyun static const struct snd_kcontrol_new p16v_mixer_controls[] = {
780*4882a593Smuzhiyun 	P16V_VOL("HD Analog Front Playback Volume", PLAYBACK_VOLUME_MIXER9, 0),
781*4882a593Smuzhiyun 	P16V_VOL("HD Analog Rear Playback Volume", PLAYBACK_VOLUME_MIXER10, 1),
782*4882a593Smuzhiyun 	P16V_VOL("HD Analog Center/LFE Playback Volume", PLAYBACK_VOLUME_MIXER9, 1),
783*4882a593Smuzhiyun 	P16V_VOL("HD Analog Side Playback Volume", PLAYBACK_VOLUME_MIXER10, 0),
784*4882a593Smuzhiyun 	P16V_VOL("HD SPDIF Front Playback Volume", PLAYBACK_VOLUME_MIXER7, 0),
785*4882a593Smuzhiyun 	P16V_VOL("HD SPDIF Rear Playback Volume", PLAYBACK_VOLUME_MIXER8, 1),
786*4882a593Smuzhiyun 	P16V_VOL("HD SPDIF Center/LFE Playback Volume", PLAYBACK_VOLUME_MIXER7, 1),
787*4882a593Smuzhiyun 	P16V_VOL("HD SPDIF Side Playback Volume", PLAYBACK_VOLUME_MIXER8, 0),
788*4882a593Smuzhiyun 	{
789*4882a593Smuzhiyun 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
790*4882a593Smuzhiyun 		.name =		"HD source Capture",
791*4882a593Smuzhiyun 		.info =		snd_p16v_capture_source_info,
792*4882a593Smuzhiyun 		.get =		snd_p16v_capture_source_get,
793*4882a593Smuzhiyun 		.put =		snd_p16v_capture_source_put
794*4882a593Smuzhiyun 	},
795*4882a593Smuzhiyun 	{
796*4882a593Smuzhiyun 		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
797*4882a593Smuzhiyun 		.name =		"HD channel Capture",
798*4882a593Smuzhiyun 		.info =		snd_p16v_capture_channel_info,
799*4882a593Smuzhiyun 		.get =		snd_p16v_capture_channel_get,
800*4882a593Smuzhiyun 		.put =		snd_p16v_capture_channel_put
801*4882a593Smuzhiyun 	},
802*4882a593Smuzhiyun };
803*4882a593Smuzhiyun 
804*4882a593Smuzhiyun 
snd_p16v_mixer(struct snd_emu10k1 * emu)805*4882a593Smuzhiyun int snd_p16v_mixer(struct snd_emu10k1 *emu)
806*4882a593Smuzhiyun {
807*4882a593Smuzhiyun 	int i, err;
808*4882a593Smuzhiyun         struct snd_card *card = emu->card;
809*4882a593Smuzhiyun 
810*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(p16v_mixer_controls); i++) {
811*4882a593Smuzhiyun 		if ((err = snd_ctl_add(card, snd_ctl_new1(&p16v_mixer_controls[i],
812*4882a593Smuzhiyun 							  emu))) < 0)
813*4882a593Smuzhiyun 			return err;
814*4882a593Smuzhiyun 	}
815*4882a593Smuzhiyun         return 0;
816*4882a593Smuzhiyun }
817*4882a593Smuzhiyun 
818*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
819*4882a593Smuzhiyun 
820*4882a593Smuzhiyun #define NUM_CHS	1	/* up to 4, but only first channel is used */
821*4882a593Smuzhiyun 
snd_p16v_alloc_pm_buffer(struct snd_emu10k1 * emu)822*4882a593Smuzhiyun int snd_p16v_alloc_pm_buffer(struct snd_emu10k1 *emu)
823*4882a593Smuzhiyun {
824*4882a593Smuzhiyun 	emu->p16v_saved = vmalloc(array_size(NUM_CHS * 4, 0x80));
825*4882a593Smuzhiyun 	if (! emu->p16v_saved)
826*4882a593Smuzhiyun 		return -ENOMEM;
827*4882a593Smuzhiyun 	return 0;
828*4882a593Smuzhiyun }
829*4882a593Smuzhiyun 
snd_p16v_free_pm_buffer(struct snd_emu10k1 * emu)830*4882a593Smuzhiyun void snd_p16v_free_pm_buffer(struct snd_emu10k1 *emu)
831*4882a593Smuzhiyun {
832*4882a593Smuzhiyun 	vfree(emu->p16v_saved);
833*4882a593Smuzhiyun }
834*4882a593Smuzhiyun 
snd_p16v_suspend(struct snd_emu10k1 * emu)835*4882a593Smuzhiyun void snd_p16v_suspend(struct snd_emu10k1 *emu)
836*4882a593Smuzhiyun {
837*4882a593Smuzhiyun 	int i, ch;
838*4882a593Smuzhiyun 	unsigned int *val;
839*4882a593Smuzhiyun 
840*4882a593Smuzhiyun 	val = emu->p16v_saved;
841*4882a593Smuzhiyun 	for (ch = 0; ch < NUM_CHS; ch++)
842*4882a593Smuzhiyun 		for (i = 0; i < 0x80; i++, val++)
843*4882a593Smuzhiyun 			*val = snd_emu10k1_ptr20_read(emu, i, ch);
844*4882a593Smuzhiyun }
845*4882a593Smuzhiyun 
snd_p16v_resume(struct snd_emu10k1 * emu)846*4882a593Smuzhiyun void snd_p16v_resume(struct snd_emu10k1 *emu)
847*4882a593Smuzhiyun {
848*4882a593Smuzhiyun 	int i, ch;
849*4882a593Smuzhiyun 	unsigned int *val;
850*4882a593Smuzhiyun 
851*4882a593Smuzhiyun 	val = emu->p16v_saved;
852*4882a593Smuzhiyun 	for (ch = 0; ch < NUM_CHS; ch++)
853*4882a593Smuzhiyun 		for (i = 0; i < 0x80; i++, val++)
854*4882a593Smuzhiyun 			snd_emu10k1_ptr20_write(emu, i, ch, *val);
855*4882a593Smuzhiyun }
856*4882a593Smuzhiyun #endif
857