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/workqueue.h> 25 #include <sound/core.h> 26 #include "hda_beep.h" 27 28 enum { 29 DIGBEEP_HZ_STEP = 46875, /* 46.875 Hz */ 30 DIGBEEP_HZ_MIN = 93750, /* 93.750 Hz */ 31 DIGBEEP_HZ_MAX = 12000000, /* 12 KHz */ 32 }; 33 34 static void snd_hda_generate_beep(struct work_struct *work) 35 { 36 struct hda_beep *beep = 37 container_of(work, struct hda_beep, beep_work); 38 struct hda_codec *codec = beep->codec; 39 40 if (!beep->enabled) 41 return; 42 43 /* generate tone */ 44 snd_hda_codec_write_cache(codec, beep->nid, 0, 45 AC_VERB_SET_BEEP_CONTROL, beep->tone); 46 } 47 48 static int snd_hda_beep_event(struct input_dev *dev, unsigned int type, 49 unsigned int code, int hz) 50 { 51 struct hda_beep *beep = input_get_drvdata(dev); 52 53 switch (code) { 54 case SND_BELL: 55 if (hz) 56 hz = 1000; 57 case SND_TONE: 58 hz *= 1000; /* fixed point */ 59 hz = hz - DIGBEEP_HZ_MIN; 60 if (hz < 0) 61 hz = 0; /* turn off PC beep*/ 62 else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN)) 63 hz = 0xff; 64 else { 65 hz /= DIGBEEP_HZ_STEP; 66 hz++; 67 } 68 break; 69 default: 70 return -1; 71 } 72 beep->tone = hz; 73 74 /* schedule beep event */ 75 schedule_work(&beep->beep_work); 76 return 0; 77 } 78 79 int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) 80 { 81 struct input_dev *input_dev; 82 struct hda_beep *beep; 83 int err; 84 85 beep = kzalloc(sizeof(*beep), GFP_KERNEL); 86 if (beep == NULL) 87 return -ENOMEM; 88 snprintf(beep->phys, sizeof(beep->phys), 89 "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr); 90 input_dev = input_allocate_device(); 91 if (!input_dev) { 92 kfree(beep); 93 return -ENOMEM; 94 } 95 96 /* setup digital beep device */ 97 input_dev->name = "HDA Digital PCBeep"; 98 input_dev->phys = beep->phys; 99 input_dev->id.bustype = BUS_PCI; 100 101 input_dev->id.vendor = codec->vendor_id >> 16; 102 input_dev->id.product = codec->vendor_id & 0xffff; 103 input_dev->id.version = 0x01; 104 105 input_dev->evbit[0] = BIT_MASK(EV_SND); 106 input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); 107 input_dev->event = snd_hda_beep_event; 108 input_dev->dev.parent = &codec->bus->pci->dev; 109 input_set_drvdata(input_dev, beep); 110 111 err = input_register_device(input_dev); 112 if (err < 0) { 113 input_free_device(input_dev); 114 kfree(beep); 115 return err; 116 } 117 118 /* enable linear scale */ 119 snd_hda_codec_write(codec, nid, 0, 120 AC_VERB_SET_DIGI_CONVERT_2, 0x01); 121 122 beep->nid = nid; 123 beep->dev = input_dev; 124 beep->codec = codec; 125 beep->enabled = 1; 126 codec->beep = beep; 127 128 INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); 129 return 0; 130 } 131 EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device); 132 133 void snd_hda_detach_beep_device(struct hda_codec *codec) 134 { 135 struct hda_beep *beep = codec->beep; 136 if (beep) { 137 cancel_work_sync(&beep->beep_work); 138 139 input_unregister_device(beep->dev); 140 kfree(beep); 141 codec->beep = NULL; 142 } 143 } 144 EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device); 145