summaryrefslogtreecommitdiff
path: root/include/sound/hdaudio.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/sound/hdaudio.h')
-rw-r--r--include/sound/hdaudio.h144
1 files changed, 120 insertions, 24 deletions
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h
index c1f78d9a6e47..4e0c1d8af09f 100644
--- a/include/sound/hdaudio.h
+++ b/include/sound/hdaudio.h
@@ -9,13 +9,16 @@
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/iopoll.h>
+#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/timecounter.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/memalloc.h>
#include <sound/hda_verbs.h>
-#include <drm/i915_component.h>
+#include <drm/intel/i915_component.h>
/* codec node id */
typedef u16 hda_nid_t;
@@ -30,7 +33,7 @@ struct hda_device_id;
/*
* exported bus type
*/
-extern struct bus_type snd_hda_bus_type;
+extern const struct bus_type snd_hda_bus_type;
/*
* generic arrays
@@ -92,6 +95,7 @@ struct hdac_device {
bool lazy_cache:1; /* don't wake up for writes */
bool caps_overwriting:1; /* caps overwrite being in process */
bool cache_coef:1; /* cache COEF read/write too */
+ unsigned int registered:1; /* codec was registered */
};
/* device/driver type used for matching */
@@ -120,7 +124,7 @@ void snd_hdac_device_exit(struct hdac_device *dev);
int snd_hdac_device_register(struct hdac_device *codec);
void snd_hdac_device_unregister(struct hdac_device *codec);
int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name);
-int snd_hdac_codec_modalias(struct hdac_device *hdac, char *buf, size_t size);
+int snd_hdac_codec_modalias(const struct hdac_device *hdac, char *buf, size_t size);
int snd_hdac_refresh_widgets(struct hdac_device *codec);
@@ -136,13 +140,14 @@ int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns);
int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid,
hda_nid_t *start_id);
-unsigned int snd_hdac_calc_stream_format(unsigned int rate,
- unsigned int channels,
- snd_pcm_format_t format,
- unsigned int maxbps,
- unsigned short spdif_ctls);
+unsigned int snd_hdac_stream_format_bits(snd_pcm_format_t format, snd_pcm_subformat_t subformat,
+ unsigned int maxbits);
+unsigned int snd_hdac_stream_format(unsigned int channels, unsigned int bits, unsigned int rate);
+unsigned int snd_hdac_spdif_stream_format(unsigned int channels, unsigned int bits,
+ unsigned int rate, unsigned short spdif_ctls);
int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
- u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
+ u32 *ratesp, u64 *formatsp, u32 *subformatsp,
+ unsigned int *bpsp);
bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid,
unsigned int format);
@@ -218,7 +223,7 @@ struct hdac_driver {
struct device_driver driver;
int type;
const struct hda_device_id *id_table;
- int (*match)(struct hdac_device *dev, struct hdac_driver *drv);
+ int (*match)(struct hdac_device *dev, const struct hdac_driver *drv);
void (*unsol_event)(struct hdac_device *dev, unsigned int event);
/* fields used by ext bus APIs */
@@ -230,7 +235,7 @@ struct hdac_driver {
#define drv_to_hdac_driver(_drv) container_of(_drv, struct hdac_driver, driver)
const struct hda_device_id *
-hdac_get_device_id(struct hdac_device *hdev, struct hdac_driver *drv);
+hdac_get_device_id(struct hdac_device *hdev, const struct hdac_driver *drv);
/*
* Bus verb operators
@@ -241,6 +246,8 @@ struct hdac_bus_ops {
/* get a response from the last command */
int (*get_response)(struct hdac_bus *bus, unsigned int addr,
unsigned int *res);
+ /* notify of codec link power-up/down */
+ void (*link_power)(struct hdac_device *hdev, bool enable);
};
/*
@@ -342,11 +349,17 @@ struct hdac_bus {
bool corbrp_self_clear:1; /* CORBRP clears itself after reset */
bool polling_mode:1;
bool needs_damn_long_delay:1;
+ bool not_use_interrupts:1; /* prohibiting the RIRB IRQ */
+ bool access_sdnctl_in_dword:1; /* accessing the sdnctl register by dword */
+ bool use_pio_for_commands:1; /* Use PIO instead of CORB for commands */
int poll_count;
int bdl_pos_adj; /* BDL position adjustment */
+ /* delay time in us for dma stop */
+ unsigned int dma_stop_delay;
+
/* locks */
spinlock_t reg_lock;
struct mutex cmd_mutex;
@@ -375,15 +388,8 @@ void snd_hdac_bus_exit(struct hdac_bus *bus);
int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr,
unsigned int cmd, unsigned int *res);
-static inline void snd_hdac_codec_link_up(struct hdac_device *codec)
-{
- set_bit(codec->addr, &codec->bus->codec_powered);
-}
-
-static inline void snd_hdac_codec_link_down(struct hdac_device *codec)
-{
- clear_bit(codec->addr, &codec->bus->codec_powered);
-}
+void snd_hdac_codec_link_up(struct hdac_device *codec);
+void snd_hdac_codec_link_down(struct hdac_device *codec);
int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val);
int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
@@ -397,6 +403,7 @@ void snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus);
void snd_hdac_bus_enter_link_reset(struct hdac_bus *bus);
void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus);
int snd_hdac_bus_reset_link(struct hdac_bus *bus, bool full_reset);
+void snd_hdac_bus_link_power(struct hdac_device *hdev, bool enable);
void snd_hdac_bus_update_rirb(struct hdac_bus *bus);
int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
@@ -449,6 +456,8 @@ static inline u16 snd_hdac_reg_readw(struct hdac_bus *bus, void __iomem *addr)
#define snd_hdac_reg_writel(bus, addr, val) writel(val, addr)
#define snd_hdac_reg_readl(bus, addr) readl(addr)
+#define snd_hdac_reg_writeq(bus, addr, val) writeq(val, addr)
+#define snd_hdac_reg_readq(bus, addr) readq(addr)
/*
* macros for easy use
@@ -491,6 +500,13 @@ static inline u16 snd_hdac_reg_readw(struct hdac_bus *bus, void __iomem *addr)
snd_hdac_chip_writeb(chip, reg, \
(snd_hdac_chip_readb(chip, reg) & ~(mask)) | (val))
+/* update register macro */
+#define snd_hdac_updatel(addr, reg, mask, val) \
+ writel(((readl(addr + reg) & ~(mask)) | (val)), addr + reg)
+
+#define snd_hdac_updatew(addr, reg, mask, val) \
+ writew(((readw(addr + reg) & ~(mask)) | (val)), addr + reg)
+
/*
* HD-audio stream
*/
@@ -507,6 +523,13 @@ struct hdac_stream {
void __iomem *sd_addr; /* stream descriptor pointer */
+ void __iomem *spib_addr; /* software position in buffers stream pointer */
+ void __iomem *fifo_addr; /* software position Max fifos stream pointer */
+
+ void __iomem *dpibr_addr; /* DMA position in buffer resume pointer */
+ u32 dpib; /* DMA position in buffer */
+ u32 lpib; /* Linear position in buffer */
+
u32 sd_int_sta_mask; /* stream int status mask */
/* pcm support */
@@ -547,28 +570,41 @@ void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev,
int idx, int direction, int tag);
struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus,
struct snd_pcm_substream *substream);
+void snd_hdac_stream_release_locked(struct hdac_stream *azx_dev);
void snd_hdac_stream_release(struct hdac_stream *azx_dev);
struct hdac_stream *snd_hdac_get_stream(struct hdac_bus *bus,
int dir, int stream_tag);
-int snd_hdac_stream_setup(struct hdac_stream *azx_dev);
+int snd_hdac_stream_setup(struct hdac_stream *azx_dev, bool code_loading);
void snd_hdac_stream_cleanup(struct hdac_stream *azx_dev);
int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev);
int snd_hdac_stream_set_params(struct hdac_stream *azx_dev,
unsigned int format_val);
-void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start);
-void snd_hdac_stream_clear(struct hdac_stream *azx_dev);
+void snd_hdac_stream_start(struct hdac_stream *azx_dev);
void snd_hdac_stream_stop(struct hdac_stream *azx_dev);
+void snd_hdac_stop_streams(struct hdac_bus *bus);
+void snd_hdac_stop_streams_and_chip(struct hdac_bus *bus);
void snd_hdac_stream_reset(struct hdac_stream *azx_dev);
void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set,
unsigned int streams, unsigned int reg);
void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start,
unsigned int streams);
void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev,
- unsigned int streams);
+ unsigned int streams, bool start);
int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
struct snd_pcm_substream *substream);
+void snd_hdac_stream_spbcap_enable(struct hdac_bus *chip,
+ bool enable, int index);
+int snd_hdac_stream_set_spib(struct hdac_bus *bus,
+ struct hdac_stream *azx_dev, u32 value);
+void snd_hdac_stream_drsm_enable(struct hdac_bus *bus,
+ bool enable, int index);
+int snd_hdac_stream_wait_drsm(struct hdac_stream *azx_dev);
+int snd_hdac_stream_set_dpibr(struct hdac_bus *bus,
+ struct hdac_stream *azx_dev, u32 value);
+int snd_hdac_stream_set_lpib(struct hdac_stream *azx_dev, u32 value);
+
/*
* macros for easy use
*/
@@ -585,6 +621,15 @@ int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
snd_hdac_reg_readw((dev)->bus, (dev)->sd_addr + AZX_REG_ ## reg)
#define snd_hdac_stream_readb(dev, reg) \
snd_hdac_reg_readb((dev)->bus, (dev)->sd_addr + AZX_REG_ ## reg)
+#define snd_hdac_stream_readb_poll(dev, reg, val, cond, delay_us, timeout_us) \
+ read_poll_timeout_atomic(snd_hdac_reg_readb, val, cond, delay_us, timeout_us, \
+ false, (dev)->bus, (dev)->sd_addr + AZX_REG_ ## reg)
+#define snd_hdac_stream_readw_poll(dev, reg, val, cond, delay_us, timeout_us) \
+ read_poll_timeout_atomic(snd_hdac_reg_readw, val, cond, delay_us, timeout_us, \
+ false, (dev)->bus, (dev)->sd_addr + AZX_REG_ ## reg)
+#define snd_hdac_stream_readl_poll(dev, reg, val, cond, delay_us, timeout_us) \
+ read_poll_timeout_atomic(snd_hdac_reg_readl, val, cond, delay_us, timeout_us, \
+ false, (dev)->bus, (dev)->sd_addr + AZX_REG_ ## reg)
/* update a register, pass without AZX_REG_ prefix */
#define snd_hdac_stream_updatel(dev, reg, mask, val) \
@@ -606,6 +651,7 @@ int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
#define snd_hdac_dsp_lock(dev) mutex_lock(&(dev)->dsp_mutex)
#define snd_hdac_dsp_unlock(dev) mutex_unlock(&(dev)->dsp_mutex)
#define snd_hdac_stream_is_locked(dev) ((dev)->locked)
+DEFINE_GUARD(snd_hdac_dsp_lock, struct hdac_stream *, snd_hdac_dsp_lock(_T), snd_hdac_dsp_unlock(_T))
/* DSP loader helpers */
int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
unsigned int byte_size, struct snd_dma_buffer *bufp);
@@ -635,6 +681,30 @@ static inline void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev,
}
#endif /* CONFIG_SND_HDA_DSP_LOADER */
+/*
+ * Easy macros for widget capabilities
+ */
+#define snd_hdac_get_wcaps(codec, nid) \
+ snd_hdac_read_parm(codec, nid, AC_PAR_AUDIO_WIDGET_CAP)
+
+/* get the widget type from widget capability bits */
+static inline int snd_hdac_get_wcaps_type(unsigned int wcaps)
+{
+ if (!wcaps)
+ return -1; /* invalid type */
+ return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+}
+
+/* get the number of supported channels */
+static inline unsigned int snd_hdac_get_wcaps_channels(u32 wcaps)
+{
+ unsigned int chans;
+
+ chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13;
+ chans = (chans + 1) * 2;
+
+ return chans;
+}
/*
* generic array helpers
@@ -663,4 +733,30 @@ static inline unsigned int snd_array_index(struct snd_array *array, void *ptr)
for ((idx) = 0, (ptr) = (array)->list; (idx) < (array)->used; \
(ptr) = snd_array_elem(array, ++(idx)))
+/*
+ * Device matching
+ */
+
+#define HDA_CONTROLLER_IS_HSW(pci) (pci_match_id((struct pci_device_id []){ \
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HDA_HSW_0) }, \
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HDA_HSW_2) }, \
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HDA_HSW_3) }, \
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HDA_BDW) }, \
+ { } \
+ }, pci))
+
+#define HDA_CONTROLLER_IS_APL(pci) (pci_match_id((struct pci_device_id []){ \
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HDA_APL) }, \
+ { } \
+ }, pci))
+
+#define HDA_CONTROLLER_IN_GPU(pci) (pci_match_id((struct pci_device_id []){ \
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HDA_DG1) }, \
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HDA_DG2_0) }, \
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HDA_DG2_1) }, \
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HDA_DG2_2) }, \
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HDA_BMG) }, \
+ { } \
+ }, pci) || HDA_CONTROLLER_IS_HSW(pci))
+
#endif /* __SOUND_HDAUDIO_H */