Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/pci/hda/hda_beep.c
10817 views
1
/*
2
* Digital Beep Input Interface for HD-audio codec
3
*
4
* Author: Matthew Ranostay <[email protected]>
5
* Copyright (c) 2008 Embedded Alley Solutions Inc
6
*
7
* This driver is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
11
*
12
* This driver is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
16
*
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
*/
21
22
#include <linux/input.h>
23
#include <linux/pci.h>
24
#include <linux/slab.h>
25
#include <linux/workqueue.h>
26
#include <sound/core.h>
27
#include "hda_beep.h"
28
#include "hda_local.h"
29
30
enum {
31
DIGBEEP_HZ_STEP = 46875, /* 46.875 Hz */
32
DIGBEEP_HZ_MIN = 93750, /* 93.750 Hz */
33
DIGBEEP_HZ_MAX = 12000000, /* 12 KHz */
34
};
35
36
static void snd_hda_generate_beep(struct work_struct *work)
37
{
38
struct hda_beep *beep =
39
container_of(work, struct hda_beep, beep_work);
40
struct hda_codec *codec = beep->codec;
41
42
if (!beep->enabled)
43
return;
44
45
/* generate tone */
46
snd_hda_codec_write(codec, beep->nid, 0,
47
AC_VERB_SET_BEEP_CONTROL, beep->tone);
48
}
49
50
/* (non-standard) Linear beep tone calculation for IDT/STAC codecs
51
*
52
* The tone frequency of beep generator on IDT/STAC codecs is
53
* defined from the 8bit tone parameter, in Hz,
54
* freq = 48000 * (257 - tone) / 1024
55
* that is from 12kHz to 93.75Hz in steps of 46.875 Hz
56
*/
57
static int beep_linear_tone(struct hda_beep *beep, int hz)
58
{
59
if (hz <= 0)
60
return 0;
61
hz *= 1000; /* fixed point */
62
hz = hz - DIGBEEP_HZ_MIN
63
+ DIGBEEP_HZ_STEP / 2; /* round to nearest step */
64
if (hz < 0)
65
hz = 0; /* turn off PC beep*/
66
else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
67
hz = 1; /* max frequency */
68
else {
69
hz /= DIGBEEP_HZ_STEP;
70
hz = 255 - hz;
71
}
72
return hz;
73
}
74
75
/* HD-audio standard beep tone parameter calculation
76
*
77
* The tone frequency in Hz is calculated as
78
* freq = 48000 / (tone * 4)
79
* from 47Hz to 12kHz
80
*/
81
static int beep_standard_tone(struct hda_beep *beep, int hz)
82
{
83
if (hz <= 0)
84
return 0; /* disabled */
85
hz = 12000 / hz;
86
if (hz > 0xff)
87
return 0xff;
88
if (hz <= 0)
89
return 1;
90
return hz;
91
}
92
93
static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
94
unsigned int code, int hz)
95
{
96
struct hda_beep *beep = input_get_drvdata(dev);
97
98
switch (code) {
99
case SND_BELL:
100
if (hz)
101
hz = 1000;
102
case SND_TONE:
103
if (beep->linear_tone)
104
beep->tone = beep_linear_tone(beep, hz);
105
else
106
beep->tone = beep_standard_tone(beep, hz);
107
break;
108
default:
109
return -1;
110
}
111
112
/* schedule beep event */
113
schedule_work(&beep->beep_work);
114
return 0;
115
}
116
117
static void snd_hda_do_detach(struct hda_beep *beep)
118
{
119
input_unregister_device(beep->dev);
120
beep->dev = NULL;
121
cancel_work_sync(&beep->beep_work);
122
/* turn off beep for sure */
123
snd_hda_codec_write(beep->codec, beep->nid, 0,
124
AC_VERB_SET_BEEP_CONTROL, 0);
125
}
126
127
static int snd_hda_do_attach(struct hda_beep *beep)
128
{
129
struct input_dev *input_dev;
130
struct hda_codec *codec = beep->codec;
131
int err;
132
133
input_dev = input_allocate_device();
134
if (!input_dev) {
135
printk(KERN_INFO "hda_beep: unable to allocate input device\n");
136
return -ENOMEM;
137
}
138
139
/* setup digital beep device */
140
input_dev->name = "HDA Digital PCBeep";
141
input_dev->phys = beep->phys;
142
input_dev->id.bustype = BUS_PCI;
143
144
input_dev->id.vendor = codec->vendor_id >> 16;
145
input_dev->id.product = codec->vendor_id & 0xffff;
146
input_dev->id.version = 0x01;
147
148
input_dev->evbit[0] = BIT_MASK(EV_SND);
149
input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
150
input_dev->event = snd_hda_beep_event;
151
input_dev->dev.parent = &codec->bus->pci->dev;
152
input_set_drvdata(input_dev, beep);
153
154
err = input_register_device(input_dev);
155
if (err < 0) {
156
input_free_device(input_dev);
157
printk(KERN_INFO "hda_beep: unable to register input device\n");
158
return err;
159
}
160
beep->dev = input_dev;
161
return 0;
162
}
163
164
static void snd_hda_do_register(struct work_struct *work)
165
{
166
struct hda_beep *beep =
167
container_of(work, struct hda_beep, register_work);
168
169
mutex_lock(&beep->mutex);
170
if (beep->enabled && !beep->dev)
171
snd_hda_do_attach(beep);
172
mutex_unlock(&beep->mutex);
173
}
174
175
static void snd_hda_do_unregister(struct work_struct *work)
176
{
177
struct hda_beep *beep =
178
container_of(work, struct hda_beep, unregister_work.work);
179
180
mutex_lock(&beep->mutex);
181
if (!beep->enabled && beep->dev)
182
snd_hda_do_detach(beep);
183
mutex_unlock(&beep->mutex);
184
}
185
186
int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
187
{
188
struct hda_beep *beep = codec->beep;
189
enable = !!enable;
190
if (beep == NULL)
191
return 0;
192
if (beep->enabled != enable) {
193
beep->enabled = enable;
194
if (!enable) {
195
/* turn off beep */
196
snd_hda_codec_write(beep->codec, beep->nid, 0,
197
AC_VERB_SET_BEEP_CONTROL, 0);
198
}
199
if (beep->mode == HDA_BEEP_MODE_SWREG) {
200
if (enable) {
201
cancel_delayed_work(&beep->unregister_work);
202
schedule_work(&beep->register_work);
203
} else {
204
schedule_delayed_work(&beep->unregister_work,
205
HZ);
206
}
207
}
208
return 1;
209
}
210
return 0;
211
}
212
EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device);
213
214
int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
215
{
216
struct hda_beep *beep;
217
218
if (!snd_hda_get_bool_hint(codec, "beep"))
219
return 0; /* disabled explicitly by hints */
220
if (codec->beep_mode == HDA_BEEP_MODE_OFF)
221
return 0; /* disabled by module option */
222
223
beep = kzalloc(sizeof(*beep), GFP_KERNEL);
224
if (beep == NULL)
225
return -ENOMEM;
226
snprintf(beep->phys, sizeof(beep->phys),
227
"card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
228
/* enable linear scale */
229
snd_hda_codec_write(codec, nid, 0,
230
AC_VERB_SET_DIGI_CONVERT_2, 0x01);
231
232
beep->nid = nid;
233
beep->codec = codec;
234
beep->mode = codec->beep_mode;
235
codec->beep = beep;
236
237
INIT_WORK(&beep->register_work, &snd_hda_do_register);
238
INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister);
239
INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
240
mutex_init(&beep->mutex);
241
242
if (beep->mode == HDA_BEEP_MODE_ON) {
243
int err = snd_hda_do_attach(beep);
244
if (err < 0) {
245
kfree(beep);
246
codec->beep = NULL;
247
return err;
248
}
249
}
250
251
return 0;
252
}
253
EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device);
254
255
void snd_hda_detach_beep_device(struct hda_codec *codec)
256
{
257
struct hda_beep *beep = codec->beep;
258
if (beep) {
259
cancel_work_sync(&beep->register_work);
260
cancel_delayed_work(&beep->unregister_work);
261
if (beep->dev)
262
snd_hda_do_detach(beep);
263
codec->beep = NULL;
264
kfree(beep);
265
}
266
}
267
EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
268
269