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