xref: /linux/sound/ppc/beep.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
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