xref: /OK3568_Linux_fs/kernel/sound/pci/hda/hda_beep.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Digital Beep Input Interface for HD-audio codec
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Author: Matt Ranostay <matt.ranostay@konsulko.com>
6*4882a593Smuzhiyun  * Copyright (c) 2008 Embedded Alley Solutions Inc
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <linux/input.h>
10*4882a593Smuzhiyun #include <linux/slab.h>
11*4882a593Smuzhiyun #include <linux/workqueue.h>
12*4882a593Smuzhiyun #include <linux/export.h>
13*4882a593Smuzhiyun #include <sound/core.h>
14*4882a593Smuzhiyun #include "hda_beep.h"
15*4882a593Smuzhiyun #include "hda_local.h"
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun enum {
18*4882a593Smuzhiyun 	DIGBEEP_HZ_STEP = 46875,	/* 46.875 Hz */
19*4882a593Smuzhiyun 	DIGBEEP_HZ_MIN = 93750,		/* 93.750 Hz */
20*4882a593Smuzhiyun 	DIGBEEP_HZ_MAX = 12000000,	/* 12 KHz */
21*4882a593Smuzhiyun };
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun /* generate or stop tone */
generate_tone(struct hda_beep * beep,int tone)24*4882a593Smuzhiyun static void generate_tone(struct hda_beep *beep, int tone)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun 	struct hda_codec *codec = beep->codec;
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun 	if (tone && !beep->playing) {
29*4882a593Smuzhiyun 		snd_hda_power_up(codec);
30*4882a593Smuzhiyun 		if (beep->power_hook)
31*4882a593Smuzhiyun 			beep->power_hook(beep, true);
32*4882a593Smuzhiyun 		beep->playing = 1;
33*4882a593Smuzhiyun 	}
34*4882a593Smuzhiyun 	snd_hda_codec_write(codec, beep->nid, 0,
35*4882a593Smuzhiyun 			    AC_VERB_SET_BEEP_CONTROL, tone);
36*4882a593Smuzhiyun 	if (!tone && beep->playing) {
37*4882a593Smuzhiyun 		beep->playing = 0;
38*4882a593Smuzhiyun 		if (beep->power_hook)
39*4882a593Smuzhiyun 			beep->power_hook(beep, false);
40*4882a593Smuzhiyun 		snd_hda_power_down(codec);
41*4882a593Smuzhiyun 	}
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun 
snd_hda_generate_beep(struct work_struct * work)44*4882a593Smuzhiyun static void snd_hda_generate_beep(struct work_struct *work)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun 	struct hda_beep *beep =
47*4882a593Smuzhiyun 		container_of(work, struct hda_beep, beep_work);
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	if (beep->enabled)
50*4882a593Smuzhiyun 		generate_tone(beep, beep->tone);
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun /* (non-standard) Linear beep tone calculation for IDT/STAC codecs
54*4882a593Smuzhiyun  *
55*4882a593Smuzhiyun  * The tone frequency of beep generator on IDT/STAC codecs is
56*4882a593Smuzhiyun  * defined from the 8bit tone parameter, in Hz,
57*4882a593Smuzhiyun  *    freq = 48000 * (257 - tone) / 1024
58*4882a593Smuzhiyun  * that is from 12kHz to 93.75Hz in steps of 46.875 Hz
59*4882a593Smuzhiyun  */
beep_linear_tone(struct hda_beep * beep,int hz)60*4882a593Smuzhiyun static int beep_linear_tone(struct hda_beep *beep, int hz)
61*4882a593Smuzhiyun {
62*4882a593Smuzhiyun 	if (hz <= 0)
63*4882a593Smuzhiyun 		return 0;
64*4882a593Smuzhiyun 	hz *= 1000; /* fixed point */
65*4882a593Smuzhiyun 	hz = hz - DIGBEEP_HZ_MIN
66*4882a593Smuzhiyun 		+ DIGBEEP_HZ_STEP / 2; /* round to nearest step */
67*4882a593Smuzhiyun 	if (hz < 0)
68*4882a593Smuzhiyun 		hz = 0; /* turn off PC beep*/
69*4882a593Smuzhiyun 	else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
70*4882a593Smuzhiyun 		hz = 1; /* max frequency */
71*4882a593Smuzhiyun 	else {
72*4882a593Smuzhiyun 		hz /= DIGBEEP_HZ_STEP;
73*4882a593Smuzhiyun 		hz = 255 - hz;
74*4882a593Smuzhiyun 	}
75*4882a593Smuzhiyun 	return hz;
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun /* HD-audio standard beep tone parameter calculation
79*4882a593Smuzhiyun  *
80*4882a593Smuzhiyun  * The tone frequency in Hz is calculated as
81*4882a593Smuzhiyun  *   freq = 48000 / (tone * 4)
82*4882a593Smuzhiyun  * from 47Hz to 12kHz
83*4882a593Smuzhiyun  */
beep_standard_tone(struct hda_beep * beep,int hz)84*4882a593Smuzhiyun static int beep_standard_tone(struct hda_beep *beep, int hz)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun 	if (hz <= 0)
87*4882a593Smuzhiyun 		return 0; /* disabled */
88*4882a593Smuzhiyun 	hz = 12000 / hz;
89*4882a593Smuzhiyun 	if (hz > 0xff)
90*4882a593Smuzhiyun 		return 0xff;
91*4882a593Smuzhiyun 	if (hz <= 0)
92*4882a593Smuzhiyun 		return 1;
93*4882a593Smuzhiyun 	return hz;
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun 
snd_hda_beep_event(struct input_dev * dev,unsigned int type,unsigned int code,int hz)96*4882a593Smuzhiyun static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
97*4882a593Smuzhiyun 				unsigned int code, int hz)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun 	struct hda_beep *beep = input_get_drvdata(dev);
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	switch (code) {
102*4882a593Smuzhiyun 	case SND_BELL:
103*4882a593Smuzhiyun 		if (hz)
104*4882a593Smuzhiyun 			hz = 1000;
105*4882a593Smuzhiyun 		fallthrough;
106*4882a593Smuzhiyun 	case SND_TONE:
107*4882a593Smuzhiyun 		if (beep->linear_tone)
108*4882a593Smuzhiyun 			beep->tone = beep_linear_tone(beep, hz);
109*4882a593Smuzhiyun 		else
110*4882a593Smuzhiyun 			beep->tone = beep_standard_tone(beep, hz);
111*4882a593Smuzhiyun 		break;
112*4882a593Smuzhiyun 	default:
113*4882a593Smuzhiyun 		return -1;
114*4882a593Smuzhiyun 	}
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	/* schedule beep event */
117*4882a593Smuzhiyun 	schedule_work(&beep->beep_work);
118*4882a593Smuzhiyun 	return 0;
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun 
turn_on_beep(struct hda_beep * beep)121*4882a593Smuzhiyun static void turn_on_beep(struct hda_beep *beep)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun 	if (beep->keep_power_at_enable)
124*4882a593Smuzhiyun 		snd_hda_power_up_pm(beep->codec);
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun 
turn_off_beep(struct hda_beep * beep)127*4882a593Smuzhiyun static void turn_off_beep(struct hda_beep *beep)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun 	cancel_work_sync(&beep->beep_work);
130*4882a593Smuzhiyun 	if (beep->playing) {
131*4882a593Smuzhiyun 		/* turn off beep */
132*4882a593Smuzhiyun 		generate_tone(beep, 0);
133*4882a593Smuzhiyun 	}
134*4882a593Smuzhiyun 	if (beep->keep_power_at_enable)
135*4882a593Smuzhiyun 		snd_hda_power_down_pm(beep->codec);
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun /**
139*4882a593Smuzhiyun  * snd_hda_enable_beep_device - Turn on/off beep sound
140*4882a593Smuzhiyun  * @codec: the HDA codec
141*4882a593Smuzhiyun  * @enable: flag to turn on/off
142*4882a593Smuzhiyun  */
snd_hda_enable_beep_device(struct hda_codec * codec,int enable)143*4882a593Smuzhiyun int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
144*4882a593Smuzhiyun {
145*4882a593Smuzhiyun 	struct hda_beep *beep = codec->beep;
146*4882a593Smuzhiyun 	if (!beep)
147*4882a593Smuzhiyun 		return 0;
148*4882a593Smuzhiyun 	enable = !!enable;
149*4882a593Smuzhiyun 	if (beep->enabled != enable) {
150*4882a593Smuzhiyun 		beep->enabled = enable;
151*4882a593Smuzhiyun 		if (enable)
152*4882a593Smuzhiyun 			turn_on_beep(beep);
153*4882a593Smuzhiyun 		else
154*4882a593Smuzhiyun 			turn_off_beep(beep);
155*4882a593Smuzhiyun 		return 1;
156*4882a593Smuzhiyun 	}
157*4882a593Smuzhiyun 	return 0;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device);
160*4882a593Smuzhiyun 
beep_dev_register(struct snd_device * device)161*4882a593Smuzhiyun static int beep_dev_register(struct snd_device *device)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun 	struct hda_beep *beep = device->device_data;
164*4882a593Smuzhiyun 	int err;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	err = input_register_device(beep->dev);
167*4882a593Smuzhiyun 	if (!err)
168*4882a593Smuzhiyun 		beep->registered = true;
169*4882a593Smuzhiyun 	return err;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun 
beep_dev_disconnect(struct snd_device * device)172*4882a593Smuzhiyun static int beep_dev_disconnect(struct snd_device *device)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun 	struct hda_beep *beep = device->device_data;
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	if (beep->registered)
177*4882a593Smuzhiyun 		input_unregister_device(beep->dev);
178*4882a593Smuzhiyun 	else
179*4882a593Smuzhiyun 		input_free_device(beep->dev);
180*4882a593Smuzhiyun 	if (beep->enabled)
181*4882a593Smuzhiyun 		turn_off_beep(beep);
182*4882a593Smuzhiyun 	return 0;
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun 
beep_dev_free(struct snd_device * device)185*4882a593Smuzhiyun static int beep_dev_free(struct snd_device *device)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun 	struct hda_beep *beep = device->device_data;
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	beep->codec->beep = NULL;
190*4882a593Smuzhiyun 	kfree(beep);
191*4882a593Smuzhiyun 	return 0;
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun /**
195*4882a593Smuzhiyun  * snd_hda_attach_beep_device - Attach a beep input device
196*4882a593Smuzhiyun  * @codec: the HDA codec
197*4882a593Smuzhiyun  * @nid: beep NID
198*4882a593Smuzhiyun  *
199*4882a593Smuzhiyun  * Attach a beep object to the given widget.  If beep hint is turned off
200*4882a593Smuzhiyun  * explicitly or beep_mode of the codec is turned off, this doesn't nothing.
201*4882a593Smuzhiyun  *
202*4882a593Smuzhiyun  * Currently, only one beep device is allowed to each codec.
203*4882a593Smuzhiyun  */
snd_hda_attach_beep_device(struct hda_codec * codec,int nid)204*4882a593Smuzhiyun int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
205*4882a593Smuzhiyun {
206*4882a593Smuzhiyun 	static const struct snd_device_ops ops = {
207*4882a593Smuzhiyun 		.dev_register = beep_dev_register,
208*4882a593Smuzhiyun 		.dev_disconnect = beep_dev_disconnect,
209*4882a593Smuzhiyun 		.dev_free = beep_dev_free,
210*4882a593Smuzhiyun 	};
211*4882a593Smuzhiyun 	struct input_dev *input_dev;
212*4882a593Smuzhiyun 	struct hda_beep *beep;
213*4882a593Smuzhiyun 	int err;
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	if (!snd_hda_get_bool_hint(codec, "beep"))
216*4882a593Smuzhiyun 		return 0; /* disabled explicitly by hints */
217*4882a593Smuzhiyun 	if (codec->beep_mode == HDA_BEEP_MODE_OFF)
218*4882a593Smuzhiyun 		return 0; /* disabled by module option */
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	beep = kzalloc(sizeof(*beep), GFP_KERNEL);
221*4882a593Smuzhiyun 	if (beep == NULL)
222*4882a593Smuzhiyun 		return -ENOMEM;
223*4882a593Smuzhiyun 	snprintf(beep->phys, sizeof(beep->phys),
224*4882a593Smuzhiyun 		"card%d/codec#%d/beep0", codec->card->number, codec->addr);
225*4882a593Smuzhiyun 	/* enable linear scale */
226*4882a593Smuzhiyun 	snd_hda_codec_write_cache(codec, nid, 0,
227*4882a593Smuzhiyun 		AC_VERB_SET_DIGI_CONVERT_2, 0x01);
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	beep->nid = nid;
230*4882a593Smuzhiyun 	beep->codec = codec;
231*4882a593Smuzhiyun 	codec->beep = beep;
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
234*4882a593Smuzhiyun 	mutex_init(&beep->mutex);
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	input_dev = input_allocate_device();
237*4882a593Smuzhiyun 	if (!input_dev) {
238*4882a593Smuzhiyun 		err = -ENOMEM;
239*4882a593Smuzhiyun 		goto err_free;
240*4882a593Smuzhiyun 	}
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	/* setup digital beep device */
243*4882a593Smuzhiyun 	input_dev->name = "HDA Digital PCBeep";
244*4882a593Smuzhiyun 	input_dev->phys = beep->phys;
245*4882a593Smuzhiyun 	input_dev->id.bustype = BUS_PCI;
246*4882a593Smuzhiyun 	input_dev->dev.parent = &codec->card->card_dev;
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	input_dev->id.vendor = codec->core.vendor_id >> 16;
249*4882a593Smuzhiyun 	input_dev->id.product = codec->core.vendor_id & 0xffff;
250*4882a593Smuzhiyun 	input_dev->id.version = 0x01;
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	input_dev->evbit[0] = BIT_MASK(EV_SND);
253*4882a593Smuzhiyun 	input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
254*4882a593Smuzhiyun 	input_dev->event = snd_hda_beep_event;
255*4882a593Smuzhiyun 	input_set_drvdata(input_dev, beep);
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	beep->dev = input_dev;
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	err = snd_device_new(codec->card, SNDRV_DEV_JACK, beep, &ops);
260*4882a593Smuzhiyun 	if (err < 0)
261*4882a593Smuzhiyun 		goto err_input;
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	return 0;
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun  err_input:
266*4882a593Smuzhiyun 	input_free_device(beep->dev);
267*4882a593Smuzhiyun  err_free:
268*4882a593Smuzhiyun 	kfree(beep);
269*4882a593Smuzhiyun 	codec->beep = NULL;
270*4882a593Smuzhiyun 	return err;
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(snd_hda_attach_beep_device);
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun /**
275*4882a593Smuzhiyun  * snd_hda_detach_beep_device - Detach the beep device
276*4882a593Smuzhiyun  * @codec: the HDA codec
277*4882a593Smuzhiyun  */
snd_hda_detach_beep_device(struct hda_codec * codec)278*4882a593Smuzhiyun void snd_hda_detach_beep_device(struct hda_codec *codec)
279*4882a593Smuzhiyun {
280*4882a593Smuzhiyun 	if (!codec->bus->shutdown && codec->beep)
281*4882a593Smuzhiyun 		snd_device_free(codec->card, codec->beep);
282*4882a593Smuzhiyun }
283*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(snd_hda_detach_beep_device);
284*4882a593Smuzhiyun 
ctl_has_mute(struct snd_kcontrol * kcontrol)285*4882a593Smuzhiyun static bool ctl_has_mute(struct snd_kcontrol *kcontrol)
286*4882a593Smuzhiyun {
287*4882a593Smuzhiyun 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
288*4882a593Smuzhiyun 	return query_amp_caps(codec, get_amp_nid(kcontrol),
289*4882a593Smuzhiyun 			      get_amp_direction(kcontrol)) & AC_AMPCAP_MUTE;
290*4882a593Smuzhiyun }
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun /* get/put callbacks for beep mute mixer switches */
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun /**
295*4882a593Smuzhiyun  * snd_hda_mixer_amp_switch_get_beep - Get callback for beep controls
296*4882a593Smuzhiyun  * @kcontrol: ctl element
297*4882a593Smuzhiyun  * @ucontrol: pointer to get/store the data
298*4882a593Smuzhiyun  */
snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)299*4882a593Smuzhiyun int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
300*4882a593Smuzhiyun 				      struct snd_ctl_elem_value *ucontrol)
301*4882a593Smuzhiyun {
302*4882a593Smuzhiyun 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
303*4882a593Smuzhiyun 	struct hda_beep *beep = codec->beep;
304*4882a593Smuzhiyun 	int chs = get_amp_channels(kcontrol);
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	if (beep && (!beep->enabled || !ctl_has_mute(kcontrol))) {
307*4882a593Smuzhiyun 		if (chs & 1)
308*4882a593Smuzhiyun 			ucontrol->value.integer.value[0] = beep->enabled;
309*4882a593Smuzhiyun 		if (chs & 2)
310*4882a593Smuzhiyun 			ucontrol->value.integer.value[1] = beep->enabled;
311*4882a593Smuzhiyun 		return 0;
312*4882a593Smuzhiyun 	}
313*4882a593Smuzhiyun 	return snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
314*4882a593Smuzhiyun }
315*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get_beep);
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun /**
318*4882a593Smuzhiyun  * snd_hda_mixer_amp_switch_put_beep - Put callback for beep controls
319*4882a593Smuzhiyun  * @kcontrol: ctl element
320*4882a593Smuzhiyun  * @ucontrol: pointer to get/store the data
321*4882a593Smuzhiyun  */
snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)322*4882a593Smuzhiyun int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
323*4882a593Smuzhiyun 				      struct snd_ctl_elem_value *ucontrol)
324*4882a593Smuzhiyun {
325*4882a593Smuzhiyun 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
326*4882a593Smuzhiyun 	struct hda_beep *beep = codec->beep;
327*4882a593Smuzhiyun 	if (beep) {
328*4882a593Smuzhiyun 		u8 chs = get_amp_channels(kcontrol);
329*4882a593Smuzhiyun 		int enable = 0;
330*4882a593Smuzhiyun 		long *valp = ucontrol->value.integer.value;
331*4882a593Smuzhiyun 		if (chs & 1) {
332*4882a593Smuzhiyun 			enable |= *valp;
333*4882a593Smuzhiyun 			valp++;
334*4882a593Smuzhiyun 		}
335*4882a593Smuzhiyun 		if (chs & 2)
336*4882a593Smuzhiyun 			enable |= *valp;
337*4882a593Smuzhiyun 		snd_hda_enable_beep_device(codec, enable);
338*4882a593Smuzhiyun 	}
339*4882a593Smuzhiyun 	if (!ctl_has_mute(kcontrol))
340*4882a593Smuzhiyun 		return 0;
341*4882a593Smuzhiyun 	return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put_beep);
344