aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/hda_beep.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda/hda_beep.c')
-rw-r--r--sound/pci/hda/hda_beep.c114
1 files changed, 96 insertions, 18 deletions
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
index 3f51a981e604..5fe34a8d8c81 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -113,23 +113,25 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
113 return 0; 113 return 0;
114} 114}
115 115
116int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) 116static void snd_hda_do_detach(struct hda_beep *beep)
117{
118 input_unregister_device(beep->dev);
119 beep->dev = NULL;
120 cancel_work_sync(&beep->beep_work);
121 /* turn off beep for sure */
122 snd_hda_codec_write_cache(beep->codec, beep->nid, 0,
123 AC_VERB_SET_BEEP_CONTROL, 0);
124}
125
126static int snd_hda_do_attach(struct hda_beep *beep)
117{ 127{
118 struct input_dev *input_dev; 128 struct input_dev *input_dev;
119 struct hda_beep *beep; 129 struct hda_codec *codec = beep->codec;
120 int err; 130 int err;
121 131
122 if (!snd_hda_get_bool_hint(codec, "beep"))
123 return 0; /* disabled explicitly */
124
125 beep = kzalloc(sizeof(*beep), GFP_KERNEL);
126 if (beep == NULL)
127 return -ENOMEM;
128 snprintf(beep->phys, sizeof(beep->phys),
129 "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
130 input_dev = input_allocate_device(); 132 input_dev = input_allocate_device();
131 if (!input_dev) { 133 if (!input_dev) {
132 kfree(beep); 134 printk(KERN_INFO "hda_beep: unable to allocate input device\n");
133 return -ENOMEM; 135 return -ENOMEM;
134 } 136 }
135 137
@@ -151,21 +153,96 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
151 err = input_register_device(input_dev); 153 err = input_register_device(input_dev);
152 if (err < 0) { 154 if (err < 0) {
153 input_free_device(input_dev); 155 input_free_device(input_dev);
154 kfree(beep); 156 printk(KERN_INFO "hda_beep: unable to register input device\n");
155 return err; 157 return err;
156 } 158 }
159 beep->dev = input_dev;
160 return 0;
161}
162
163static void snd_hda_do_register(struct work_struct *work)
164{
165 struct hda_beep *beep =
166 container_of(work, struct hda_beep, register_work);
167
168 mutex_lock(&beep->mutex);
169 if (beep->enabled && !beep->dev)
170 snd_hda_do_attach(beep);
171 mutex_unlock(&beep->mutex);
172}
173
174static void snd_hda_do_unregister(struct work_struct *work)
175{
176 struct hda_beep *beep =
177 container_of(work, struct hda_beep, unregister_work.work);
178
179 mutex_lock(&beep->mutex);
180 if (!beep->enabled && beep->dev)
181 snd_hda_do_detach(beep);
182 mutex_unlock(&beep->mutex);
183}
157 184
185int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
186{
187 struct hda_beep *beep = codec->beep;
188 enable = !!enable;
189 if (beep == NULL)
190 return 0;
191 if (beep->enabled != enable) {
192 beep->enabled = enable;
193 if (!enable) {
194 /* turn off beep */
195 snd_hda_codec_write_cache(beep->codec, beep->nid, 0,
196 AC_VERB_SET_BEEP_CONTROL, 0);
197 }
198 if (beep->mode == HDA_BEEP_MODE_SWREG) {
199 if (enable) {
200 cancel_delayed_work(&beep->unregister_work);
201 schedule_work(&beep->register_work);
202 } else {
203 schedule_delayed_work(&beep->unregister_work,
204 HZ);
205 }
206 }
207 return 1;
208 }
209 return 0;
210}
211EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device);
212
213int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
214{
215 struct hda_beep *beep;
216
217 if (!snd_hda_get_bool_hint(codec, "beep"))
218 return 0; /* disabled explicitly by hints */
219 if (codec->beep_mode == HDA_BEEP_MODE_OFF)
220 return 0; /* disabled by module option */
221
222 beep = kzalloc(sizeof(*beep), GFP_KERNEL);
223 if (beep == NULL)
224 return -ENOMEM;
225 snprintf(beep->phys, sizeof(beep->phys),
226 "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
158 /* enable linear scale */ 227 /* enable linear scale */
159 snd_hda_codec_write(codec, nid, 0, 228 snd_hda_codec_write(codec, nid, 0,
160 AC_VERB_SET_DIGI_CONVERT_2, 0x01); 229 AC_VERB_SET_DIGI_CONVERT_2, 0x01);
161 230
162 beep->nid = nid; 231 beep->nid = nid;
163 beep->dev = input_dev;
164 beep->codec = codec; 232 beep->codec = codec;
165 beep->enabled = 1; 233 beep->mode = codec->beep_mode;
166 codec->beep = beep; 234 codec->beep = beep;
167 235
236 INIT_WORK(&beep->register_work, &snd_hda_do_register);
237 INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister);
168 INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); 238 INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
239 mutex_init(&beep->mutex);
240
241 if (beep->mode == HDA_BEEP_MODE_ON) {
242 beep->enabled = 1;
243 snd_hda_do_register(&beep->register_work);
244 }
245
169 return 0; 246 return 0;
170} 247}
171EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device); 248EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device);
@@ -174,11 +251,12 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
174{ 251{
175 struct hda_beep *beep = codec->beep; 252 struct hda_beep *beep = codec->beep;
176 if (beep) { 253 if (beep) {
177 cancel_work_sync(&beep->beep_work); 254 cancel_work_sync(&beep->register_work);
178 255 cancel_delayed_work(&beep->unregister_work);
179 input_unregister_device(beep->dev); 256 if (beep->enabled)
180 kfree(beep); 257 snd_hda_do_detach(beep);
181 codec->beep = NULL; 258 codec->beep = NULL;
259 kfree(beep);
182 } 260 }
183} 261}
184EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device); 262EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);