File: /usr/src/linux/drivers/sound/ics2101.c

1     /*
2      * sound/ics2101.c
3      *
4      * Driver for the ICS2101 mixer of GUS v3.7.
5      *
6      *
7      * Copyright (C) by Hannu Savolainen 1993-1997
8      *
9      * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
10      * Version 2 (June 1991). See the "COPYING" file distributed with this software
11      * for more info.
12      *
13      *
14      * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
15      * Bartlomiej Zolnierkiewicz : added __init to ics2101_mixer_init()
16      */
17     #include <linux/init.h>
18     #include "sound_config.h"
19     
20     #include <linux/ultrasound.h>
21     
22     #include "gus.h"
23     #include "gus_hw.h"
24     
25     #define MIX_DEVS	(SOUND_MASK_MIC|SOUND_MASK_LINE| \
26     			 SOUND_MASK_SYNTH| \
27       			 SOUND_MASK_CD | SOUND_MASK_VOLUME)
28     
29     extern int     *gus_osp;
30     extern int      gus_base;
31     static int      volumes[ICS_MIXDEVS];
32     static int      left_fix[ICS_MIXDEVS] =
33     {1, 1, 1, 2, 1, 2};
34     static int      right_fix[ICS_MIXDEVS] =
35     {2, 2, 2, 1, 2, 1};
36     
37     static int scale_vol(int vol)
38     {
39     	/*
40     	 *  Experimental volume scaling by Risto Kankkunen.
41     	 *  This should give smoother volume response than just
42     	 *  a plain multiplication.
43     	 */
44     	 
45     	int e;
46     
47     	if (vol < 0)
48     		vol = 0;
49     	if (vol > 100)
50     		vol = 100;
51     	vol = (31 * vol + 50) / 100;
52     	e = 0;
53     	if (vol)
54     	{
55     		while (vol < 16)
56     		{
57     			vol <<= 1;
58     			e--;
59     		}
60     		vol -= 16;
61     		e += 7;
62     	}
63     	return ((e << 4) + vol);
64     }
65     
66     static void write_mix(int dev, int chn, int vol)
67     {
68     	int *selector;
69     	unsigned long flags;
70     	int ctrl_addr = dev << 3;
71     	int attn_addr = dev << 3;
72     
73     	vol = scale_vol(vol);
74     
75     	if (chn == CHN_LEFT)
76     	{
77     		selector = left_fix;
78     		ctrl_addr |= 0x00;
79     		attn_addr |= 0x02;
80     	}
81     	else
82     	{
83     		selector = right_fix;
84     		ctrl_addr |= 0x01;
85     		attn_addr |= 0x03;
86     	}
87     
88     	save_flags(flags);
89     	cli();
90     	outb((ctrl_addr), u_MixSelect);
91     	outb((selector[dev]), u_MixData);
92     	outb((attn_addr), u_MixSelect);
93     	outb(((unsigned char) vol), u_MixData);
94     	restore_flags(flags);
95     }
96     
97     static int set_volumes(int dev, int vol)
98     {
99     	int left = vol & 0x00ff;
100     	int right = (vol >> 8) & 0x00ff;
101     
102     	if (left < 0)
103     		left = 0;
104     	if (left > 100)
105     		left = 100;
106     	if (right < 0)
107     		right = 0;
108     	if (right > 100)
109     		right = 100;
110     
111     	write_mix(dev, CHN_LEFT, left);
112     	write_mix(dev, CHN_RIGHT, right);
113     
114     	vol = left + (right << 8);
115     	volumes[dev] = vol;
116     	return vol;
117     }
118     
119     static int ics2101_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
120     {
121     	int val;
122     	
123     	if (((cmd >> 8) & 0xff) == 'M') {
124     		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
125     			
126     			if (get_user(val, (int *)arg))
127     				return -EFAULT;
128     			switch (cmd & 0xff) {
129     			case SOUND_MIXER_RECSRC:
130     				return gus_default_mixer_ioctl(dev, cmd, arg);
131     
132     			case SOUND_MIXER_MIC:
133     				val = set_volumes(DEV_MIC, val);
134     				break;
135     				
136     			case SOUND_MIXER_CD:
137     				val = set_volumes(DEV_CD, val);
138     				break;
139     
140     			case SOUND_MIXER_LINE:
141     				val = set_volumes(DEV_LINE, val);
142     				break;
143     
144     			case SOUND_MIXER_SYNTH:
145     				val = set_volumes(DEV_GF1, val);
146     				break;
147     
148     			case SOUND_MIXER_VOLUME:
149     				val = set_volumes(DEV_VOL, val);
150     				break;
151     
152     			default:
153     				return -EINVAL;
154     			}
155     			return put_user(val, (int *)arg);
156     		} else {
157     			switch (cmd & 0xff) {
158     				/*
159     				 * Return parameters
160     				 */
161     			case SOUND_MIXER_RECSRC:
162     				return gus_default_mixer_ioctl(dev, cmd, arg);
163     
164     			case SOUND_MIXER_DEVMASK:
165     				val = MIX_DEVS; 
166     				break;
167     
168     			case SOUND_MIXER_STEREODEVS:
169     				val = SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC; 
170     				break;
171     
172     			case SOUND_MIXER_RECMASK:
173     				val = SOUND_MASK_MIC | SOUND_MASK_LINE; 
174     				break;
175     				
176     			case SOUND_MIXER_CAPS:
177     				val = 0; 
178     				break;
179     
180     			case SOUND_MIXER_MIC:
181     				val = volumes[DEV_MIC];
182     				break;
183     				
184     			case SOUND_MIXER_LINE:
185     				val = volumes[DEV_LINE];
186     				break;
187     
188     			case SOUND_MIXER_CD:
189     				val = volumes[DEV_CD];
190     				break;
191     
192     			case SOUND_MIXER_VOLUME:
193     				val = volumes[DEV_VOL];
194     				break;
195     
196     			case SOUND_MIXER_SYNTH:
197     				val = volumes[DEV_GF1]; 
198     				break;
199     
200     			default:
201     				return -EINVAL;
202     			}
203     			return put_user(val, (int *)arg);
204     		}
205     	}
206     	return -EINVAL;
207     }
208     
209     static struct mixer_operations ics2101_mixer_operations =
210     {
211     	owner:	THIS_MODULE,
212     	id:	"ICS2101",
213     	name:	"ICS2101 Multimedia Mixer",
214     	ioctl:	ics2101_mixer_ioctl
215     };
216     
217     int __init ics2101_mixer_init(void)
218     {
219     	int i;
220     	int n;
221     
222     	if ((n = sound_alloc_mixerdev()) != -1)
223     	{
224     		mixer_devs[n] = &ics2101_mixer_operations;
225     
226     		/*
227     		 * Some GUS v3.7 cards had some channels flipped. Disable
228     		 * the flipping feature if the model id is other than 5.
229     		 */
230     
231     		if (inb(u_MixSelect) != 5)
232     		{
233     			for (i = 0; i < ICS_MIXDEVS; i++)
234     				left_fix[i] = 1;
235     			for (i = 0; i < ICS_MIXDEVS; i++)
236     				right_fix[i] = 2;
237     		}
238     		set_volumes(DEV_GF1, 0x5a5a);
239     		set_volumes(DEV_CD, 0x5a5a);
240     		set_volumes(DEV_MIC, 0x0000);
241     		set_volumes(DEV_LINE, 0x5a5a);
242     		set_volumes(DEV_VOL, 0x5a5a);
243     		set_volumes(DEV_UNUSED, 0x0000);
244     	}
245     	return n;
246     }
247