xref: /linux/sound/pci/hda/hda_beep.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
1248a380aSMatt Ranostay // SPDX-License-Identifier: GPL-2.0+
21cd2224cSMatthew Ranostay /*
31cd2224cSMatthew Ranostay  * Digital Beep Input Interface for HD-audio codec
41cd2224cSMatthew Ranostay  *
5248a380aSMatt Ranostay  * Author: Matt Ranostay <matt.ranostay@konsulko.com>
61cd2224cSMatthew Ranostay  * Copyright (c) 2008 Embedded Alley Solutions Inc
71cd2224cSMatthew Ranostay  */
81cd2224cSMatthew Ranostay 
91cd2224cSMatthew Ranostay #include <linux/input.h>
105a0e3ad6STejun Heo #include <linux/slab.h>
111cd2224cSMatthew Ranostay #include <linux/workqueue.h>
12d81a6d71SPaul Gortmaker #include <linux/export.h>
131cd2224cSMatthew Ranostay #include <sound/core.h>
141cd2224cSMatthew Ranostay #include "hda_beep.h"
15b7b51141STakashi Iwai #include "hda_local.h"
161cd2224cSMatthew Ranostay 
171cd2224cSMatthew Ranostay enum {
181cd2224cSMatthew Ranostay 	DIGBEEP_HZ_STEP = 46875,	/* 46.875 Hz */
191cd2224cSMatthew Ranostay 	DIGBEEP_HZ_MIN = 93750,		/* 93.750 Hz */
201cd2224cSMatthew Ranostay 	DIGBEEP_HZ_MAX = 12000000,	/* 12 KHz */
211cd2224cSMatthew Ranostay };
221cd2224cSMatthew Ranostay 
235ccf835cSTakashi Iwai /* generate or stop tone */
generate_tone(struct hda_beep * beep,int tone)245ccf835cSTakashi Iwai static void generate_tone(struct hda_beep *beep, int tone)
251cd2224cSMatthew Ranostay {
261cd2224cSMatthew Ranostay 	struct hda_codec *codec = beep->codec;
271cd2224cSMatthew Ranostay 
28e914b25eSTakashi Iwai 	if (tone && !beep->playing) {
29e914b25eSTakashi Iwai 		snd_hda_power_up(codec);
305ccf835cSTakashi Iwai 		if (beep->power_hook)
315ccf835cSTakashi Iwai 			beep->power_hook(beep, true);
32e914b25eSTakashi Iwai 		beep->playing = 1;
33e914b25eSTakashi Iwai 	}
34411fe85cSTakashi Iwai 	snd_hda_codec_write(codec, beep->nid, 0,
35e914b25eSTakashi Iwai 			    AC_VERB_SET_BEEP_CONTROL, tone);
36e914b25eSTakashi Iwai 	if (!tone && beep->playing) {
37e914b25eSTakashi Iwai 		beep->playing = 0;
385ccf835cSTakashi Iwai 		if (beep->power_hook)
395ccf835cSTakashi Iwai 			beep->power_hook(beep, false);
40e914b25eSTakashi Iwai 		snd_hda_power_down(codec);
41e914b25eSTakashi Iwai 	}
421cd2224cSMatthew Ranostay }
431cd2224cSMatthew Ranostay 
snd_hda_generate_beep(struct work_struct * work)445ccf835cSTakashi Iwai static void snd_hda_generate_beep(struct work_struct *work)
455ccf835cSTakashi Iwai {
465ccf835cSTakashi Iwai 	struct hda_beep *beep =
475ccf835cSTakashi Iwai 		container_of(work, struct hda_beep, beep_work);
485ccf835cSTakashi Iwai 
495ccf835cSTakashi Iwai 	if (beep->enabled)
505ccf835cSTakashi Iwai 		generate_tone(beep, beep->tone);
515ccf835cSTakashi Iwai }
525ccf835cSTakashi Iwai 
53fa797966STakashi Iwai /* (non-standard) Linear beep tone calculation for IDT/STAC codecs
54fa797966STakashi Iwai  *
55fa797966STakashi Iwai  * The tone frequency of beep generator on IDT/STAC codecs is
56fa797966STakashi Iwai  * defined from the 8bit tone parameter, in Hz,
57fa797966STakashi Iwai  *    freq = 48000 * (257 - tone) / 1024
58369693dcSPaul Vojta  * that is from 12kHz to 93.75Hz in steps of 46.875 Hz
59fa797966STakashi Iwai  */
beep_linear_tone(struct hda_beep * beep,int hz)60fa797966STakashi Iwai static int beep_linear_tone(struct hda_beep *beep, int hz)
611cd2224cSMatthew Ranostay {
62369693dcSPaul Vojta 	if (hz <= 0)
63369693dcSPaul Vojta 		return 0;
641cd2224cSMatthew Ranostay 	hz *= 1000; /* fixed point */
65369693dcSPaul Vojta 	hz = hz - DIGBEEP_HZ_MIN
66369693dcSPaul Vojta 		+ DIGBEEP_HZ_STEP / 2; /* round to nearest step */
671cd2224cSMatthew Ranostay 	if (hz < 0)
681cd2224cSMatthew Ranostay 		hz = 0; /* turn off PC beep*/
691cd2224cSMatthew Ranostay 	else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
70369693dcSPaul Vojta 		hz = 1; /* max frequency */
711cd2224cSMatthew Ranostay 	else {
721cd2224cSMatthew Ranostay 		hz /= DIGBEEP_HZ_STEP;
73369693dcSPaul Vojta 		hz = 255 - hz;
741cd2224cSMatthew Ranostay 	}
75fa797966STakashi Iwai 	return hz;
76fa797966STakashi Iwai }
77fa797966STakashi Iwai 
78fa797966STakashi Iwai /* HD-audio standard beep tone parameter calculation
79fa797966STakashi Iwai  *
80fa797966STakashi Iwai  * The tone frequency in Hz is calculated as
81fa797966STakashi Iwai  *   freq = 48000 / (tone * 4)
82fa797966STakashi Iwai  * from 47Hz to 12kHz
83fa797966STakashi Iwai  */
beep_standard_tone(struct hda_beep * beep,int hz)84fa797966STakashi Iwai static int beep_standard_tone(struct hda_beep *beep, int hz)
85fa797966STakashi Iwai {
86fa797966STakashi Iwai 	if (hz <= 0)
87fa797966STakashi Iwai 		return 0; /* disabled */
88fa797966STakashi Iwai 	hz = 12000 / hz;
89fa797966STakashi Iwai 	if (hz > 0xff)
90fa797966STakashi Iwai 		return 0xff;
91fa797966STakashi Iwai 	if (hz <= 0)
92fa797966STakashi Iwai 		return 1;
93fa797966STakashi Iwai 	return hz;
94fa797966STakashi Iwai }
95fa797966STakashi Iwai 
snd_hda_beep_event(struct input_dev * dev,unsigned int type,unsigned int code,int hz)96fa797966STakashi Iwai static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
97fa797966STakashi Iwai 				unsigned int code, int hz)
98fa797966STakashi Iwai {
99fa797966STakashi Iwai 	struct hda_beep *beep = input_get_drvdata(dev);
100fa797966STakashi Iwai 
101fa797966STakashi Iwai 	switch (code) {
102fa797966STakashi Iwai 	case SND_BELL:
103fa797966STakashi Iwai 		if (hz)
104fa797966STakashi Iwai 			hz = 1000;
105c0dbbdadSGustavo A. R. Silva 		fallthrough;
106fa797966STakashi Iwai 	case SND_TONE:
107fa797966STakashi Iwai 		if (beep->linear_tone)
108fa797966STakashi Iwai 			beep->tone = beep_linear_tone(beep, hz);
109fa797966STakashi Iwai 		else
110fa797966STakashi Iwai 			beep->tone = beep_standard_tone(beep, hz);
1111cd2224cSMatthew Ranostay 		break;
1121cd2224cSMatthew Ranostay 	default:
1131cd2224cSMatthew Ranostay 		return -1;
1141cd2224cSMatthew Ranostay 	}
1151cd2224cSMatthew Ranostay 
1161cd2224cSMatthew Ranostay 	/* schedule beep event */
1171cd2224cSMatthew Ranostay 	schedule_work(&beep->beep_work);
1181cd2224cSMatthew Ranostay 	return 0;
1191cd2224cSMatthew Ranostay }
1201cd2224cSMatthew Ranostay 
turn_on_beep(struct hda_beep * beep)121*4c8d695cSTakashi Iwai static void turn_on_beep(struct hda_beep *beep)
122*4c8d695cSTakashi Iwai {
123*4c8d695cSTakashi Iwai 	if (beep->keep_power_at_enable)
124*4c8d695cSTakashi Iwai 		snd_hda_power_up_pm(beep->codec);
125*4c8d695cSTakashi Iwai }
126*4c8d695cSTakashi Iwai 
turn_off_beep(struct hda_beep * beep)127e914b25eSTakashi Iwai static void turn_off_beep(struct hda_beep *beep)
128e914b25eSTakashi Iwai {
129e914b25eSTakashi Iwai 	cancel_work_sync(&beep->beep_work);
130e914b25eSTakashi Iwai 	if (beep->playing) {
131e914b25eSTakashi Iwai 		/* turn off beep */
1325ccf835cSTakashi Iwai 		generate_tone(beep, 0);
133e914b25eSTakashi Iwai 	}
134*4c8d695cSTakashi Iwai 	if (beep->keep_power_at_enable)
135*4c8d695cSTakashi Iwai 		snd_hda_power_down_pm(beep->codec);
136e914b25eSTakashi Iwai }
137e914b25eSTakashi Iwai 
13895a962c3STakashi Iwai /**
13995a962c3STakashi Iwai  * snd_hda_enable_beep_device - Turn on/off beep sound
14095a962c3STakashi Iwai  * @codec: the HDA codec
14195a962c3STakashi Iwai  * @enable: flag to turn on/off
14295a962c3STakashi Iwai  */
snd_hda_enable_beep_device(struct hda_codec * codec,int enable)143123c07aeSJaroslav Kysela int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
144123c07aeSJaroslav Kysela {
145123c07aeSJaroslav Kysela 	struct hda_beep *beep = codec->beep;
1463fd877d3STakashi Iwai 	if (!beep)
14713dab080SJaroslav Kysela 		return 0;
1483fd877d3STakashi Iwai 	enable = !!enable;
14913dab080SJaroslav Kysela 	if (beep->enabled != enable) {
15013dab080SJaroslav Kysela 		beep->enabled = enable;
151*4c8d695cSTakashi Iwai 		if (enable)
152*4c8d695cSTakashi Iwai 			turn_on_beep(beep);
153*4c8d695cSTakashi Iwai 		else
154e914b25eSTakashi Iwai 			turn_off_beep(beep);
155123c07aeSJaroslav Kysela 		return 1;
156123c07aeSJaroslav Kysela 	}
157123c07aeSJaroslav Kysela 	return 0;
158123c07aeSJaroslav Kysela }
1592698ea98STakashi Iwai EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device);
160123c07aeSJaroslav Kysela 
beep_dev_register(struct snd_device * device)16145571bb8STakashi Iwai static int beep_dev_register(struct snd_device *device)
16245571bb8STakashi Iwai {
16345571bb8STakashi Iwai 	struct hda_beep *beep = device->device_data;
16445571bb8STakashi Iwai 	int err;
16545571bb8STakashi Iwai 
16645571bb8STakashi Iwai 	err = input_register_device(beep->dev);
16745571bb8STakashi Iwai 	if (!err)
16845571bb8STakashi Iwai 		beep->registered = true;
16945571bb8STakashi Iwai 	return err;
17045571bb8STakashi Iwai }
17145571bb8STakashi Iwai 
beep_dev_disconnect(struct snd_device * device)17245571bb8STakashi Iwai static int beep_dev_disconnect(struct snd_device *device)
17345571bb8STakashi Iwai {
17445571bb8STakashi Iwai 	struct hda_beep *beep = device->device_data;
17545571bb8STakashi Iwai 
17645571bb8STakashi Iwai 	if (beep->registered)
17745571bb8STakashi Iwai 		input_unregister_device(beep->dev);
17845571bb8STakashi Iwai 	else
17945571bb8STakashi Iwai 		input_free_device(beep->dev);
180*4c8d695cSTakashi Iwai 	if (beep->enabled)
18145571bb8STakashi Iwai 		turn_off_beep(beep);
18245571bb8STakashi Iwai 	return 0;
18345571bb8STakashi Iwai }
18445571bb8STakashi Iwai 
beep_dev_free(struct snd_device * device)18545571bb8STakashi Iwai static int beep_dev_free(struct snd_device *device)
18645571bb8STakashi Iwai {
18745571bb8STakashi Iwai 	struct hda_beep *beep = device->device_data;
18845571bb8STakashi Iwai 
18945571bb8STakashi Iwai 	beep->codec->beep = NULL;
19045571bb8STakashi Iwai 	kfree(beep);
19145571bb8STakashi Iwai 	return 0;
19245571bb8STakashi Iwai }
19345571bb8STakashi Iwai 
19495a962c3STakashi Iwai /**
19595a962c3STakashi Iwai  * snd_hda_attach_beep_device - Attach a beep input device
19695a962c3STakashi Iwai  * @codec: the HDA codec
19795a962c3STakashi Iwai  * @nid: beep NID
19895a962c3STakashi Iwai  *
19995a962c3STakashi Iwai  * Attach a beep object to the given widget.  If beep hint is turned off
20095a962c3STakashi Iwai  * explicitly or beep_mode of the codec is turned off, this doesn't nothing.
20195a962c3STakashi Iwai  *
20295a962c3STakashi Iwai  * Currently, only one beep device is allowed to each codec.
20395a962c3STakashi Iwai  */
snd_hda_attach_beep_device(struct hda_codec * codec,int nid)204123c07aeSJaroslav Kysela int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
205123c07aeSJaroslav Kysela {
20641f394a8STakashi Iwai 	static const struct snd_device_ops ops = {
20745571bb8STakashi Iwai 		.dev_register = beep_dev_register,
20845571bb8STakashi Iwai 		.dev_disconnect = beep_dev_disconnect,
20945571bb8STakashi Iwai 		.dev_free = beep_dev_free,
21045571bb8STakashi Iwai 	};
21145571bb8STakashi Iwai 	struct input_dev *input_dev;
212123c07aeSJaroslav Kysela 	struct hda_beep *beep;
213257dfb41STakashi Iwai 	int err;
214123c07aeSJaroslav Kysela 
215123c07aeSJaroslav Kysela 	if (!snd_hda_get_bool_hint(codec, "beep"))
2169bb1fe39STakashi Iwai 		return 0; /* disabled explicitly by hints */
2179bb1fe39STakashi Iwai 	if (codec->beep_mode == HDA_BEEP_MODE_OFF)
2189bb1fe39STakashi Iwai 		return 0; /* disabled by module option */
219123c07aeSJaroslav Kysela 
220123c07aeSJaroslav Kysela 	beep = kzalloc(sizeof(*beep), GFP_KERNEL);
221123c07aeSJaroslav Kysela 	if (beep == NULL)
222123c07aeSJaroslav Kysela 		return -ENOMEM;
223123c07aeSJaroslav Kysela 	snprintf(beep->phys, sizeof(beep->phys),
2246efdd851STakashi Iwai 		"card%d/codec#%d/beep0", codec->card->number, codec->addr);
2251cd2224cSMatthew Ranostay 	/* enable linear scale */
2268bc0a846STakashi Iwai 	snd_hda_codec_write_cache(codec, nid, 0,
2271cd2224cSMatthew Ranostay 		AC_VERB_SET_DIGI_CONVERT_2, 0x01);
2281cd2224cSMatthew Ranostay 
2291cd2224cSMatthew Ranostay 	beep->nid = nid;
2301cd2224cSMatthew Ranostay 	beep->codec = codec;
2311cd2224cSMatthew Ranostay 	codec->beep = beep;
2321cd2224cSMatthew Ranostay 
2331cd2224cSMatthew Ranostay 	INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
234123c07aeSJaroslav Kysela 
23545571bb8STakashi Iwai 	input_dev = input_allocate_device();
23645571bb8STakashi Iwai 	if (!input_dev) {
23745571bb8STakashi Iwai 		err = -ENOMEM;
23845571bb8STakashi Iwai 		goto err_free;
23945571bb8STakashi Iwai 	}
24045571bb8STakashi Iwai 
24145571bb8STakashi Iwai 	/* setup digital beep device */
24245571bb8STakashi Iwai 	input_dev->name = "HDA Digital PCBeep";
24345571bb8STakashi Iwai 	input_dev->phys = beep->phys;
24445571bb8STakashi Iwai 	input_dev->id.bustype = BUS_PCI;
24545571bb8STakashi Iwai 	input_dev->dev.parent = &codec->card->card_dev;
24645571bb8STakashi Iwai 
24745571bb8STakashi Iwai 	input_dev->id.vendor = codec->core.vendor_id >> 16;
24845571bb8STakashi Iwai 	input_dev->id.product = codec->core.vendor_id & 0xffff;
24945571bb8STakashi Iwai 	input_dev->id.version = 0x01;
25045571bb8STakashi Iwai 
25145571bb8STakashi Iwai 	input_dev->evbit[0] = BIT_MASK(EV_SND);
25245571bb8STakashi Iwai 	input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
25345571bb8STakashi Iwai 	input_dev->event = snd_hda_beep_event;
25445571bb8STakashi Iwai 	input_set_drvdata(input_dev, beep);
25545571bb8STakashi Iwai 
25645571bb8STakashi Iwai 	beep->dev = input_dev;
25745571bb8STakashi Iwai 
25845571bb8STakashi Iwai 	err = snd_device_new(codec->card, SNDRV_DEV_JACK, beep, &ops);
25945571bb8STakashi Iwai 	if (err < 0)
26045571bb8STakashi Iwai 		goto err_input;
26145571bb8STakashi Iwai 
26245571bb8STakashi Iwai 	return 0;
26345571bb8STakashi Iwai 
26445571bb8STakashi Iwai  err_input:
26545571bb8STakashi Iwai 	input_free_device(beep->dev);
26645571bb8STakashi Iwai  err_free:
26754f7190bSTakashi Iwai 	kfree(beep);
26854f7190bSTakashi Iwai 	codec->beep = NULL;
26954f7190bSTakashi Iwai 	return err;
27054f7190bSTakashi Iwai }
2712698ea98STakashi Iwai EXPORT_SYMBOL_GPL(snd_hda_attach_beep_device);
2721cd2224cSMatthew Ranostay 
27395a962c3STakashi Iwai /**
27495a962c3STakashi Iwai  * snd_hda_detach_beep_device - Detach the beep device
27595a962c3STakashi Iwai  * @codec: the HDA codec
27695a962c3STakashi Iwai  */
snd_hda_detach_beep_device(struct hda_codec * codec)2771cd2224cSMatthew Ranostay void snd_hda_detach_beep_device(struct hda_codec *codec)
2781cd2224cSMatthew Ranostay {
27945571bb8STakashi Iwai 	if (!codec->bus->shutdown && codec->beep)
28045571bb8STakashi Iwai 		snd_device_free(codec->card, codec->beep);
2811cd2224cSMatthew Ranostay }
2822698ea98STakashi Iwai EXPORT_SYMBOL_GPL(snd_hda_detach_beep_device);
2830401e854STakashi Iwai 
ctl_has_mute(struct snd_kcontrol * kcontrol)284265d931aSDavid Henningsson static bool ctl_has_mute(struct snd_kcontrol *kcontrol)
285265d931aSDavid Henningsson {
286265d931aSDavid Henningsson 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
287265d931aSDavid Henningsson 	return query_amp_caps(codec, get_amp_nid(kcontrol),
288265d931aSDavid Henningsson 			      get_amp_direction(kcontrol)) & AC_AMPCAP_MUTE;
289265d931aSDavid Henningsson }
290265d931aSDavid Henningsson 
2910401e854STakashi Iwai /* get/put callbacks for beep mute mixer switches */
29295a962c3STakashi Iwai 
29395a962c3STakashi Iwai /**
29495a962c3STakashi Iwai  * snd_hda_mixer_amp_switch_get_beep - Get callback for beep controls
29595a962c3STakashi Iwai  * @kcontrol: ctl element
29695a962c3STakashi Iwai  * @ucontrol: pointer to get/store the data
29795a962c3STakashi Iwai  */
snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2980401e854STakashi Iwai int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
2990401e854STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
3000401e854STakashi Iwai {
3010401e854STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3020401e854STakashi Iwai 	struct hda_beep *beep = codec->beep;
3030ad3f0b3STakashi Iwai 	int chs = get_amp_channels(kcontrol);
3040ad3f0b3STakashi Iwai 
305265d931aSDavid Henningsson 	if (beep && (!beep->enabled || !ctl_has_mute(kcontrol))) {
3060ad3f0b3STakashi Iwai 		if (chs & 1)
3070ad3f0b3STakashi Iwai 			ucontrol->value.integer.value[0] = beep->enabled;
3080ad3f0b3STakashi Iwai 		if (chs & 2)
309265d931aSDavid Henningsson 			ucontrol->value.integer.value[1] = beep->enabled;
3100401e854STakashi Iwai 		return 0;
3110401e854STakashi Iwai 	}
3120401e854STakashi Iwai 	return snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
3130401e854STakashi Iwai }
3142698ea98STakashi Iwai EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get_beep);
3150401e854STakashi Iwai 
31695a962c3STakashi Iwai /**
31795a962c3STakashi Iwai  * snd_hda_mixer_amp_switch_put_beep - Put callback for beep controls
31895a962c3STakashi Iwai  * @kcontrol: ctl element
31995a962c3STakashi Iwai  * @ucontrol: pointer to get/store the data
32095a962c3STakashi Iwai  */
snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)3210401e854STakashi Iwai int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
3220401e854STakashi Iwai 				      struct snd_ctl_elem_value *ucontrol)
3230401e854STakashi Iwai {
3240401e854STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3250401e854STakashi Iwai 	struct hda_beep *beep = codec->beep;
32614bc9c6dSDavid Henningsson 	if (beep) {
32714bc9c6dSDavid Henningsson 		u8 chs = get_amp_channels(kcontrol);
32814bc9c6dSDavid Henningsson 		int enable = 0;
32914bc9c6dSDavid Henningsson 		long *valp = ucontrol->value.integer.value;
33014bc9c6dSDavid Henningsson 		if (chs & 1) {
33114bc9c6dSDavid Henningsson 			enable |= *valp;
33214bc9c6dSDavid Henningsson 			valp++;
33314bc9c6dSDavid Henningsson 		}
33414bc9c6dSDavid Henningsson 		if (chs & 2)
33514bc9c6dSDavid Henningsson 			enable |= *valp;
33614bc9c6dSDavid Henningsson 		snd_hda_enable_beep_device(codec, enable);
33714bc9c6dSDavid Henningsson 	}
338265d931aSDavid Henningsson 	if (!ctl_has_mute(kcontrol))
339265d931aSDavid Henningsson 		return 0;
3400401e854STakashi Iwai 	return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
3410401e854STakashi Iwai }
3422698ea98STakashi Iwai EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put_beep);
343