blob: 6bc586a5db0f221de52a79e78671472d3bda98d4 [file] [log] [blame]
Thomas Gleixner1a59d1b82019-05-27 08:55:05 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * Beep using pcm
4 *
5 * Copyright (c) by Takashi Iwai <tiwai@suse.de>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 */
7
Takashi Iwai6cbbfe12015-01-28 16:49:33 +01008#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include <asm/irq.h>
10#include <linux/init.h>
11#include <linux/slab.h>
12#include <linux/input.h>
Benjamin Herrenschmidt7bbd8272005-04-16 15:24:32 -070013#include <linux/pci.h>
14#include <linux/dma-mapping.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <sound/core.h>
16#include <sound/control.h>
17#include "pmac.h"
18
Takashi Iwai65b29f52005-11-17 15:09:46 +010019struct pmac_beep {
Dmitry Torokhov5ebdcbc2005-09-15 02:01:49 -050020 int running; /* boolean */
21 int volume; /* mixer volume: 0-100 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070022 int volume_play; /* currently playing volume */
23 int hz;
24 int nsamples;
25 short *buf; /* allocated wave buffer */
Benjamin Herrenschmidt7bbd8272005-04-16 15:24:32 -070026 dma_addr_t addr; /* physical address of buffer */
Dmitry Torokhov5ebdcbc2005-09-15 02:01:49 -050027 struct input_dev *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -070028};
29
30/*
31 * stop beep if running
32 */
Takashi Iwai65b29f52005-11-17 15:09:46 +010033void snd_pmac_beep_stop(struct snd_pmac *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -070034{
Takashi Iwai65b29f52005-11-17 15:09:46 +010035 struct pmac_beep *beep = chip->beep;
Linus Torvalds1da177e2005-04-16 15:20:36 -070036 if (beep && beep->running) {
37 beep->running = 0;
38 snd_pmac_beep_dma_stop(chip);
39 }
40}
41
42/*
43 * Stuff for outputting a beep. The values range from -327 to +327
44 * so we can multiply by an amplitude in the range 0..100 to get a
45 * signed short value to put in the output buffer.
46 */
Takashi Iwai6e9ef322020-01-05 15:47:32 +010047static const short beep_wform[256] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 0, 40, 79, 117, 153, 187, 218, 245,
49 269, 288, 304, 316, 323, 327, 327, 324,
50 318, 310, 299, 288, 275, 262, 249, 236,
51 224, 213, 204, 196, 190, 186, 183, 182,
52 182, 183, 186, 189, 192, 196, 200, 203,
53 206, 208, 209, 209, 209, 207, 204, 201,
54 197, 193, 188, 183, 179, 174, 170, 166,
55 163, 161, 160, 159, 159, 160, 161, 162,
56 164, 166, 168, 169, 171, 171, 171, 170,
57 169, 167, 163, 159, 155, 150, 144, 139,
58 133, 128, 122, 117, 113, 110, 107, 105,
59 103, 103, 103, 103, 104, 104, 105, 105,
60 105, 103, 101, 97, 92, 86, 78, 68,
61 58, 45, 32, 18, 3, -11, -26, -41,
62 -55, -68, -79, -88, -95, -100, -102, -102,
63 -99, -93, -85, -75, -62, -48, -33, -16,
64 0, 16, 33, 48, 62, 75, 85, 93,
65 99, 102, 102, 100, 95, 88, 79, 68,
66 55, 41, 26, 11, -3, -18, -32, -45,
67 -58, -68, -78, -86, -92, -97, -101, -103,
68 -105, -105, -105, -104, -104, -103, -103, -103,
69 -103, -105, -107, -110, -113, -117, -122, -128,
70 -133, -139, -144, -150, -155, -159, -163, -167,
71 -169, -170, -171, -171, -171, -169, -168, -166,
72 -164, -162, -161, -160, -159, -159, -160, -161,
73 -163, -166, -170, -174, -179, -183, -188, -193,
74 -197, -201, -204, -207, -209, -209, -209, -208,
75 -206, -203, -200, -196, -192, -189, -186, -183,
76 -182, -182, -183, -186, -190, -196, -204, -213,
77 -224, -236, -249, -262, -275, -288, -299, -310,
78 -318, -324, -327, -327, -323, -316, -304, -288,
79 -269, -245, -218, -187, -153, -117, -79, -40,
80};
81
82#define BEEP_SRATE 22050 /* 22050 Hz sample rate */
83#define BEEP_BUFLEN 512
84#define BEEP_VOLUME 15 /* 0 - 100 */
85
Takashi Iwai65b29f52005-11-17 15:09:46 +010086static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type,
87 unsigned int code, int hz)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088{
Takashi Iwai65b29f52005-11-17 15:09:46 +010089 struct snd_pmac *chip;
90 struct pmac_beep *beep;
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 unsigned long flags;
92 int beep_speed = 0;
93 int srate;
94 int period, ncycles, nsamples;
95 int i, j, f;
96 short *p;
97
98 if (type != EV_SND)
99 return -1;
100
101 switch (code) {
102 case SND_BELL: if (hz) hz = 1000;
103 case SND_TONE: break;
104 default: return -1;
105 }
106
Dmitry Torokhov1e2831d2007-06-14 23:58:51 -0400107 chip = input_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 if (! chip || (beep = chip->beep) == NULL)
109 return -1;
110
111 if (! hz) {
112 spin_lock_irqsave(&chip->reg_lock, flags);
113 if (beep->running)
114 snd_pmac_beep_stop(chip);
115 spin_unlock_irqrestore(&chip->reg_lock, flags);
116 return 0;
117 }
118
119 beep_speed = snd_pmac_rate_index(chip, &chip->playback, BEEP_SRATE);
120 srate = chip->freq_table[beep_speed];
121
122 if (hz <= srate / BEEP_BUFLEN || hz > srate / 2)
123 hz = 1000;
124
125 spin_lock_irqsave(&chip->reg_lock, flags);
126 if (chip->playback.running || chip->capture.running || beep->running) {
127 spin_unlock_irqrestore(&chip->reg_lock, flags);
128 return 0;
129 }
130 beep->running = 1;
131 spin_unlock_irqrestore(&chip->reg_lock, flags);
132
133 if (hz == beep->hz && beep->volume == beep->volume_play) {
134 nsamples = beep->nsamples;
135 } else {
136 period = srate * 256 / hz; /* fixed point */
137 ncycles = BEEP_BUFLEN * 256 / period;
138 nsamples = (period * ncycles) >> 8;
139 f = ncycles * 65536 / nsamples;
140 j = 0;
141 p = beep->buf;
142 for (i = 0; i < nsamples; ++i, p += 2) {
143 p[0] = p[1] = beep_wform[j >> 8] * beep->volume;
144 j = (j + f) & 0xffff;
145 }
146 beep->hz = hz;
147 beep->volume_play = beep->volume;
148 beep->nsamples = nsamples;
149 }
150
151 spin_lock_irqsave(&chip->reg_lock, flags);
152 snd_pmac_beep_dma_start(chip, beep->nsamples * 4, beep->addr, beep_speed);
153 spin_unlock_irqrestore(&chip->reg_lock, flags);
154 return 0;
155}
156
157/*
158 * beep volume mixer
159 */
160
Takashi Iwai65b29f52005-11-17 15:09:46 +0100161static int snd_pmac_info_beep(struct snd_kcontrol *kcontrol,
162 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163{
164 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
165 uinfo->count = 1;
166 uinfo->value.integer.min = 0;
167 uinfo->value.integer.max = 100;
168 return 0;
169}
170
Takashi Iwai65b29f52005-11-17 15:09:46 +0100171static int snd_pmac_get_beep(struct snd_kcontrol *kcontrol,
172 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100174 struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
Takashi Iwai5e246b82008-08-08 17:12:47 +0200175 if (snd_BUG_ON(!chip->beep))
176 return -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 ucontrol->value.integer.value[0] = chip->beep->volume;
178 return 0;
179}
180
Takashi Iwai65b29f52005-11-17 15:09:46 +0100181static int snd_pmac_put_beep(struct snd_kcontrol *kcontrol,
182 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100184 struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
Takashi Iwaid4079ac2007-11-15 16:14:12 +0100185 unsigned int oval, nval;
Takashi Iwai5e246b82008-08-08 17:12:47 +0200186 if (snd_BUG_ON(!chip->beep))
187 return -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 oval = chip->beep->volume;
Takashi Iwaid4079ac2007-11-15 16:14:12 +0100189 nval = ucontrol->value.integer.value[0];
190 if (nval > 100)
191 return -EINVAL;
192 chip->beep->volume = nval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 return oval != chip->beep->volume;
194}
195
Bhumika Goyal905e46a2017-05-27 20:16:15 +0530196static const struct snd_kcontrol_new snd_pmac_beep_mixer = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
198 .name = "Beep Playback Volume",
199 .info = snd_pmac_info_beep,
200 .get = snd_pmac_get_beep,
201 .put = snd_pmac_put_beep,
202};
203
204/* Initialize beep stuff */
Bill Pemberton15afafc2012-12-06 12:35:23 -0500205int snd_pmac_attach_beep(struct snd_pmac *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100207 struct pmac_beep *beep;
Dmitry Torokhov5ebdcbc2005-09-15 02:01:49 -0500208 struct input_dev *input_dev;
Dmitry Torokhovf03d68f2006-08-03 15:06:14 +0200209 struct snd_kcontrol *beep_ctl;
Dmitry Torokhov5ebdcbc2005-09-15 02:01:49 -0500210 void *dmabuf;
211 int err = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212
Dmitry Torokhov5ebdcbc2005-09-15 02:01:49 -0500213 beep = kzalloc(sizeof(*beep), GFP_KERNEL);
Dmitry Torokhovf03d68f2006-08-03 15:06:14 +0200214 if (! beep)
215 return -ENOMEM;
Dmitry Torokhov5ebdcbc2005-09-15 02:01:49 -0500216 dmabuf = dma_alloc_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
217 &beep->addr, GFP_KERNEL);
218 input_dev = input_allocate_device();
Dmitry Torokhovf03d68f2006-08-03 15:06:14 +0200219 if (! dmabuf || ! input_dev)
220 goto fail1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221
222 /* FIXME: set more better values */
Dmitry Torokhov5ebdcbc2005-09-15 02:01:49 -0500223 input_dev->name = "PowerMac Beep";
224 input_dev->phys = "powermac/beep";
225 input_dev->id.bustype = BUS_ADB;
226 input_dev->id.vendor = 0x001f;
227 input_dev->id.product = 0x0001;
228 input_dev->id.version = 0x0100;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229
Jiri Slaby7b19ada2007-10-18 23:40:32 -0700230 input_dev->evbit[0] = BIT_MASK(EV_SND);
231 input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
Dmitry Torokhov5ebdcbc2005-09-15 02:01:49 -0500232 input_dev->event = snd_pmac_beep_event;
Dmitry Torokhov1e2831d2007-06-14 23:58:51 -0400233 input_dev->dev.parent = &chip->pdev->dev;
234 input_set_drvdata(input_dev, chip);
Dmitry Torokhov5ebdcbc2005-09-15 02:01:49 -0500235
236 beep->dev = input_dev;
237 beep->buf = dmabuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 beep->volume = BEEP_VOLUME;
239 beep->running = 0;
Dmitry Torokhov5ebdcbc2005-09-15 02:01:49 -0500240
Dmitry Torokhovf03d68f2006-08-03 15:06:14 +0200241 beep_ctl = snd_ctl_new1(&snd_pmac_beep_mixer, chip);
242 err = snd_ctl_add(chip->card, beep_ctl);
Dmitry Torokhov5ebdcbc2005-09-15 02:01:49 -0500243 if (err < 0)
Dmitry Torokhovf03d68f2006-08-03 15:06:14 +0200244 goto fail1;
Dmitry Torokhov1e2831d2007-06-14 23:58:51 -0400245
246 chip->beep = beep;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247
Dmitry Torokhovf03d68f2006-08-03 15:06:14 +0200248 err = input_register_device(beep->dev);
249 if (err)
250 goto fail2;
251
252 return 0;
253
254 fail2: snd_ctl_remove(chip->card, beep_ctl);
255 fail1: input_free_device(input_dev);
256 if (dmabuf)
257 dma_free_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
258 dmabuf, beep->addr);
Dmitry Torokhov5ebdcbc2005-09-15 02:01:49 -0500259 kfree(beep);
260 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261}
262
Takashi Iwai65b29f52005-11-17 15:09:46 +0100263void snd_pmac_detach_beep(struct snd_pmac *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264{
265 if (chip->beep) {
Dmitry Torokhov5ebdcbc2005-09-15 02:01:49 -0500266 input_unregister_device(chip->beep->dev);
Benjamin Herrenschmidt7bbd8272005-04-16 15:24:32 -0700267 dma_free_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
268 chip->beep->buf, chip->beep->addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 kfree(chip->beep);
270 chip->beep = NULL;
271 }
272}