This source file includes following definitions.
- generate_tone
- snd_hda_generate_beep
- beep_linear_tone
- beep_standard_tone
- snd_hda_beep_event
- turn_off_beep
- snd_hda_enable_beep_device
- beep_dev_register
- beep_dev_disconnect
- beep_dev_free
- snd_hda_attach_beep_device
- snd_hda_detach_beep_device
- ctl_has_mute
- snd_hda_mixer_amp_switch_get_beep
- snd_hda_mixer_amp_switch_put_beep
   1 
   2 
   3 
   4 
   5 
   6 
   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,        
  19         DIGBEEP_HZ_MIN = 93750,         
  20         DIGBEEP_HZ_MAX = 12000000,      
  21 };
  22 
  23 
  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 
  54 
  55 
  56 
  57 
  58 
  59 
  60 static int beep_linear_tone(struct hda_beep *beep, int hz)
  61 {
  62         if (hz <= 0)
  63                 return 0;
  64         hz *= 1000; 
  65         hz = hz - DIGBEEP_HZ_MIN
  66                 + DIGBEEP_HZ_STEP / 2; 
  67         if (hz < 0)
  68                 hz = 0; 
  69         else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
  70                 hz = 1; 
  71         else {
  72                 hz /= DIGBEEP_HZ_STEP;
  73                 hz = 255 - hz;
  74         }
  75         return hz;
  76 }
  77 
  78 
  79 
  80 
  81 
  82 
  83 
  84 static int beep_standard_tone(struct hda_beep *beep, int hz)
  85 {
  86         if (hz <= 0)
  87                 return 0; 
  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                 
 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         
 117         schedule_work(&beep->beep_work);
 118         return 0;
 119 }
 120 
 121 static void turn_off_beep(struct hda_beep *beep)
 122 {
 123         cancel_work_sync(&beep->beep_work);
 124         if (beep->playing) {
 125                 
 126                 generate_tone(beep, 0);
 127         }
 128 }
 129 
 130 
 131 
 132 
 133 
 134 
 135 int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
 136 {
 137         struct hda_beep *beep = codec->beep;
 138         if (!beep)
 139                 return 0;
 140         enable = !!enable;
 141         if (beep->enabled != enable) {
 142                 beep->enabled = enable;
 143                 if (!enable)
 144                         turn_off_beep(beep);
 145                 return 1;
 146         }
 147         return 0;
 148 }
 149 EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device);
 150 
 151 static int beep_dev_register(struct snd_device *device)
 152 {
 153         struct hda_beep *beep = device->device_data;
 154         int err;
 155 
 156         err = input_register_device(beep->dev);
 157         if (!err)
 158                 beep->registered = true;
 159         return err;
 160 }
 161 
 162 static int beep_dev_disconnect(struct snd_device *device)
 163 {
 164         struct hda_beep *beep = device->device_data;
 165 
 166         if (beep->registered)
 167                 input_unregister_device(beep->dev);
 168         else
 169                 input_free_device(beep->dev);
 170         turn_off_beep(beep);
 171         return 0;
 172 }
 173 
 174 static int beep_dev_free(struct snd_device *device)
 175 {
 176         struct hda_beep *beep = device->device_data;
 177 
 178         beep->codec->beep = NULL;
 179         kfree(beep);
 180         return 0;
 181 }
 182 
 183 
 184 
 185 
 186 
 187 
 188 
 189 
 190 
 191 
 192 
 193 int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
 194 {
 195         static struct snd_device_ops ops = {
 196                 .dev_register = beep_dev_register,
 197                 .dev_disconnect = beep_dev_disconnect,
 198                 .dev_free = beep_dev_free,
 199         };
 200         struct input_dev *input_dev;
 201         struct hda_beep *beep;
 202         int err;
 203 
 204         if (!snd_hda_get_bool_hint(codec, "beep"))
 205                 return 0; 
 206         if (codec->beep_mode == HDA_BEEP_MODE_OFF)
 207                 return 0; 
 208 
 209         beep = kzalloc(sizeof(*beep), GFP_KERNEL);
 210         if (beep == NULL)
 211                 return -ENOMEM;
 212         snprintf(beep->phys, sizeof(beep->phys),
 213                 "card%d/codec#%d/beep0", codec->card->number, codec->addr);
 214         
 215         snd_hda_codec_write_cache(codec, nid, 0,
 216                 AC_VERB_SET_DIGI_CONVERT_2, 0x01);
 217 
 218         beep->nid = nid;
 219         beep->codec = codec;
 220         codec->beep = beep;
 221 
 222         INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
 223         mutex_init(&beep->mutex);
 224 
 225         input_dev = input_allocate_device();
 226         if (!input_dev) {
 227                 err = -ENOMEM;
 228                 goto err_free;
 229         }
 230 
 231         
 232         input_dev->name = "HDA Digital PCBeep";
 233         input_dev->phys = beep->phys;
 234         input_dev->id.bustype = BUS_PCI;
 235         input_dev->dev.parent = &codec->card->card_dev;
 236 
 237         input_dev->id.vendor = codec->core.vendor_id >> 16;
 238         input_dev->id.product = codec->core.vendor_id & 0xffff;
 239         input_dev->id.version = 0x01;
 240 
 241         input_dev->evbit[0] = BIT_MASK(EV_SND);
 242         input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
 243         input_dev->event = snd_hda_beep_event;
 244         input_set_drvdata(input_dev, beep);
 245 
 246         beep->dev = input_dev;
 247 
 248         err = snd_device_new(codec->card, SNDRV_DEV_JACK, beep, &ops);
 249         if (err < 0)
 250                 goto err_input;
 251 
 252         return 0;
 253 
 254  err_input:
 255         input_free_device(beep->dev);
 256  err_free:
 257         kfree(beep);
 258         codec->beep = NULL;
 259         return err;
 260 }
 261 EXPORT_SYMBOL_GPL(snd_hda_attach_beep_device);
 262 
 263 
 264 
 265 
 266 
 267 void snd_hda_detach_beep_device(struct hda_codec *codec)
 268 {
 269         if (!codec->bus->shutdown && codec->beep)
 270                 snd_device_free(codec->card, codec->beep);
 271 }
 272 EXPORT_SYMBOL_GPL(snd_hda_detach_beep_device);
 273 
 274 static bool ctl_has_mute(struct snd_kcontrol *kcontrol)
 275 {
 276         struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 277         return query_amp_caps(codec, get_amp_nid(kcontrol),
 278                               get_amp_direction(kcontrol)) & AC_AMPCAP_MUTE;
 279 }
 280 
 281 
 282 
 283 
 284 
 285 
 286 
 287 
 288 int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
 289                                       struct snd_ctl_elem_value *ucontrol)
 290 {
 291         struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 292         struct hda_beep *beep = codec->beep;
 293         int chs = get_amp_channels(kcontrol);
 294 
 295         if (beep && (!beep->enabled || !ctl_has_mute(kcontrol))) {
 296                 if (chs & 1)
 297                         ucontrol->value.integer.value[0] = beep->enabled;
 298                 if (chs & 2)
 299                         ucontrol->value.integer.value[1] = beep->enabled;
 300                 return 0;
 301         }
 302         return snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
 303 }
 304 EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get_beep);
 305 
 306 
 307 
 308 
 309 
 310 
 311 int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
 312                                       struct snd_ctl_elem_value *ucontrol)
 313 {
 314         struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 315         struct hda_beep *beep = codec->beep;
 316         if (beep) {
 317                 u8 chs = get_amp_channels(kcontrol);
 318                 int enable = 0;
 319                 long *valp = ucontrol->value.integer.value;
 320                 if (chs & 1) {
 321                         enable |= *valp;
 322                         valp++;
 323                 }
 324                 if (chs & 2)
 325                         enable |= *valp;
 326                 snd_hda_enable_beep_device(codec, enable);
 327         }
 328         if (!ctl_has_mute(kcontrol))
 329                 return 0;
 330         return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
 331 }
 332 EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put_beep);