cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hda_beep.c (8107B)


      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
     17enum {
     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 */
     24static 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
     44static 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 */
     60static 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 */
     84static 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
     96static 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		fallthrough;
    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
    121static 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 */
    135int 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}
    149EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device);
    150
    151static 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
    162static 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
    174static 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 */
    193int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
    194{
    195	static const 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}
    261EXPORT_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 */
    267void 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}
    272EXPORT_SYMBOL_GPL(snd_hda_detach_beep_device);
    273
    274static 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 */
    288int 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}
    304EXPORT_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 */
    311int 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}
    332EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put_beep);