Blame view

sound/ppc/beep.c 7.75 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  /*
   * Beep using pcm
   *
   * Copyright (c) by Takashi Iwai <tiwai@suse.de>
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   *   This program is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with this program; if not, write to the Free Software
   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
21
22
23
24
  #include <asm/io.h>
  #include <asm/irq.h>
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/input.h>
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
25
26
  #include <linux/pci.h>
  #include <linux/dma-mapping.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
  #include <sound/core.h>
  #include <sound/control.h>
  #include "pmac.h"
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
30
  struct pmac_beep {
5ebdcbc2f   Dmitry Torokhov   [PATCH] Input: co...
31
32
  	int running;		/* boolean */
  	int volume;		/* mixer volume: 0-100 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
  	int volume_play;	/* currently playing volume */
  	int hz;
  	int nsamples;
  	short *buf;		/* allocated wave buffer */
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
37
  	dma_addr_t addr;	/* physical address of buffer */
5ebdcbc2f   Dmitry Torokhov   [PATCH] Input: co...
38
  	struct input_dev *dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
41
42
43
  };
  
  /*
   * stop beep if running
   */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
44
  void snd_pmac_beep_stop(struct snd_pmac *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
46
  	struct pmac_beep *beep = chip->beep;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
  	if (beep && beep->running) {
  		beep->running = 0;
  		snd_pmac_beep_dma_stop(chip);
  	}
  }
  
  /*
   * Stuff for outputting a beep.  The values range from -327 to +327
   * so we can multiply by an amplitude in the range 0..100 to get a
   * signed short value to put in the output buffer.
   */
  static short beep_wform[256] = {
  	0,	40,	79,	117,	153,	187,	218,	245,
  	269,	288,	304,	316,	323,	327,	327,	324,
  	318,	310,	299,	288,	275,	262,	249,	236,
  	224,	213,	204,	196,	190,	186,	183,	182,
  	182,	183,	186,	189,	192,	196,	200,	203,
  	206,	208,	209,	209,	209,	207,	204,	201,
  	197,	193,	188,	183,	179,	174,	170,	166,
  	163,	161,	160,	159,	159,	160,	161,	162,
  	164,	166,	168,	169,	171,	171,	171,	170,
  	169,	167,	163,	159,	155,	150,	144,	139,
  	133,	128,	122,	117,	113,	110,	107,	105,
  	103,	103,	103,	103,	104,	104,	105,	105,
  	105,	103,	101,	97,	92,	86,	78,	68,
  	58,	45,	32,	18,	3,	-11,	-26,	-41,
  	-55,	-68,	-79,	-88,	-95,	-100,	-102,	-102,
  	-99,	-93,	-85,	-75,	-62,	-48,	-33,	-16,
  	0,	16,	33,	48,	62,	75,	85,	93,
  	99,	102,	102,	100,	95,	88,	79,	68,
  	55,	41,	26,	11,	-3,	-18,	-32,	-45,
  	-58,	-68,	-78,	-86,	-92,	-97,	-101,	-103,
  	-105,	-105,	-105,	-104,	-104,	-103,	-103,	-103,
  	-103,	-105,	-107,	-110,	-113,	-117,	-122,	-128,
  	-133,	-139,	-144,	-150,	-155,	-159,	-163,	-167,
  	-169,	-170,	-171,	-171,	-171,	-169,	-168,	-166,
  	-164,	-162,	-161,	-160,	-159,	-159,	-160,	-161,
  	-163,	-166,	-170,	-174,	-179,	-183,	-188,	-193,
  	-197,	-201,	-204,	-207,	-209,	-209,	-209,	-208,
  	-206,	-203,	-200,	-196,	-192,	-189,	-186,	-183,
  	-182,	-182,	-183,	-186,	-190,	-196,	-204,	-213,
  	-224,	-236,	-249,	-262,	-275,	-288,	-299,	-310,
  	-318,	-324,	-327,	-327,	-323,	-316,	-304,	-288,
  	-269,	-245,	-218,	-187,	-153,	-117,	-79,	-40,
  };
  
  #define BEEP_SRATE	22050	/* 22050 Hz sample rate */
  #define BEEP_BUFLEN	512
  #define BEEP_VOLUME	15	/* 0 - 100 */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
96
97
  static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type,
  			       unsigned int code, int hz)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
99
100
  	struct snd_pmac *chip;
  	struct pmac_beep *beep;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
  	unsigned long flags;
  	int beep_speed = 0;
  	int srate;
  	int period, ncycles, nsamples;
  	int i, j, f;
  	short *p;
  
  	if (type != EV_SND)
  		return -1;
  
  	switch (code) {
  	case SND_BELL: if (hz) hz = 1000;
  	case SND_TONE: break;
  	default: return -1;
  	}
1e2831db0   Dmitry Torokhov   Input: ppc-beep -...
116
  	chip = input_get_drvdata(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
  	if (! chip || (beep = chip->beep) == NULL)
  		return -1;
  
  	if (! hz) {
  		spin_lock_irqsave(&chip->reg_lock, flags);
  		if (beep->running)
  			snd_pmac_beep_stop(chip);
  		spin_unlock_irqrestore(&chip->reg_lock, flags);
  		return 0;
  	}
  
  	beep_speed = snd_pmac_rate_index(chip, &chip->playback, BEEP_SRATE);
  	srate = chip->freq_table[beep_speed];
  
  	if (hz <= srate / BEEP_BUFLEN || hz > srate / 2)
  		hz = 1000;
  
  	spin_lock_irqsave(&chip->reg_lock, flags);
  	if (chip->playback.running || chip->capture.running || beep->running) {
  		spin_unlock_irqrestore(&chip->reg_lock, flags);
  		return 0;
  	}
  	beep->running = 1;
  	spin_unlock_irqrestore(&chip->reg_lock, flags);
  
  	if (hz == beep->hz && beep->volume == beep->volume_play) {
  		nsamples = beep->nsamples;
  	} else {
  		period = srate * 256 / hz;	/* fixed point */
  		ncycles = BEEP_BUFLEN * 256 / period;
  		nsamples = (period * ncycles) >> 8;
  		f = ncycles * 65536 / nsamples;
  		j = 0;
  		p = beep->buf;
  		for (i = 0; i < nsamples; ++i, p += 2) {
  			p[0] = p[1] = beep_wform[j >> 8] * beep->volume;
  			j = (j + f) & 0xffff;
  		}
  		beep->hz = hz;
  		beep->volume_play = beep->volume;
  		beep->nsamples = nsamples;
  	}
  
  	spin_lock_irqsave(&chip->reg_lock, flags);
  	snd_pmac_beep_dma_start(chip, beep->nsamples * 4, beep->addr, beep_speed);
  	spin_unlock_irqrestore(&chip->reg_lock, flags);
  	return 0;
  }
  
  /*
   * beep volume mixer
   */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
169
170
  static int snd_pmac_info_beep(struct snd_kcontrol *kcontrol,
  			      struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
173
174
175
176
177
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->count = 1;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = 100;
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
178
179
  static int snd_pmac_get_beep(struct snd_kcontrol *kcontrol,
  			     struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
181
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
5e246b850   Takashi Iwai   ALSA: Kill snd_as...
182
183
  	if (snd_BUG_ON(!chip->beep))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
186
  	ucontrol->value.integer.value[0] = chip->beep->volume;
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
187
188
  static int snd_pmac_put_beep(struct snd_kcontrol *kcontrol,
  			     struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
190
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
d4079ac49   Takashi Iwai   [ALSA] powermac -...
191
  	unsigned int oval, nval;
5e246b850   Takashi Iwai   ALSA: Kill snd_as...
192
193
  	if (snd_BUG_ON(!chip->beep))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
  	oval = chip->beep->volume;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
195
196
197
198
  	nval = ucontrol->value.integer.value[0];
  	if (nval > 100)
  		return -EINVAL;
  	chip->beep->volume = nval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
  	return oval != chip->beep->volume;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
201
  static struct snd_kcontrol_new snd_pmac_beep_mixer = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
205
206
207
208
209
  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  	.name = "Beep Playback Volume",
  	.info = snd_pmac_info_beep,
  	.get = snd_pmac_get_beep,
  	.put = snd_pmac_put_beep,
  };
  
  /* Initialize beep stuff */
5c9b6e9e6   Stephen Rothwell   ALSA: sound/ppc: ...
210
  int __devinit snd_pmac_attach_beep(struct snd_pmac *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
212
  	struct pmac_beep *beep;
5ebdcbc2f   Dmitry Torokhov   [PATCH] Input: co...
213
  	struct input_dev *input_dev;
f03d68fe3   Dmitry Torokhov   [ALSA] ppc-beep -...
214
  	struct snd_kcontrol *beep_ctl;
5ebdcbc2f   Dmitry Torokhov   [PATCH] Input: co...
215
216
  	void *dmabuf;
  	int err = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217

5ebdcbc2f   Dmitry Torokhov   [PATCH] Input: co...
218
  	beep = kzalloc(sizeof(*beep), GFP_KERNEL);
f03d68fe3   Dmitry Torokhov   [ALSA] ppc-beep -...
219
220
  	if (! beep)
  		return -ENOMEM;
5ebdcbc2f   Dmitry Torokhov   [PATCH] Input: co...
221
222
223
  	dmabuf = dma_alloc_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
  				    &beep->addr, GFP_KERNEL);
  	input_dev = input_allocate_device();
f03d68fe3   Dmitry Torokhov   [ALSA] ppc-beep -...
224
225
  	if (! dmabuf || ! input_dev)
  		goto fail1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
227
  
  	/* FIXME: set more better values */
5ebdcbc2f   Dmitry Torokhov   [PATCH] Input: co...
228
229
230
231
232
233
  	input_dev->name = "PowerMac Beep";
  	input_dev->phys = "powermac/beep";
  	input_dev->id.bustype = BUS_ADB;
  	input_dev->id.vendor = 0x001f;
  	input_dev->id.product = 0x0001;
  	input_dev->id.version = 0x0100;
7b19ada2e   Jiri Slaby   get rid of input ...
234
235
  	input_dev->evbit[0] = BIT_MASK(EV_SND);
  	input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
5ebdcbc2f   Dmitry Torokhov   [PATCH] Input: co...
236
  	input_dev->event = snd_pmac_beep_event;
1e2831db0   Dmitry Torokhov   Input: ppc-beep -...
237
238
  	input_dev->dev.parent = &chip->pdev->dev;
  	input_set_drvdata(input_dev, chip);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239

5ebdcbc2f   Dmitry Torokhov   [PATCH] Input: co...
240
241
  	beep->dev = input_dev;
  	beep->buf = dmabuf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
243
  	beep->volume = BEEP_VOLUME;
  	beep->running = 0;
5ebdcbc2f   Dmitry Torokhov   [PATCH] Input: co...
244

f03d68fe3   Dmitry Torokhov   [ALSA] ppc-beep -...
245
246
  	beep_ctl = snd_ctl_new1(&snd_pmac_beep_mixer, chip);
  	err = snd_ctl_add(chip->card, beep_ctl);
5ebdcbc2f   Dmitry Torokhov   [PATCH] Input: co...
247
  	if (err < 0)
f03d68fe3   Dmitry Torokhov   [ALSA] ppc-beep -...
248
  		goto fail1;
1e2831db0   Dmitry Torokhov   Input: ppc-beep -...
249
250
  
  	chip->beep = beep;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251

f03d68fe3   Dmitry Torokhov   [ALSA] ppc-beep -...
252
253
254
255
256
257
258
259
260
261
262
  	err = input_register_device(beep->dev);
  	if (err)
  		goto fail2;
   
   	return 0;
   
   fail2:	snd_ctl_remove(chip->card, beep_ctl);
   fail1:	input_free_device(input_dev);
  	if (dmabuf)
  		dma_free_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
  				  dmabuf, beep->addr);
5ebdcbc2f   Dmitry Torokhov   [PATCH] Input: co...
263
264
  	kfree(beep);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
266
  void snd_pmac_detach_beep(struct snd_pmac *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
268
  {
  	if (chip->beep) {
5ebdcbc2f   Dmitry Torokhov   [PATCH] Input: co...
269
  		input_unregister_device(chip->beep->dev);
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
270
271
  		dma_free_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
  				  chip->beep->buf, chip->beep->addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
273
274
275
  		kfree(chip->beep);
  		chip->beep = NULL;
  	}
  }