root/sound/pci/hda/hda_beep.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. generate_tone
  2. snd_hda_generate_beep
  3. beep_linear_tone
  4. beep_standard_tone
  5. snd_hda_beep_event
  6. turn_off_beep
  7. snd_hda_enable_beep_device
  8. beep_dev_register
  9. beep_dev_disconnect
  10. beep_dev_free
  11. snd_hda_attach_beep_device
  12. snd_hda_detach_beep_device
  13. ctl_has_mute
  14. snd_hda_mixer_amp_switch_get_beep
  15. snd_hda_mixer_amp_switch_put_beep

   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                 /* fallthru */
 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_off_beep(struct hda_beep *beep)
 122 {
 123         cancel_work_sync(&beep->beep_work);
 124         if (beep->playing) {
 125                 /* turn off beep */
 126                 generate_tone(beep, 0);
 127         }
 128 }
 129 
 130 /**
 131  * snd_hda_enable_beep_device - Turn on/off beep sound
 132  * @codec: the HDA codec
 133  * @enable: flag to turn on/off
 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  * snd_hda_attach_beep_device - Attach a beep input device
 185  * @codec: the HDA codec
 186  * @nid: beep NID
 187  *
 188  * Attach a beep object to the given widget.  If beep hint is turned off
 189  * explicitly or beep_mode of the codec is turned off, this doesn't nothing.
 190  *
 191  * Currently, only one beep device is allowed to each codec.
 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; /* disabled explicitly by hints */
 206         if (codec->beep_mode == HDA_BEEP_MODE_OFF)
 207                 return 0; /* disabled by module option */
 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         /* enable linear scale */
 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         /* setup digital beep device */
 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  * snd_hda_detach_beep_device - Detach the beep device
 265  * @codec: the HDA codec
 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 /* get/put callbacks for beep mute mixer switches */
 282 
 283 /**
 284  * snd_hda_mixer_amp_switch_get_beep - Get callback for beep controls
 285  * @kcontrol: ctl element
 286  * @ucontrol: pointer to get/store the data
 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  * snd_hda_mixer_amp_switch_put_beep - Put callback for beep controls
 308  * @kcontrol: ctl element
 309  * @ucontrol: pointer to get/store the data
 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);

/* [<][>][^][v][top][bottom][index][help] */