11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Beep using pcm
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (c) by Takashi Iwai <tiwai@suse.de>
61da177e4SLinus Torvalds */
71da177e4SLinus Torvalds
86cbbfe1cSTakashi Iwai #include <linux/io.h>
91da177e4SLinus Torvalds #include <asm/irq.h>
101da177e4SLinus Torvalds #include <linux/init.h>
111da177e4SLinus Torvalds #include <linux/slab.h>
121da177e4SLinus Torvalds #include <linux/input.h>
137bbd8277SBenjamin Herrenschmidt #include <linux/pci.h>
147bbd8277SBenjamin Herrenschmidt #include <linux/dma-mapping.h>
151da177e4SLinus Torvalds #include <sound/core.h>
161da177e4SLinus Torvalds #include <sound/control.h>
171da177e4SLinus Torvalds #include "pmac.h"
181da177e4SLinus Torvalds
1965b29f50STakashi Iwai struct pmac_beep {
201da177e4SLinus Torvalds int running; /* boolean */
211da177e4SLinus Torvalds int volume; /* mixer volume: 0-100 */
221da177e4SLinus Torvalds int volume_play; /* currently playing volume */
231da177e4SLinus Torvalds int hz;
241da177e4SLinus Torvalds int nsamples;
251da177e4SLinus Torvalds short *buf; /* allocated wave buffer */
267bbd8277SBenjamin Herrenschmidt dma_addr_t addr; /* physical address of buffer */
275ebdcbc2SDmitry Torokhov struct input_dev *dev;
281da177e4SLinus Torvalds };
291da177e4SLinus Torvalds
301da177e4SLinus Torvalds /*
311da177e4SLinus Torvalds * stop beep if running
321da177e4SLinus Torvalds */
snd_pmac_beep_stop(struct snd_pmac * chip)3365b29f50STakashi Iwai void snd_pmac_beep_stop(struct snd_pmac *chip)
341da177e4SLinus Torvalds {
3565b29f50STakashi Iwai struct pmac_beep *beep = chip->beep;
361da177e4SLinus Torvalds if (beep && beep->running) {
371da177e4SLinus Torvalds beep->running = 0;
381da177e4SLinus Torvalds snd_pmac_beep_dma_stop(chip);
391da177e4SLinus Torvalds }
401da177e4SLinus Torvalds }
411da177e4SLinus Torvalds
421da177e4SLinus Torvalds /*
431da177e4SLinus Torvalds * Stuff for outputting a beep. The values range from -327 to +327
441da177e4SLinus Torvalds * so we can multiply by an amplitude in the range 0..100 to get a
451da177e4SLinus Torvalds * signed short value to put in the output buffer.
461da177e4SLinus Torvalds */
476e9ef32fSTakashi Iwai static const short beep_wform[256] = {
481da177e4SLinus Torvalds 0, 40, 79, 117, 153, 187, 218, 245,
491da177e4SLinus Torvalds 269, 288, 304, 316, 323, 327, 327, 324,
501da177e4SLinus Torvalds 318, 310, 299, 288, 275, 262, 249, 236,
511da177e4SLinus Torvalds 224, 213, 204, 196, 190, 186, 183, 182,
521da177e4SLinus Torvalds 182, 183, 186, 189, 192, 196, 200, 203,
531da177e4SLinus Torvalds 206, 208, 209, 209, 209, 207, 204, 201,
541da177e4SLinus Torvalds 197, 193, 188, 183, 179, 174, 170, 166,
551da177e4SLinus Torvalds 163, 161, 160, 159, 159, 160, 161, 162,
561da177e4SLinus Torvalds 164, 166, 168, 169, 171, 171, 171, 170,
571da177e4SLinus Torvalds 169, 167, 163, 159, 155, 150, 144, 139,
581da177e4SLinus Torvalds 133, 128, 122, 117, 113, 110, 107, 105,
591da177e4SLinus Torvalds 103, 103, 103, 103, 104, 104, 105, 105,
601da177e4SLinus Torvalds 105, 103, 101, 97, 92, 86, 78, 68,
611da177e4SLinus Torvalds 58, 45, 32, 18, 3, -11, -26, -41,
621da177e4SLinus Torvalds -55, -68, -79, -88, -95, -100, -102, -102,
631da177e4SLinus Torvalds -99, -93, -85, -75, -62, -48, -33, -16,
641da177e4SLinus Torvalds 0, 16, 33, 48, 62, 75, 85, 93,
651da177e4SLinus Torvalds 99, 102, 102, 100, 95, 88, 79, 68,
661da177e4SLinus Torvalds 55, 41, 26, 11, -3, -18, -32, -45,
671da177e4SLinus Torvalds -58, -68, -78, -86, -92, -97, -101, -103,
681da177e4SLinus Torvalds -105, -105, -105, -104, -104, -103, -103, -103,
691da177e4SLinus Torvalds -103, -105, -107, -110, -113, -117, -122, -128,
701da177e4SLinus Torvalds -133, -139, -144, -150, -155, -159, -163, -167,
711da177e4SLinus Torvalds -169, -170, -171, -171, -171, -169, -168, -166,
721da177e4SLinus Torvalds -164, -162, -161, -160, -159, -159, -160, -161,
731da177e4SLinus Torvalds -163, -166, -170, -174, -179, -183, -188, -193,
741da177e4SLinus Torvalds -197, -201, -204, -207, -209, -209, -209, -208,
751da177e4SLinus Torvalds -206, -203, -200, -196, -192, -189, -186, -183,
761da177e4SLinus Torvalds -182, -182, -183, -186, -190, -196, -204, -213,
771da177e4SLinus Torvalds -224, -236, -249, -262, -275, -288, -299, -310,
781da177e4SLinus Torvalds -318, -324, -327, -327, -323, -316, -304, -288,
791da177e4SLinus Torvalds -269, -245, -218, -187, -153, -117, -79, -40,
801da177e4SLinus Torvalds };
811da177e4SLinus Torvalds
821da177e4SLinus Torvalds #define BEEP_SRATE 22050 /* 22050 Hz sample rate */
831da177e4SLinus Torvalds #define BEEP_BUFLEN 512
841da177e4SLinus Torvalds #define BEEP_VOLUME 15 /* 0 - 100 */
851da177e4SLinus Torvalds
snd_pmac_beep_event(struct input_dev * dev,unsigned int type,unsigned int code,int hz)8665b29f50STakashi Iwai static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type,
8765b29f50STakashi Iwai unsigned int code, int hz)
881da177e4SLinus Torvalds {
8965b29f50STakashi Iwai struct snd_pmac *chip;
9065b29f50STakashi Iwai struct pmac_beep *beep;
911da177e4SLinus Torvalds unsigned long flags;
921da177e4SLinus Torvalds int beep_speed = 0;
931da177e4SLinus Torvalds int srate;
941da177e4SLinus Torvalds int period, ncycles, nsamples;
951da177e4SLinus Torvalds int i, j, f;
961da177e4SLinus Torvalds short *p;
971da177e4SLinus Torvalds
981da177e4SLinus Torvalds if (type != EV_SND)
991da177e4SLinus Torvalds return -1;
1001da177e4SLinus Torvalds
1011da177e4SLinus Torvalds switch (code) {
102*a98478f8SAnders Roxell case SND_BELL: if (hz) hz = 1000; break;
1031da177e4SLinus Torvalds case SND_TONE: break;
1041da177e4SLinus Torvalds default: return -1;
1051da177e4SLinus Torvalds }
1061da177e4SLinus Torvalds
1071e2831dbSDmitry Torokhov chip = input_get_drvdata(dev);
108e73ad388STakashi Iwai if (!chip)
109e73ad388STakashi Iwai return -1;
110e73ad388STakashi Iwai beep = chip->beep;
111e73ad388STakashi Iwai if (!beep)
1121da177e4SLinus Torvalds return -1;
1131da177e4SLinus Torvalds
1141da177e4SLinus Torvalds if (! hz) {
1151da177e4SLinus Torvalds spin_lock_irqsave(&chip->reg_lock, flags);
1161da177e4SLinus Torvalds if (beep->running)
1171da177e4SLinus Torvalds snd_pmac_beep_stop(chip);
1181da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->reg_lock, flags);
1191da177e4SLinus Torvalds return 0;
1201da177e4SLinus Torvalds }
1211da177e4SLinus Torvalds
1221da177e4SLinus Torvalds beep_speed = snd_pmac_rate_index(chip, &chip->playback, BEEP_SRATE);
1231da177e4SLinus Torvalds srate = chip->freq_table[beep_speed];
1241da177e4SLinus Torvalds
1251da177e4SLinus Torvalds if (hz <= srate / BEEP_BUFLEN || hz > srate / 2)
1261da177e4SLinus Torvalds hz = 1000;
1271da177e4SLinus Torvalds
1281da177e4SLinus Torvalds spin_lock_irqsave(&chip->reg_lock, flags);
1291da177e4SLinus Torvalds if (chip->playback.running || chip->capture.running || beep->running) {
1301da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->reg_lock, flags);
1311da177e4SLinus Torvalds return 0;
1321da177e4SLinus Torvalds }
1331da177e4SLinus Torvalds beep->running = 1;
1341da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->reg_lock, flags);
1351da177e4SLinus Torvalds
1361da177e4SLinus Torvalds if (hz == beep->hz && beep->volume == beep->volume_play) {
1371da177e4SLinus Torvalds nsamples = beep->nsamples;
1381da177e4SLinus Torvalds } else {
1391da177e4SLinus Torvalds period = srate * 256 / hz; /* fixed point */
1401da177e4SLinus Torvalds ncycles = BEEP_BUFLEN * 256 / period;
1411da177e4SLinus Torvalds nsamples = (period * ncycles) >> 8;
1421da177e4SLinus Torvalds f = ncycles * 65536 / nsamples;
1431da177e4SLinus Torvalds j = 0;
1441da177e4SLinus Torvalds p = beep->buf;
1451da177e4SLinus Torvalds for (i = 0; i < nsamples; ++i, p += 2) {
1461da177e4SLinus Torvalds p[0] = p[1] = beep_wform[j >> 8] * beep->volume;
1471da177e4SLinus Torvalds j = (j + f) & 0xffff;
1481da177e4SLinus Torvalds }
1491da177e4SLinus Torvalds beep->hz = hz;
1501da177e4SLinus Torvalds beep->volume_play = beep->volume;
1511da177e4SLinus Torvalds beep->nsamples = nsamples;
1521da177e4SLinus Torvalds }
1531da177e4SLinus Torvalds
1541da177e4SLinus Torvalds spin_lock_irqsave(&chip->reg_lock, flags);
1551da177e4SLinus Torvalds snd_pmac_beep_dma_start(chip, beep->nsamples * 4, beep->addr, beep_speed);
1561da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->reg_lock, flags);
1571da177e4SLinus Torvalds return 0;
1581da177e4SLinus Torvalds }
1591da177e4SLinus Torvalds
1601da177e4SLinus Torvalds /*
1611da177e4SLinus Torvalds * beep volume mixer
1621da177e4SLinus Torvalds */
1631da177e4SLinus Torvalds
snd_pmac_info_beep(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)16465b29f50STakashi Iwai static int snd_pmac_info_beep(struct snd_kcontrol *kcontrol,
16565b29f50STakashi Iwai struct snd_ctl_elem_info *uinfo)
1661da177e4SLinus Torvalds {
1671da177e4SLinus Torvalds uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
1681da177e4SLinus Torvalds uinfo->count = 1;
1691da177e4SLinus Torvalds uinfo->value.integer.min = 0;
1701da177e4SLinus Torvalds uinfo->value.integer.max = 100;
1711da177e4SLinus Torvalds return 0;
1721da177e4SLinus Torvalds }
1731da177e4SLinus Torvalds
snd_pmac_get_beep(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)17465b29f50STakashi Iwai static int snd_pmac_get_beep(struct snd_kcontrol *kcontrol,
17565b29f50STakashi Iwai struct snd_ctl_elem_value *ucontrol)
1761da177e4SLinus Torvalds {
17765b29f50STakashi Iwai struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
1785e246b85STakashi Iwai if (snd_BUG_ON(!chip->beep))
1795e246b85STakashi Iwai return -ENXIO;
1801da177e4SLinus Torvalds ucontrol->value.integer.value[0] = chip->beep->volume;
1811da177e4SLinus Torvalds return 0;
1821da177e4SLinus Torvalds }
1831da177e4SLinus Torvalds
snd_pmac_put_beep(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)18465b29f50STakashi Iwai static int snd_pmac_put_beep(struct snd_kcontrol *kcontrol,
18565b29f50STakashi Iwai struct snd_ctl_elem_value *ucontrol)
1861da177e4SLinus Torvalds {
18765b29f50STakashi Iwai struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
188d4079ac4STakashi Iwai unsigned int oval, nval;
1895e246b85STakashi Iwai if (snd_BUG_ON(!chip->beep))
1905e246b85STakashi Iwai return -ENXIO;
1911da177e4SLinus Torvalds oval = chip->beep->volume;
192d4079ac4STakashi Iwai nval = ucontrol->value.integer.value[0];
193d4079ac4STakashi Iwai if (nval > 100)
194d4079ac4STakashi Iwai return -EINVAL;
195d4079ac4STakashi Iwai chip->beep->volume = nval;
1961da177e4SLinus Torvalds return oval != chip->beep->volume;
1971da177e4SLinus Torvalds }
1981da177e4SLinus Torvalds
199905e46acSBhumika Goyal static const struct snd_kcontrol_new snd_pmac_beep_mixer = {
2001da177e4SLinus Torvalds .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2011da177e4SLinus Torvalds .name = "Beep Playback Volume",
2021da177e4SLinus Torvalds .info = snd_pmac_info_beep,
2031da177e4SLinus Torvalds .get = snd_pmac_get_beep,
2041da177e4SLinus Torvalds .put = snd_pmac_put_beep,
2051da177e4SLinus Torvalds };
2061da177e4SLinus Torvalds
2071da177e4SLinus Torvalds /* Initialize beep stuff */
snd_pmac_attach_beep(struct snd_pmac * chip)20815afafc2SBill Pemberton int snd_pmac_attach_beep(struct snd_pmac *chip)
2091da177e4SLinus Torvalds {
21065b29f50STakashi Iwai struct pmac_beep *beep;
2115ebdcbc2SDmitry Torokhov struct input_dev *input_dev;
212f03d68feSDmitry Torokhov struct snd_kcontrol *beep_ctl;
2135ebdcbc2SDmitry Torokhov void *dmabuf;
2145ebdcbc2SDmitry Torokhov int err = -ENOMEM;
2151da177e4SLinus Torvalds
2165ebdcbc2SDmitry Torokhov beep = kzalloc(sizeof(*beep), GFP_KERNEL);
217f03d68feSDmitry Torokhov if (! beep)
218f03d68feSDmitry Torokhov return -ENOMEM;
2195ebdcbc2SDmitry Torokhov dmabuf = dma_alloc_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
2207bbd8277SBenjamin Herrenschmidt &beep->addr, GFP_KERNEL);
2215ebdcbc2SDmitry Torokhov input_dev = input_allocate_device();
222f03d68feSDmitry Torokhov if (! dmabuf || ! input_dev)
223f03d68feSDmitry Torokhov goto fail1;
2241da177e4SLinus Torvalds
2251da177e4SLinus Torvalds /* FIXME: set more better values */
2265ebdcbc2SDmitry Torokhov input_dev->name = "PowerMac Beep";
2275ebdcbc2SDmitry Torokhov input_dev->phys = "powermac/beep";
2285ebdcbc2SDmitry Torokhov input_dev->id.bustype = BUS_ADB;
2295ebdcbc2SDmitry Torokhov input_dev->id.vendor = 0x001f;
2305ebdcbc2SDmitry Torokhov input_dev->id.product = 0x0001;
2315ebdcbc2SDmitry Torokhov input_dev->id.version = 0x0100;
2321da177e4SLinus Torvalds
2337b19ada2SJiri Slaby input_dev->evbit[0] = BIT_MASK(EV_SND);
2347b19ada2SJiri Slaby input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
2355ebdcbc2SDmitry Torokhov input_dev->event = snd_pmac_beep_event;
2361e2831dbSDmitry Torokhov input_dev->dev.parent = &chip->pdev->dev;
2371e2831dbSDmitry Torokhov input_set_drvdata(input_dev, chip);
2385ebdcbc2SDmitry Torokhov
2395ebdcbc2SDmitry Torokhov beep->dev = input_dev;
2405ebdcbc2SDmitry Torokhov beep->buf = dmabuf;
2411da177e4SLinus Torvalds beep->volume = BEEP_VOLUME;
2421da177e4SLinus Torvalds beep->running = 0;
2435ebdcbc2SDmitry Torokhov
244f03d68feSDmitry Torokhov beep_ctl = snd_ctl_new1(&snd_pmac_beep_mixer, chip);
245f03d68feSDmitry Torokhov err = snd_ctl_add(chip->card, beep_ctl);
2465ebdcbc2SDmitry Torokhov if (err < 0)
247f03d68feSDmitry Torokhov goto fail1;
2481da177e4SLinus Torvalds
2491da177e4SLinus Torvalds chip->beep = beep;
250f03d68feSDmitry Torokhov
251f03d68feSDmitry Torokhov err = input_register_device(beep->dev);
252f03d68feSDmitry Torokhov if (err)
253f03d68feSDmitry Torokhov goto fail2;
2541da177e4SLinus Torvalds
2551da177e4SLinus Torvalds return 0;
2565ebdcbc2SDmitry Torokhov
257f03d68feSDmitry Torokhov fail2: snd_ctl_remove(chip->card, beep_ctl);
258f03d68feSDmitry Torokhov fail1: input_free_device(input_dev);
259f03d68feSDmitry Torokhov if (dmabuf)
260f03d68feSDmitry Torokhov dma_free_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
261f03d68feSDmitry Torokhov dmabuf, beep->addr);
2625ebdcbc2SDmitry Torokhov kfree(beep);
2635ebdcbc2SDmitry Torokhov return err;
2641da177e4SLinus Torvalds }
2651da177e4SLinus Torvalds
snd_pmac_detach_beep(struct snd_pmac * chip)26665b29f50STakashi Iwai void snd_pmac_detach_beep(struct snd_pmac *chip)
2671da177e4SLinus Torvalds {
2681da177e4SLinus Torvalds if (chip->beep) {
2695ebdcbc2SDmitry Torokhov input_unregister_device(chip->beep->dev);
2707bbd8277SBenjamin Herrenschmidt dma_free_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
2717bbd8277SBenjamin Herrenschmidt chip->beep->buf, chip->beep->addr);
2721da177e4SLinus Torvalds kfree(chip->beep);
2731da177e4SLinus Torvalds chip->beep = NULL;
2741da177e4SLinus Torvalds }
2751da177e4SLinus Torvalds }
276