2008-07-18 18:20:52 +02:00
/*
* Digital Beep Input Interface for HD - audio codec
*
* Author : Matthew Ranostay < mranostay @ embeddedalley . com >
* Copyright ( c ) 2008 Embedded Alley Solutions Inc
*
* This driver is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This driver is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/input.h>
# include <linux/pci.h>
# include <linux/workqueue.h>
# include <sound/core.h>
# include "hda_beep.h"
2009-06-29 08:34:06 +02:00
# include "hda_local.h"
2008-07-18 18:20:52 +02:00
enum {
DIGBEEP_HZ_STEP = 46875 , /* 46.875 Hz */
DIGBEEP_HZ_MIN = 93750 , /* 93.750 Hz */
DIGBEEP_HZ_MAX = 12000000 , /* 12 KHz */
} ;
static void snd_hda_generate_beep ( struct work_struct * work )
{
struct hda_beep * beep =
container_of ( work , struct hda_beep , beep_work ) ;
struct hda_codec * codec = beep - > codec ;
2008-11-12 16:45:04 +01:00
if ( ! beep - > enabled )
return ;
2008-07-18 18:20:52 +02:00
/* generate tone */
snd_hda_codec_write_cache ( codec , beep - > nid , 0 ,
AC_VERB_SET_BEEP_CONTROL , beep - > tone ) ;
}
2009-05-19 12:50:04 +02:00
/* (non-standard) Linear beep tone calculation for IDT/STAC codecs
*
* The tone frequency of beep generator on IDT / STAC codecs is
* defined from the 8 bit tone parameter , in Hz ,
* freq = 48000 * ( 257 - tone ) / 1024
2009-07-08 23:57:46 -07:00
* that is from 12 kHz to 93.75 Hz in steps of 46.875 Hz
2009-05-19 12:50:04 +02:00
*/
static int beep_linear_tone ( struct hda_beep * beep , int hz )
{
2009-07-08 23:57:46 -07:00
if ( hz < = 0 )
return 0 ;
2009-05-19 12:50:04 +02:00
hz * = 1000 ; /* fixed point */
2009-07-08 23:57:46 -07:00
hz = hz - DIGBEEP_HZ_MIN
+ DIGBEEP_HZ_STEP / 2 ; /* round to nearest step */
2009-05-19 12:50:04 +02:00
if ( hz < 0 )
hz = 0 ; /* turn off PC beep*/
else if ( hz > = ( DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN ) )
2009-07-08 23:57:46 -07:00
hz = 1 ; /* max frequency */
2009-05-19 12:50:04 +02:00
else {
hz / = DIGBEEP_HZ_STEP ;
2009-07-08 23:57:46 -07:00
hz = 255 - hz ;
2009-05-19 12:50:04 +02:00
}
return hz ;
}
/* HD-audio standard beep tone parameter calculation
*
* The tone frequency in Hz is calculated as
* freq = 48000 / ( tone * 4 )
* from 47 Hz to 12 kHz
*/
static int beep_standard_tone ( struct hda_beep * beep , int hz )
{
if ( hz < = 0 )
return 0 ; /* disabled */
hz = 12000 / hz ;
if ( hz > 0xff )
return 0xff ;
if ( hz < = 0 )
return 1 ;
return hz ;
}
2008-07-18 18:20:52 +02:00
static int snd_hda_beep_event ( struct input_dev * dev , unsigned int type ,
unsigned int code , int hz )
{
struct hda_beep * beep = input_get_drvdata ( dev ) ;
switch ( code ) {
case SND_BELL :
if ( hz )
hz = 1000 ;
case SND_TONE :
2009-05-19 12:50:04 +02:00
if ( beep - > linear_tone )
beep - > tone = beep_linear_tone ( beep , hz ) ;
else
beep - > tone = beep_standard_tone ( beep , hz ) ;
2008-07-18 18:20:52 +02:00
break ;
default :
return - 1 ;
}
/* schedule beep event */
schedule_work ( & beep - > beep_work ) ;
return 0 ;
}
int snd_hda_attach_beep_device ( struct hda_codec * codec , int nid )
{
struct input_dev * input_dev ;
struct hda_beep * beep ;
int err ;
2009-06-29 08:34:06 +02:00
if ( ! snd_hda_get_bool_hint ( codec , " beep " ) )
return 0 ; /* disabled explicitly */
2008-07-18 18:20:52 +02:00
beep = kzalloc ( sizeof ( * beep ) , GFP_KERNEL ) ;
if ( beep = = NULL )
return - ENOMEM ;
snprintf ( beep - > phys , sizeof ( beep - > phys ) ,
" card%d/codec#%d/beep0 " , codec - > bus - > card - > number , codec - > addr ) ;
input_dev = input_allocate_device ( ) ;
2008-11-13 13:08:56 +01:00
if ( ! input_dev ) {
kfree ( beep ) ;
return - ENOMEM ;
}
2008-07-18 18:20:52 +02:00
/* setup digital beep device */
input_dev - > name = " HDA Digital PCBeep " ;
input_dev - > phys = beep - > phys ;
input_dev - > id . bustype = BUS_PCI ;
input_dev - > id . vendor = codec - > vendor_id > > 16 ;
input_dev - > id . product = codec - > vendor_id & 0xffff ;
input_dev - > id . version = 0x01 ;
input_dev - > evbit [ 0 ] = BIT_MASK ( EV_SND ) ;
input_dev - > sndbit [ 0 ] = BIT_MASK ( SND_BELL ) | BIT_MASK ( SND_TONE ) ;
input_dev - > event = snd_hda_beep_event ;
input_dev - > dev . parent = & codec - > bus - > pci - > dev ;
input_set_drvdata ( input_dev , beep ) ;
err = input_register_device ( input_dev ) ;
if ( err < 0 ) {
2008-07-29 12:08:16 +02:00
input_free_device ( input_dev ) ;
2008-07-18 18:20:52 +02:00
kfree ( beep ) ;
return err ;
}
/* enable linear scale */
snd_hda_codec_write ( codec , nid , 0 ,
AC_VERB_SET_DIGI_CONVERT_2 , 0x01 ) ;
beep - > nid = nid ;
beep - > dev = input_dev ;
beep - > codec = codec ;
2008-11-12 16:45:04 +01:00
beep - > enabled = 1 ;
2008-07-18 18:20:52 +02:00
codec - > beep = beep ;
INIT_WORK ( & beep - > beep_work , & snd_hda_generate_beep ) ;
return 0 ;
}
2008-11-28 15:17:06 +01:00
EXPORT_SYMBOL_HDA ( snd_hda_attach_beep_device ) ;
2008-07-18 18:20:52 +02:00
void snd_hda_detach_beep_device ( struct hda_codec * codec )
{
struct hda_beep * beep = codec - > beep ;
if ( beep ) {
cancel_work_sync ( & beep - > beep_work ) ;
input_unregister_device ( beep - > dev ) ;
kfree ( beep ) ;
2009-02-06 16:48:10 +01:00
codec - > beep = NULL ;
2008-07-18 18:20:52 +02:00
}
}
2008-11-28 15:17:06 +01:00
EXPORT_SYMBOL_HDA ( snd_hda_detach_beep_device ) ;