linux/sound/pci/hda/hda_beep.c
<<
>>
Prefs
   1/*
   2 * Digital Beep Input Interface for HD-audio codec
   3 *
   4 * Author: Matthew Ranostay <mranostay@embeddedalley.com>
   5 * Copyright (c) 2008 Embedded Alley Solutions Inc
   6 *
   7 *  This driver is free software; you can redistribute it and/or modify
   8 *  it under the terms of the GNU General Public License as published by
   9 *  the Free Software Foundation; either version 2 of the License, or
  10 *  (at your option) any later version.
  11 *
  12 *  This driver is distributed in the hope that it will be useful,
  13 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 *  GNU General Public License for more details.
  16 *
  17 *  You should have received a copy of the GNU General Public License
  18 *  along with this program; if not, write to the Free Software
  19 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  20 */
  21
  22#include <linux/input.h>
  23#include <linux/pci.h>
  24#include <linux/slab.h>
  25#include <linux/workqueue.h>
  26#include <linux/export.h>
  27#include <sound/core.h>
  28#include "hda_beep.h"
  29#include "hda_local.h"
  30
  31enum {
  32        DIGBEEP_HZ_STEP = 46875,        /* 46.875 Hz */
  33        DIGBEEP_HZ_MIN = 93750,         /* 93.750 Hz */
  34        DIGBEEP_HZ_MAX = 12000000,      /* 12 KHz */
  35};
  36
  37static void snd_hda_generate_beep(struct work_struct *work)
  38{
  39        struct hda_beep *beep =
  40                container_of(work, struct hda_beep, beep_work);
  41        struct hda_codec *codec = beep->codec;
  42
  43        if (!beep->enabled)
  44                return;
  45
  46        /* generate tone */
  47        snd_hda_codec_write(codec, beep->nid, 0,
  48                        AC_VERB_SET_BEEP_CONTROL, beep->tone);
  49}
  50
  51/* (non-standard) Linear beep tone calculation for IDT/STAC codecs 
  52 *
  53 * The tone frequency of beep generator on IDT/STAC codecs is
  54 * defined from the 8bit tone parameter, in Hz,
  55 *    freq = 48000 * (257 - tone) / 1024
  56 * that is from 12kHz to 93.75Hz in steps of 46.875 Hz
  57 */
  58static int beep_linear_tone(struct hda_beep *beep, int hz)
  59{
  60        if (hz <= 0)
  61                return 0;
  62        hz *= 1000; /* fixed point */
  63        hz = hz - DIGBEEP_HZ_MIN
  64                + DIGBEEP_HZ_STEP / 2; /* round to nearest step */
  65        if (hz < 0)
  66                hz = 0; /* turn off PC beep*/
  67        else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
  68                hz = 1; /* max frequency */
  69        else {
  70                hz /= DIGBEEP_HZ_STEP;
  71                hz = 255 - hz;
  72        }
  73        return hz;
  74}
  75
  76/* HD-audio standard beep tone parameter calculation
  77 *
  78 * The tone frequency in Hz is calculated as
  79 *   freq = 48000 / (tone * 4)
  80 * from 47Hz to 12kHz
  81 */
  82static int beep_standard_tone(struct hda_beep *beep, int hz)
  83{
  84        if (hz <= 0)
  85                return 0; /* disabled */
  86        hz = 12000 / hz;
  87        if (hz > 0xff)
  88                return 0xff;
  89        if (hz <= 0)
  90                return 1;
  91        return hz;
  92}
  93
  94static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
  95                                unsigned int code, int hz)
  96{
  97        struct hda_beep *beep = input_get_drvdata(dev);
  98
  99        switch (code) {
 100        case SND_BELL:
 101                if (hz)
 102                        hz = 1000;
 103        case SND_TONE:
 104                if (beep->linear_tone)
 105                        beep->tone = beep_linear_tone(beep, hz);
 106                else
 107                        beep->tone = beep_standard_tone(beep, hz);
 108                break;
 109        default:
 110                return -1;
 111        }
 112
 113        /* schedule beep event */
 114        schedule_work(&beep->beep_work);
 115        return 0;
 116}
 117
 118static void snd_hda_do_detach(struct hda_beep *beep)
 119{
 120        input_unregister_device(beep->dev);
 121        beep->dev = NULL;
 122        cancel_work_sync(&beep->beep_work);
 123        /* turn off beep for sure */
 124        snd_hda_codec_write(beep->codec, beep->nid, 0,
 125                                  AC_VERB_SET_BEEP_CONTROL, 0);
 126}
 127
 128static int snd_hda_do_attach(struct hda_beep *beep)
 129{
 130        struct input_dev *input_dev;
 131        struct hda_codec *codec = beep->codec;
 132        int err;
 133
 134        input_dev = input_allocate_device();
 135        if (!input_dev) {
 136                printk(KERN_INFO "hda_beep: unable to allocate input device\n");
 137                return -ENOMEM;
 138        }
 139
 140        /* setup digital beep device */
 141        input_dev->name = "HDA Digital PCBeep";
 142        input_dev->phys = beep->phys;
 143        input_dev->id.bustype = BUS_PCI;
 144
 145        input_dev->id.vendor = codec->vendor_id >> 16;
 146        input_dev->id.product = codec->vendor_id & 0xffff;
 147        input_dev->id.version = 0x01;
 148
 149        input_dev->evbit[0] = BIT_MASK(EV_SND);
 150        input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
 151        input_dev->event = snd_hda_beep_event;
 152        input_dev->dev.parent = &codec->bus->pci->dev;
 153        input_set_drvdata(input_dev, beep);
 154
 155        err = input_register_device(input_dev);
 156        if (err < 0) {
 157                input_free_device(input_dev);
 158                printk(KERN_INFO "hda_beep: unable to register input device\n");
 159                return err;
 160        }
 161        beep->dev = input_dev;
 162        return 0;
 163}
 164
 165static void snd_hda_do_register(struct work_struct *work)
 166{
 167        struct hda_beep *beep =
 168                container_of(work, struct hda_beep, register_work);
 169
 170        mutex_lock(&beep->mutex);
 171        if (beep->enabled && !beep->dev)
 172                snd_hda_do_attach(beep);
 173        mutex_unlock(&beep->mutex);
 174}
 175
 176static void snd_hda_do_unregister(struct work_struct *work)
 177{
 178        struct hda_beep *beep =
 179                container_of(work, struct hda_beep, unregister_work.work);
 180
 181        mutex_lock(&beep->mutex);
 182        if (!beep->enabled && beep->dev)
 183                snd_hda_do_detach(beep);
 184        mutex_unlock(&beep->mutex);
 185}
 186
 187int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
 188{
 189        struct hda_beep *beep = codec->beep;
 190        enable = !!enable;
 191        if (beep == NULL)
 192                return 0;
 193        if (beep->enabled != enable) {
 194                beep->enabled = enable;
 195                if (!enable) {
 196                        /* turn off beep */
 197                        snd_hda_codec_write(beep->codec, beep->nid, 0,
 198                                                  AC_VERB_SET_BEEP_CONTROL, 0);
 199                }
 200                if (beep->mode == HDA_BEEP_MODE_SWREG) {
 201                        if (enable) {
 202                                cancel_delayed_work(&beep->unregister_work);
 203                                schedule_work(&beep->register_work);
 204                        } else {
 205                                schedule_delayed_work(&beep->unregister_work,
 206                                                                           HZ);
 207                        }
 208                }
 209                return 1;
 210        }
 211        return 0;
 212}
 213EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device);
 214
 215int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
 216{
 217        struct hda_beep *beep;
 218
 219        if (!snd_hda_get_bool_hint(codec, "beep"))
 220                return 0; /* disabled explicitly by hints */
 221        if (codec->beep_mode == HDA_BEEP_MODE_OFF)
 222                return 0; /* disabled by module option */
 223
 224        beep = kzalloc(sizeof(*beep), GFP_KERNEL);
 225        if (beep == NULL)
 226                return -ENOMEM;
 227        snprintf(beep->phys, sizeof(beep->phys),
 228                "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
 229        /* enable linear scale */
 230        snd_hda_codec_write(codec, nid, 0,
 231                AC_VERB_SET_DIGI_CONVERT_2, 0x01);
 232
 233        beep->nid = nid;
 234        beep->codec = codec;
 235        beep->mode = codec->beep_mode;
 236        codec->beep = beep;
 237
 238        INIT_WORK(&beep->register_work, &snd_hda_do_register);
 239        INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister);
 240        INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
 241        mutex_init(&beep->mutex);
 242
 243        if (beep->mode == HDA_BEEP_MODE_ON) {
 244                int err = snd_hda_do_attach(beep);
 245                if (err < 0) {
 246                        kfree(beep);
 247                        codec->beep = NULL;
 248                        return err;
 249                }
 250        }
 251
 252        return 0;
 253}
 254EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device);
 255
 256void snd_hda_detach_beep_device(struct hda_codec *codec)
 257{
 258        struct hda_beep *beep = codec->beep;
 259        if (beep) {
 260                cancel_work_sync(&beep->register_work);
 261                cancel_delayed_work(&beep->unregister_work);
 262                if (beep->dev)
 263                        snd_hda_do_detach(beep);
 264                codec->beep = NULL;
 265                kfree(beep);
 266        }
 267}
 268EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
 269