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