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 17 enum { 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 */ 24 static 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 44 static 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 */ 60 static 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 */ 84 static 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 96 static 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 121 static 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 127 static 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 */ 143 int 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 } 159 EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device); 160 161 static 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 172 static 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 185 static 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 */ 204 int 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 } 271 EXPORT_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 */ 277 void 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 } 282 EXPORT_SYMBOL_GPL(snd_hda_detach_beep_device); 283 284 static 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 */ 298 int 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 } 314 EXPORT_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 */ 321 int 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 } 342 EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put_beep); 343
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.