linux_dsm_epyc7002/sound/pci/hda/hda_codec.h

563 lines
17 KiB
C
Raw Normal View History

/*
* Universal Interface for Intel High Definition Audio Codec
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
*
* This program 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 program 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.
*/
#ifndef __SOUND_HDA_CODEC_H
#define __SOUND_HDA_CODEC_H
#include <linux/kref.h>
#include <sound/info.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/hwdep.h>
#include <sound/hdaudio.h>
#include <sound/hda_verbs.h>
ALSA: hda - Add regmap support This patch adds an infrastructure to support regmap-based verb accesses. Because o the asymmetric nature of HD-audio verbs, especially the amp verbs, we need to translate the verbs as a sort of pseudo registers to be mapped uniquely in regmap. In this patch, a pseudo register is built from the NID, the AC_VERB_GET_* and 8bit parameters, i.e. almost in the form to be sent to HD-audio bus but without codec address field. OTOH, for writing, the same pseudo register is translated to AC_VERB_SET_* automatically. The AC_VERB_SET_AMP_* verb is re-encoded from the corresponding AC_VERB_GET_AMP_* verb and parameter at writing. Some verbs has a single command for read but multiple for writes. A write for such a verb is split automatically to multiple verbs. The patch provides also a few handy helper functions. They are designed to be accessible even without regmap. When no regmap is set up (e.g. before the codec device instantiation), the direct hardware access is used. Also, it tries to avoid the unnecessary power-up. The power up/down sequence is performed only on demand. The codec driver needs to call snd_hdac_regmap_exit() and snd_hdac_regmap_exit() at probe and remove if it wants the regmap access. There is one flag added to hdac_device. When the flag lazy_cache is set, regmap helper ignores a write for a suspended device and returns as if it was actually written. It reduces the hardware access pretty much, e.g. when adjusting the mixer volume while in idle. This assumes that the driver will sync the cache later at resume properly, so use it carefully. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-02-25 20:42:38 +07:00
#include <sound/hda_regmap.h>
/*
* Structures
*/
struct hda_bus;
struct hda_beep;
struct hda_codec;
struct hda_pcm;
struct hda_pcm_stream;
/* bus operators */
struct hda_bus_ops {
/* free the private data */
void (*private_free)(struct hda_bus *);
/* attach a PCM stream */
int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec,
struct hda_pcm *pcm);
/* reset bus for retry verb */
void (*bus_reset)(struct hda_bus *bus);
#ifdef CONFIG_SND_HDA_DSP_LOADER
/* prepare DSP transfer */
int (*load_dsp_prepare)(struct hda_bus *bus, unsigned int format,
unsigned int byte_size,
struct snd_dma_buffer *bufp);
/* start/stop DSP transfer */
void (*load_dsp_trigger)(struct hda_bus *bus, bool start);
/* clean up DSP transfer */
void (*load_dsp_cleanup)(struct hda_bus *bus,
struct snd_dma_buffer *dmab);
#endif
};
/*
* codec bus
*
* each controller needs to creata a hda_bus to assign the accessor.
* A hda_bus contains several codecs in the list codec_list.
*/
struct hda_bus {
struct hdac_bus core;
struct snd_card *card;
void *private_data;
struct pci_dev *pci;
const char *modelname;
struct hda_bus_ops ops;
struct mutex prepare_mutex;
/* assigned PCMs */
DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES);
/* misc op flags */
unsigned int needs_damn_long_delay :1;
unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */
/* status for codec/controller */
unsigned int shutdown :1; /* being unloaded */
unsigned int response_reset:1; /* controller was reset */
unsigned int in_reset:1; /* during reset operation */
unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
int primary_dig_out_type; /* primary digital out PCM type */
};
/* from hdac_bus to hda_bus */
#define to_hda_bus(bus) container_of(bus, struct hda_bus, core)
/*
* codec preset
*
* Known codecs have the patch to build and set up the controls/PCMs
* better than the generic parser.
*/
struct hda_codec_preset {
unsigned int id;
unsigned int mask;
unsigned int subs;
unsigned int subs_mask;
unsigned int rev;
hda_nid_t afg, mfg;
const char *name;
int (*patch)(struct hda_codec *codec);
};
#define HDA_CODEC_ID_GENERIC_HDMI 0x00000101
#define HDA_CODEC_ID_GENERIC 0x00000201
struct hda_codec_driver {
struct hdac_driver core;
const struct hda_codec_preset *preset;
};
int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
struct module *owner);
#define hda_codec_driver_register(drv) \
__hda_codec_driver_register(drv, KBUILD_MODNAME, THIS_MODULE)
void hda_codec_driver_unregister(struct hda_codec_driver *drv);
#define module_hda_codec_driver(drv) \
module_driver(drv, hda_codec_driver_register, \
hda_codec_driver_unregister)
/* ops set by the preset patch */
struct hda_codec_ops {
int (*build_controls)(struct hda_codec *codec);
int (*build_pcms)(struct hda_codec *codec);
int (*init)(struct hda_codec *codec);
void (*free)(struct hda_codec *codec);
void (*unsol_event)(struct hda_codec *codec, unsigned int res);
void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state);
#ifdef CONFIG_PM
int (*suspend)(struct hda_codec *codec);
int (*resume)(struct hda_codec *codec);
int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
#endif
void (*reboot_notify)(struct hda_codec *codec);
ALSA: hda - Support advanced power state controls This patch enables the finer power state control of each widget depending on the jack plug state and streaming state in addition to the existing power_down_unused power optimization. The new feature is enabled only when codec->power_mgmt flag is set. Two new flags, pin_enabled and stream_enabled, are introduced in nid_path struct for marking the two individual power states: the pin plug/unplug and DAC/ADC stream, respectively. They can be set statically in case they are static routes (e.g. some mixer paths), too. The power up and down events for each pin are triggered via the standard hda_jack table. The call order is hard-coded, relying on the current implementation of jack event chain (a la FILO/stack order). One point to be dealt carefully is that DAC/ADC cannot be powered on/off while streaming. They are pinned as long as the stream is running. For controlling the power of DAC/ADC, a new patch_ops is added. The generic parser provides the default callback for that. As of this patch, only IDT/Sigmatel codec driver enables the flag. The support on other codecs will follow. An assumption we made in this code is that the widget state (e.g. amp, pinctl, connections) remains after the widget power transition (not about FG power transition). This is true for IDT codecs, at least. But if the widget state is lost at widget power transition, we'd need to implement additional code to sync the cached amp/verbs for the specific NID. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-03-17 03:32:11 +07:00
void (*stream_pm)(struct hda_codec *codec, hda_nid_t nid, bool on);
};
/* PCM callbacks */
struct hda_pcm_ops {
int (*open)(struct hda_pcm_stream *info, struct hda_codec *codec,
struct snd_pcm_substream *substream);
int (*close)(struct hda_pcm_stream *info, struct hda_codec *codec,
struct snd_pcm_substream *substream);
int (*prepare)(struct hda_pcm_stream *info, struct hda_codec *codec,
unsigned int stream_tag, unsigned int format,
struct snd_pcm_substream *substream);
int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec,
struct snd_pcm_substream *substream);
unsigned int (*get_delay)(struct hda_pcm_stream *info,
struct hda_codec *codec,
struct snd_pcm_substream *substream);
};
/* PCM information for each substream */
struct hda_pcm_stream {
unsigned int substreams; /* number of substreams, 0 = not exist*/
unsigned int channels_min; /* min. number of channels */
unsigned int channels_max; /* max. number of channels */
hda_nid_t nid; /* default NID to query rates/formats/bps, or set up */
u32 rates; /* supported rates */
u64 formats; /* supported formats (SNDRV_PCM_FMTBIT_) */
unsigned int maxbps; /* supported max. bit per sample */
const struct snd_pcm_chmap_elem *chmap; /* chmap to override */
struct hda_pcm_ops ops;
};
/* PCM types */
enum {
HDA_PCM_TYPE_AUDIO,
HDA_PCM_TYPE_SPDIF,
HDA_PCM_TYPE_HDMI,
HDA_PCM_TYPE_MODEM,
HDA_PCM_NTYPES
};
/* for PCM creation */
struct hda_pcm {
char *name;
struct hda_pcm_stream stream[2];
unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */
int device; /* device number to assign */
struct snd_pcm *pcm; /* assigned PCM instance */
bool own_chmap; /* codec driver provides own channel maps */
/* private: */
struct hda_codec *codec;
struct kref kref;
struct list_head list;
};
/* codec information */
struct hda_codec {
struct hdac_device core;
struct hda_bus *bus;
struct snd_card *card;
unsigned int addr; /* codec addr*/
u32 probe_id; /* overridden id for probing */
/* detected preset */
const struct hda_codec_preset *preset;
const char *modelname; /* model name for preset */
/* set by patch */
struct hda_codec_ops patch_ops;
/* PCM to create, set by patch_ops.build_pcms callback */
struct list_head pcm_list_head;
/* codec specific info */
void *spec;
/* beep device */
struct hda_beep *beep;
unsigned int beep_mode;
/* widget capabilities cache */
u32 *wcaps;
struct snd_array mixers; /* list of assigned mixer elements */
struct snd_array nids; /* list of mapped mixer elements */
struct list_head conn_list; /* linked-list of connection-list */
struct mutex spdif_mutex;
struct mutex control_mutex;
struct snd_array spdif_out;
unsigned int spdif_in_enable; /* SPDIF input enable? */
const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
struct snd_array init_pins; /* initial (BIOS) pin configurations */
struct snd_array driver_pins; /* pin configs set by codec parser */
struct snd_array cvt_setups; /* audio convert setups */
struct mutex user_mutex;
#ifdef CONFIG_SND_HDA_RECONFIG
struct snd_array init_verbs; /* additional init verbs */
struct snd_array hints; /* additional hints */
struct snd_array user_pins; /* default pin configs to override */
#endif
#ifdef CONFIG_SND_HDA_HWDEP
struct snd_hwdep *hwdep; /* assigned hwdep device */
#endif
/* misc flags */
unsigned int in_freeing:1; /* being released */
unsigned int registered:1; /* codec was registered */
unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each
* status change
* (e.g. Realtek codecs)
*/
unsigned int pin_amp_workaround:1; /* pin out-amp takes index
* (e.g. Conexant codecs)
*/
unsigned int single_adc_amp:1; /* adc in-amp takes no index
* (e.g. CX20549 codec)
*/
unsigned int no_sticky_stream:1; /* no sticky-PCM stream assignment */
unsigned int pins_shutup:1; /* pins are shut up */
unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
unsigned int no_jack_detect:1; /* Machine has no jack-detection */
unsigned int inv_eapd:1; /* broken h/w: inverted EAPD control */
unsigned int inv_jack_detect:1; /* broken h/w: inverted detection bit */
unsigned int pcm_format_first:1; /* PCM format must be set first */
unsigned int cached_write:1; /* write only to caches */
unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */
unsigned int dump_coef:1; /* dump processing coefs in codec proc file */
unsigned int power_save_node:1; /* advanced PM for each widget */
#ifdef CONFIG_PM
unsigned long power_on_acct;
unsigned long power_off_acct;
unsigned long power_jiffies;
#endif
/* filter the requested power state per nid */
unsigned int (*power_filter)(struct hda_codec *codec, hda_nid_t nid,
unsigned int power_state);
/* codec-specific additional proc output */
void (*proc_widget_hook)(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid);
/* jack detection */
struct snd_array jacktbl;
unsigned long jackpoll_interval; /* In jiffies. Zero means no poll, rely on unsol events */
struct delayed_work jackpoll_work;
#ifdef CONFIG_SND_HDA_INPUT_JACK
/* jack detection */
struct snd_array jacks;
#endif
int depop_delay; /* depop delay in ms, -1 for default delay time */
/* fix-up list */
int fixup_id;
const struct hda_fixup *fixup_list;
const char *fixup_name;
/* additional init verbs */
struct snd_array verbs;
};
#define dev_to_hda_codec(_dev) container_of(_dev, struct hda_codec, core.dev)
#define hda_codec_dev(_dev) (&(_dev)->core.dev)
#define list_for_each_codec(c, bus) \
list_for_each_entry(c, &(bus)->core.codec_list, core.list)
/* snd_hda_codec_read/write optional flags */
#define HDA_RW_NO_RESPONSE_FALLBACK (1 << 0)
/*
* constructors
*/
int snd_hda_bus_new(struct snd_card *card,
const struct hdac_bus_ops *ops,
struct hda_bus **busp);
int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
unsigned int codec_addr, struct hda_codec **codecp);
int snd_hda_codec_configure(struct hda_codec *codec);
int snd_hda_codec_update_widgets(struct hda_codec *codec);
/*
* low level functions
*/
unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
int flags,
unsigned int verb, unsigned int parm);
int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
unsigned int verb, unsigned int parm);
#define snd_hda_param_read(codec, nid, param) \
snd_hdac_read_parm(&(codec)->core, nid, param)
#define snd_hda_get_sub_nodes(codec, nid, start_nid) \
snd_hdac_get_sub_nodes(&(codec)->core, nid, start_nid)
int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns);
static inline int
snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid)
{
return snd_hda_get_connections(codec, nid, NULL, 0);
}
#define snd_hda_get_raw_connections(codec, nid, list, max_conns) \
snd_hdac_get_connections(&(codec)->core, nid, list, max_conns)
#define snd_hda_get_num_raw_conns(codec, nid) \
snd_hdac_get_connections(&(codec)->core, nid, NULL, 0);
int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
const hda_nid_t **listp);
int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
const hda_nid_t *list);
int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
hda_nid_t nid, int recursive);
int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
u8 *dev_list, int max_devices);
ALSA: hda: HDMI: Support codecs with fewer cvts than pins The general concept of this change is to create a PCM device for each pin widget instead of each converter widget. Whenever a PCM is opened, a converter is dynamically selected to drive that pin based on those available for muxing into the pin. The one thing this model doesn't support is a single PCM/converter sending audio to multiple pin widgets at once. Note that this means that a struct hda_pcm_stream's nid variable is set to 0 except between a stream's open and cleanup calls. The dynamic de-assignment of converters to PCMs occurs within cleanup, not close, in order for it to co-incide with when controller stream IDs are cleaned up from converters. While the PCM for a pin is not open, the pin is disabled (its widget control's PIN_OUT bit is cleared) so that if the currently routed converter is used to drive a different PCM/pin, that audio does not leak out over a disabled pin. We use the recently added SPDIF virtualization feature in order to create SPDIF controls for each pin widget instead of each converter widget, so that state is specific to a PCM. In order to support this, a number of more mechanical changes are made: * s/nid/pin_nid/ or s/nid/cvt_nid/ in many places in order to make it clear exactly what the code is dealing with. * We now have per_pin and per_cvt arrays in hdmi_spec to store relevant data. In particular, we store a converter's capabilities in the per_cvt entry, rather than relying on a combination of codec_pcm_pars and the struct hda_pcm_stream. * ELD-related workarounds were removed from hdmi_channel_allocation into hdmi_instrinsic in order to simplifiy infoframe calculations and remove HW dependencies. * Various functions only apply to a single pin, since there is now only 1 pin per PCM. For example, hdmi_setup_infoframe, hdmi_setup_stream. * hdmi_add_pin and hdmi_add_cvt are more oriented at pure codec parsing and data retrieval, rather than determining which pins/converters are to be used for creating PCMs. This is quite a large change; it may be appropriate to simply read the result of the patch rather than the diffs. Some small parts of the change might be separable into different patches, but I think the bulk of the change will probably always be one large patch. Hopefully the change isn't too opaque! This has been tested on: * NVIDIA GeForce 400 series discrete graphics card. This model has the classical 1:1:1 codec:converter:pcm widget model. Tested stereo PCM audio to a PC monitor that supports audio. * NVIDIA GeForce 520 discrete graphics card. This model is the new 1 codec n converters m pins m>n model. Tested stereo PCM audio to a PC monitor that supports audio. * NVIDIA GeForce 400 series laptop graphics chip. This model has the classical 1:1:1 codec:converter:pcm widget model. Tested stereo PCM, multi-channel PCM, and AC3 pass-through to an AV receiver. * Intel Ibex Peak laptop. This model is the new 1 codec n converters m pins m>n model. Tested stereo PCM, multi-channel PCM, and AC3 pass- through to an AV receiver. Note that I'm not familiar at all with AC3 pass-through. Hence, I may not have covered all possible mechanisms that are applicable here. I do know that my receiver definitely received AC3, not decoded PCM. I tested with mplayer's "-afm hwac3" and/or "-af lavcac3enc" options, and alsa a WAV file that I believe has AC3 content rather than PCM. I also tested: * Play a stream * Mute while playing * Stop stream * Play some other streams to re-assign the converter to a different pin, PCM, set of SPDIF controls, ... hence hopefully triggering cleanup for the original PCM. * Unmute original stream while not playing * Play a stream on the original pin/PCM. This was to test SPDIF control virtualization. Signed-off-by: Stephen Warren <swarren@nvidia.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
2011-06-02 00:14:21 +07:00
int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
struct hda_verb {
hda_nid_t nid;
u32 verb;
u32 param;
};
void snd_hda_sequence_write(struct hda_codec *codec,
const struct hda_verb *seq);
/* unsolicited event */
static inline void
snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
{
snd_hdac_bus_queue_event(&bus->core, res, res_ex);
}
/* cached write */
static inline int
snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
int flags, unsigned int verb, unsigned int parm)
{
return snd_hdac_regmap_write(&codec->core, nid, verb, parm);
}
#define snd_hda_codec_update_cache(codec, nid, flags, verb, parm) \
snd_hda_codec_write_cache(codec, nid, flags, verb, parm)
/* the struct for codec->pin_configs */
struct hda_pincfg {
hda_nid_t nid;
unsigned char ctrl; /* original pin control value */
unsigned char target; /* target pin control value */
unsigned int cfg; /* default configuration */
};
unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid,
unsigned int cfg);
int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
hda_nid_t nid, unsigned int cfg); /* for hwdep */
void snd_hda_shutup_pins(struct hda_codec *codec);
/* SPDIF controls */
struct hda_spdif_out {
hda_nid_t nid; /* Converter nid values relate to */
unsigned int status; /* IEC958 status bits */
unsigned short ctls; /* SPDIF control bits */
};
struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
hda_nid_t nid);
void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx);
void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid);
/*
* Mixer
*/
int snd_hda_codec_build_controls(struct hda_codec *codec);
/*
* PCM
*/
int snd_hda_codec_parse_pcms(struct hda_codec *codec);
int snd_hda_codec_build_pcms(struct hda_codec *codec);
__printf(2, 3)
struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
const char *fmt, ...);
static inline void snd_hda_codec_pcm_get(struct hda_pcm *pcm)
{
kref_get(&pcm->kref);
}
void snd_hda_codec_pcm_put(struct hda_pcm *pcm);
int snd_hda_codec_prepare(struct hda_codec *codec,
struct hda_pcm_stream *hinfo,
unsigned int stream,
unsigned int format,
struct snd_pcm_substream *substream);
void snd_hda_codec_cleanup(struct hda_codec *codec,
struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream);
void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
u32 stream_tag,
int channel_id, int format);
void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
int do_now);
#define snd_hda_codec_cleanup_stream(codec, nid) \
__snd_hda_codec_cleanup_stream(codec, nid, 0)
unsigned int snd_hda_calc_stream_format(struct hda_codec *codec,
unsigned int rate,
unsigned int channels,
unsigned int format,
unsigned int maxbps,
unsigned short spdif_ctls);
int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
unsigned int format);
extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[];
/*
* Misc
*/
void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state);
int snd_hda_lock_devices(struct hda_bus *bus);
void snd_hda_unlock_devices(struct hda_bus *bus);
void snd_hda_bus_reset(struct hda_bus *bus);
/*
* power management
*/
extern const struct dev_pm_ops hda_codec_driver_pm;
static inline
int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
#ifdef CONFIG_PM
if (codec->patch_ops.check_power_status)
return codec->patch_ops.check_power_status(codec, nid);
#endif
return 0;
}
/*
* get widget information
*/
const char *snd_hda_get_jack_connectivity(u32 cfg);
const char *snd_hda_get_jack_type(u32 cfg);
const char *snd_hda_get_jack_location(u32 cfg);
/*
* power saving
*/
#define snd_hda_power_up(codec) snd_hdac_power_up(&(codec)->core)
ALSA: hda - Work around races of power up/down with runtime PM Currently, snd_hdac_power_up()/down() helpers checks whether the codec is being in pm (suspend/resume), and skips the call of runtime get/put during it. This is needed as there are lots of power up/down sequences called in the paths that are also used in the PM itself. An example is found in hda_codec.c::codec_exec_verb(), where this can power up the codec while it may be called again in its power up sequence, too. The above works in most cases, but sometimes we really want to wait for the real power up. For example, the control element get/put may want explicit power up so that the value change is assured to reach to the hardware. Using the current snd_hdac_power_up(), however, results in a race, e.g. when it's called during the runtime suspend is being performed. In the worst case, as found in patch_ca0132.c, it can even lead to the deadlock because the code assumes the power up while it was skipped due to the check above. For dealing with such cases, this patch makes snd_hdac_power_up() and _down() to two variants: with and without in_pm flag check. The version with pm flag check is named as snd_hdac_power_up_pm() while the version without pm flag check is still kept as snd_hdac_power_up(). (Just because the usage of the former is fewer.) Then finally, the patch replaces each call potentially done in PM with the new _pm() variant. In theory, we can implement a unified version -- if we can distinguish the current context whether it's in the pm path. But such an implementation is cumbersome, so leave the code like this a bit messy way for now... Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=96271 Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-04-08 16:43:14 +07:00
#define snd_hda_power_up_pm(codec) snd_hdac_power_up_pm(&(codec)->core)
#define snd_hda_power_down(codec) snd_hdac_power_down(&(codec)->core)
ALSA: hda - Work around races of power up/down with runtime PM Currently, snd_hdac_power_up()/down() helpers checks whether the codec is being in pm (suspend/resume), and skips the call of runtime get/put during it. This is needed as there are lots of power up/down sequences called in the paths that are also used in the PM itself. An example is found in hda_codec.c::codec_exec_verb(), where this can power up the codec while it may be called again in its power up sequence, too. The above works in most cases, but sometimes we really want to wait for the real power up. For example, the control element get/put may want explicit power up so that the value change is assured to reach to the hardware. Using the current snd_hdac_power_up(), however, results in a race, e.g. when it's called during the runtime suspend is being performed. In the worst case, as found in patch_ca0132.c, it can even lead to the deadlock because the code assumes the power up while it was skipped due to the check above. For dealing with such cases, this patch makes snd_hdac_power_up() and _down() to two variants: with and without in_pm flag check. The version with pm flag check is named as snd_hdac_power_up_pm() while the version without pm flag check is still kept as snd_hdac_power_up(). (Just because the usage of the former is fewer.) Then finally, the patch replaces each call potentially done in PM with the new _pm() variant. In theory, we can implement a unified version -- if we can distinguish the current context whether it's in the pm path. But such an implementation is cumbersome, so leave the code like this a bit messy way for now... Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=96271 Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-04-08 16:43:14 +07:00
#define snd_hda_power_down_pm(codec) snd_hdac_power_down_pm(&(codec)->core)
#ifdef CONFIG_PM
void snd_hda_set_power_save(struct hda_bus *bus, int delay);
void snd_hda_update_power_acct(struct hda_codec *codec);
#else
static inline void snd_hda_set_power_save(struct hda_bus *bus, int delay) {}
#endif
#ifdef CONFIG_SND_HDA_PATCH_LOADER
/*
* patch firmware
*/
int snd_hda_load_patch(struct hda_bus *bus, size_t size, const void *buf);
#endif
#ifdef CONFIG_SND_HDA_DSP_LOADER
static inline int
snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
unsigned int size,
struct snd_dma_buffer *bufp)
{
return codec->bus->ops.load_dsp_prepare(codec->bus, format, size, bufp);
}
static inline void
snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start)
{
return codec->bus->ops.load_dsp_trigger(codec->bus, start);
}
static inline void
snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
struct snd_dma_buffer *dmab)
{
return codec->bus->ops.load_dsp_cleanup(codec->bus, dmab);
}
#else
static inline int
snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
unsigned int size,
struct snd_dma_buffer *bufp)
{
return -ENOSYS;
}
static inline void
snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start) {}
static inline void
snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
struct snd_dma_buffer *dmab) {}
#endif
#endif /* __SOUND_HDA_CODEC_H */