diff options
Diffstat (limited to 'sound/pci/hda/hda_beep.c')
-rw-r--r-- | sound/pci/hda/hda_beep.c | 114 |
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 | ||
116 | int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) | 116 | static 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 | |||
126 | static 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 | |||
163 | static 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 | |||
174 | static 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 | ||
185 | int 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 | } | ||
211 | EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device); | ||
212 | |||
213 | int 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 | } |
171 | EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device); | 248 | EXPORT_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 | } |
184 | EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device); | 262 | EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device); |