forked from Minki/linux
Merge branch 'topic/hda-link-time' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound into asoc-intel
This commit is contained in:
commit
da74273c49
@ -89,6 +89,19 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
|
||||
#define AZX_REG_SD_BDLPL 0x18
|
||||
#define AZX_REG_SD_BDLPU 0x1c
|
||||
|
||||
/* GTS registers */
|
||||
#define AZX_REG_LLCH 0x14
|
||||
|
||||
#define AZX_REG_GTS_BASE 0x520
|
||||
|
||||
#define AZX_REG_GTSCC (AZX_REG_GTS_BASE + 0x00)
|
||||
#define AZX_REG_WALFCC (AZX_REG_GTS_BASE + 0x04)
|
||||
#define AZX_REG_TSCCL (AZX_REG_GTS_BASE + 0x08)
|
||||
#define AZX_REG_TSCCU (AZX_REG_GTS_BASE + 0x0C)
|
||||
#define AZX_REG_LLPFOC (AZX_REG_GTS_BASE + 0x14)
|
||||
#define AZX_REG_LLPCL (AZX_REG_GTS_BASE + 0x18)
|
||||
#define AZX_REG_LLPCU (AZX_REG_GTS_BASE + 0x1C)
|
||||
|
||||
/* Haswell/Broadwell display HD-A controller Extended Mode registers */
|
||||
#define AZX_REG_HSW_EM4 0x100c
|
||||
#define AZX_REG_HSW_EM5 0x1010
|
||||
@ -242,6 +255,29 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
|
||||
/* Interval used to calculate the iterating register offset */
|
||||
#define AZX_DRSM_INTERVAL 0x08
|
||||
|
||||
/* Global time synchronization registers */
|
||||
#define GTSCC_TSCCD_MASK 0x80000000
|
||||
#define GTSCC_TSCCD_SHIFT BIT(31)
|
||||
#define GTSCC_TSCCI_MASK 0x20
|
||||
#define GTSCC_CDMAS_DMA_DIR_SHIFT 4
|
||||
|
||||
#define WALFCC_CIF_MASK 0x1FF
|
||||
#define WALFCC_FN_SHIFT 9
|
||||
#define HDA_CLK_CYCLES_PER_FRAME 512
|
||||
|
||||
/*
|
||||
* An error occurs near frame "rollover". The clocks in frame value indicates
|
||||
* whether this error may have occurred. Here we use the value of 10. Please
|
||||
* see the errata for the right number [<10]
|
||||
*/
|
||||
#define HDA_MAX_CYCLE_VALUE 499
|
||||
#define HDA_MAX_CYCLE_OFFSET 10
|
||||
#define HDA_MAX_CYCLE_READ_RETRY 10
|
||||
|
||||
#define TSCCU_CCU_SHIFT 32
|
||||
#define LLPC_CCU_SHIFT 32
|
||||
|
||||
|
||||
/*
|
||||
* helpers to read the stream position
|
||||
*/
|
||||
|
@ -245,6 +245,12 @@ struct hdac_rb {
|
||||
|
||||
/*
|
||||
* HD-audio bus base driver
|
||||
*
|
||||
* @ppcap: pp capabilities pointer
|
||||
* @spbcap: SPIB capabilities pointer
|
||||
* @mlcap: MultiLink capabilities pointer
|
||||
* @gtscap: gts capabilities pointer
|
||||
* @drsmcap: dma resume capabilities pointer
|
||||
*/
|
||||
struct hdac_bus {
|
||||
struct device *dev;
|
||||
@ -256,6 +262,12 @@ struct hdac_bus {
|
||||
void __iomem *remap_addr;
|
||||
int irq;
|
||||
|
||||
void __iomem *ppcap;
|
||||
void __iomem *spbcap;
|
||||
void __iomem *mlcap;
|
||||
void __iomem *gtscap;
|
||||
void __iomem *drsmcap;
|
||||
|
||||
/* codec linked list */
|
||||
struct list_head codec_list;
|
||||
unsigned int num_codecs;
|
||||
@ -335,6 +347,7 @@ static inline 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,
|
||||
unsigned int *res);
|
||||
int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus);
|
||||
int snd_hdac_link_power(struct hdac_device *codec, bool enable);
|
||||
|
||||
bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset);
|
||||
|
@ -8,11 +8,6 @@
|
||||
*
|
||||
* @bus: hdac bus
|
||||
* @num_streams: streams supported
|
||||
* @ppcap: pp capabilities pointer
|
||||
* @spbcap: SPIB capabilities pointer
|
||||
* @mlcap: MultiLink capabilities pointer
|
||||
* @gtscap: gts capabilities pointer
|
||||
* @drsmcap: dma resume capabilities pointer
|
||||
* @hlink_list: link list of HDA links
|
||||
* @lock: lock for link mgmt
|
||||
* @cmd_dma_state: state of cmd DMAs: CORB and RIRB
|
||||
@ -22,12 +17,6 @@ struct hdac_ext_bus {
|
||||
int num_streams;
|
||||
int idx;
|
||||
|
||||
void __iomem *ppcap;
|
||||
void __iomem *spbcap;
|
||||
void __iomem *mlcap;
|
||||
void __iomem *gtscap;
|
||||
void __iomem *drsmcap;
|
||||
|
||||
struct list_head hlink_list;
|
||||
|
||||
struct mutex lock;
|
||||
@ -54,7 +43,6 @@ void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus);
|
||||
#define HDA_CODEC_EXT_ENTRY(_vid, _revid, _name, _drv_data) \
|
||||
HDA_CODEC_REV_EXT_ENTRY(_vid, _revid, _name, _drv_data)
|
||||
|
||||
int snd_hdac_ext_bus_parse_capabilities(struct hdac_ext_bus *sbus);
|
||||
void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *chip, bool enable);
|
||||
void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *chip, bool enable);
|
||||
|
||||
|
@ -29,81 +29,6 @@
|
||||
*/
|
||||
#define HDAC_MAX_CAPS 10
|
||||
|
||||
/**
|
||||
* snd_hdac_ext_bus_parse_capabilities - parse capablity structure
|
||||
* @ebus: the pointer to extended bus object
|
||||
*
|
||||
* Returns 0 if successful, or a negative error code.
|
||||
*/
|
||||
int snd_hdac_ext_bus_parse_capabilities(struct hdac_ext_bus *ebus)
|
||||
{
|
||||
unsigned int cur_cap;
|
||||
unsigned int offset;
|
||||
struct hdac_bus *bus = &ebus->bus;
|
||||
unsigned int counter = 0;
|
||||
|
||||
offset = snd_hdac_chip_readl(bus, LLCH);
|
||||
|
||||
/* Lets walk the linked capabilities list */
|
||||
do {
|
||||
cur_cap = _snd_hdac_chip_read(l, bus, offset);
|
||||
|
||||
dev_dbg(bus->dev, "Capability version: 0x%x\n",
|
||||
((cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF));
|
||||
|
||||
dev_dbg(bus->dev, "HDA capability ID: 0x%x\n",
|
||||
(cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF);
|
||||
|
||||
switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) {
|
||||
case AZX_ML_CAP_ID:
|
||||
dev_dbg(bus->dev, "Found ML capability\n");
|
||||
ebus->mlcap = bus->remap_addr + offset;
|
||||
break;
|
||||
|
||||
case AZX_GTS_CAP_ID:
|
||||
dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset);
|
||||
ebus->gtscap = bus->remap_addr + offset;
|
||||
break;
|
||||
|
||||
case AZX_PP_CAP_ID:
|
||||
/* PP capability found, the Audio DSP is present */
|
||||
dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset);
|
||||
ebus->ppcap = bus->remap_addr + offset;
|
||||
break;
|
||||
|
||||
case AZX_SPB_CAP_ID:
|
||||
/* SPIB capability found, handler function */
|
||||
dev_dbg(bus->dev, "Found SPB capability\n");
|
||||
ebus->spbcap = bus->remap_addr + offset;
|
||||
break;
|
||||
|
||||
case AZX_DRSM_CAP_ID:
|
||||
/* DMA resume capability found, handler function */
|
||||
dev_dbg(bus->dev, "Found DRSM capability\n");
|
||||
ebus->drsmcap = bus->remap_addr + offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_dbg(bus->dev, "Unknown capability %d\n", cur_cap);
|
||||
break;
|
||||
}
|
||||
|
||||
counter++;
|
||||
|
||||
if (counter > HDAC_MAX_CAPS) {
|
||||
dev_err(bus->dev, "We exceeded HDAC Ext capablities!!!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* read the offset of next capabiity */
|
||||
offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK;
|
||||
|
||||
} while (offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_parse_capabilities);
|
||||
|
||||
/*
|
||||
* processing pipe helpers - these helpers are useful for dealing with HDA
|
||||
* new capability of processing pipelines
|
||||
@ -118,15 +43,15 @@ void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *ebus, bool enable)
|
||||
{
|
||||
struct hdac_bus *bus = &ebus->bus;
|
||||
|
||||
if (!ebus->ppcap) {
|
||||
if (!bus->ppcap) {
|
||||
dev_err(bus->dev, "Address of PP capability is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_GPROCEN);
|
||||
snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_GPROCEN);
|
||||
else
|
||||
snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_GPROCEN, 0);
|
||||
snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_GPROCEN, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable);
|
||||
|
||||
@ -139,15 +64,15 @@ void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *ebus, bool enable)
|
||||
{
|
||||
struct hdac_bus *bus = &ebus->bus;
|
||||
|
||||
if (!ebus->ppcap) {
|
||||
if (!bus->ppcap) {
|
||||
dev_err(bus->dev, "Address of PP capability is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_PIE);
|
||||
snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_PIE);
|
||||
else
|
||||
snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_PIE, 0);
|
||||
snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_PIE, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable);
|
||||
|
||||
@ -171,7 +96,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
|
||||
struct hdac_ext_link *hlink;
|
||||
struct hdac_bus *bus = &ebus->bus;
|
||||
|
||||
link_count = readl(ebus->mlcap + AZX_REG_ML_MLCD) + 1;
|
||||
link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
|
||||
|
||||
dev_dbg(bus->dev, "In %s Link count: %d\n", __func__, link_count);
|
||||
|
||||
@ -181,7 +106,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
|
||||
return -ENOMEM;
|
||||
hlink->index = idx;
|
||||
hlink->bus = bus;
|
||||
hlink->ml_addr = ebus->mlcap + AZX_ML_BASE +
|
||||
hlink->ml_addr = bus->mlcap + AZX_ML_BASE +
|
||||
(AZX_ML_INTERVAL * idx);
|
||||
hlink->lcaps = readl(hlink->ml_addr + AZX_REG_ML_LCAP);
|
||||
hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID);
|
||||
|
@ -40,27 +40,27 @@ void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus,
|
||||
{
|
||||
struct hdac_bus *bus = &ebus->bus;
|
||||
|
||||
if (ebus->ppcap) {
|
||||
stream->pphc_addr = ebus->ppcap + AZX_PPHC_BASE +
|
||||
if (bus->ppcap) {
|
||||
stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
|
||||
AZX_PPHC_INTERVAL * idx;
|
||||
|
||||
stream->pplc_addr = ebus->ppcap + AZX_PPLC_BASE +
|
||||
stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE +
|
||||
AZX_PPLC_MULTI * ebus->num_streams +
|
||||
AZX_PPLC_INTERVAL * idx;
|
||||
}
|
||||
|
||||
if (ebus->spbcap) {
|
||||
stream->spib_addr = ebus->spbcap + AZX_SPB_BASE +
|
||||
if (bus->spbcap) {
|
||||
stream->spib_addr = bus->spbcap + AZX_SPB_BASE +
|
||||
AZX_SPB_INTERVAL * idx +
|
||||
AZX_SPB_SPIB;
|
||||
|
||||
stream->fifo_addr = ebus->spbcap + AZX_SPB_BASE +
|
||||
stream->fifo_addr = bus->spbcap + AZX_SPB_BASE +
|
||||
AZX_SPB_INTERVAL * idx +
|
||||
AZX_SPB_MAXFIFO;
|
||||
}
|
||||
|
||||
if (ebus->drsmcap)
|
||||
stream->dpibr_addr = ebus->drsmcap + AZX_DRSM_BASE +
|
||||
if (bus->drsmcap)
|
||||
stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE +
|
||||
AZX_DRSM_INTERVAL * idx;
|
||||
|
||||
stream->decoupled = false;
|
||||
@ -131,10 +131,10 @@ void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus,
|
||||
|
||||
spin_lock_irq(&bus->reg_lock);
|
||||
if (decouple)
|
||||
snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0,
|
||||
snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0,
|
||||
AZX_PPCTL_PROCEN(hstream->index));
|
||||
else
|
||||
snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL,
|
||||
snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
|
||||
AZX_PPCTL_PROCEN(hstream->index), 0);
|
||||
stream->decoupled = decouple;
|
||||
spin_unlock_irq(&bus->reg_lock);
|
||||
@ -255,7 +255,7 @@ hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus,
|
||||
struct hdac_stream *stream = NULL;
|
||||
struct hdac_bus *hbus = &ebus->bus;
|
||||
|
||||
if (!ebus->ppcap) {
|
||||
if (!hbus->ppcap) {
|
||||
dev_err(hbus->dev, "stream type not supported\n");
|
||||
return NULL;
|
||||
}
|
||||
@ -296,7 +296,7 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus,
|
||||
struct hdac_stream *stream = NULL;
|
||||
struct hdac_bus *hbus = &ebus->bus;
|
||||
|
||||
if (!ebus->ppcap) {
|
||||
if (!hbus->ppcap) {
|
||||
dev_err(hbus->dev, "stream type not supported\n");
|
||||
return NULL;
|
||||
}
|
||||
@ -423,21 +423,21 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus,
|
||||
u32 register_mask = 0;
|
||||
struct hdac_bus *bus = &ebus->bus;
|
||||
|
||||
if (!ebus->spbcap) {
|
||||
if (!bus->spbcap) {
|
||||
dev_err(bus->dev, "Address of SPB capability is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
mask |= (1 << index);
|
||||
|
||||
register_mask = readl(ebus->spbcap + AZX_REG_SPB_SPBFCCTL);
|
||||
register_mask = readl(bus->spbcap + AZX_REG_SPB_SPBFCCTL);
|
||||
|
||||
mask |= register_mask;
|
||||
|
||||
if (enable)
|
||||
snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, 0, mask);
|
||||
snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, 0, mask);
|
||||
else
|
||||
snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0);
|
||||
snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable);
|
||||
|
||||
@ -452,7 +452,7 @@ int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus,
|
||||
{
|
||||
struct hdac_bus *bus = &ebus->bus;
|
||||
|
||||
if (!ebus->spbcap) {
|
||||
if (!bus->spbcap) {
|
||||
dev_err(bus->dev, "Address of SPB capability is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -475,7 +475,7 @@ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus,
|
||||
{
|
||||
struct hdac_bus *bus = &ebus->bus;
|
||||
|
||||
if (!ebus->spbcap) {
|
||||
if (!bus->spbcap) {
|
||||
dev_err(bus->dev, "Address of SPB capability is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -515,21 +515,21 @@ void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus,
|
||||
u32 register_mask = 0;
|
||||
struct hdac_bus *bus = &ebus->bus;
|
||||
|
||||
if (!ebus->drsmcap) {
|
||||
if (!bus->drsmcap) {
|
||||
dev_err(bus->dev, "Address of DRSM capability is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
mask |= (1 << index);
|
||||
|
||||
register_mask = readl(ebus->drsmcap + AZX_REG_SPB_SPBFCCTL);
|
||||
register_mask = readl(bus->drsmcap + AZX_REG_SPB_SPBFCCTL);
|
||||
|
||||
mask |= register_mask;
|
||||
|
||||
if (enable)
|
||||
snd_hdac_updatel(ebus->drsmcap, AZX_REG_DRSM_CTL, 0, mask);
|
||||
snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, 0, mask);
|
||||
else
|
||||
snd_hdac_updatel(ebus->drsmcap, AZX_REG_DRSM_CTL, mask, 0);
|
||||
snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable);
|
||||
|
||||
@ -544,7 +544,7 @@ int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus,
|
||||
{
|
||||
struct hdac_bus *bus = &ebus->bus;
|
||||
|
||||
if (!ebus->drsmcap) {
|
||||
if (!bus->drsmcap) {
|
||||
dev_err(bus->dev, "Address of DRSM capability is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -255,6 +255,81 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response);
|
||||
|
||||
#define HDAC_MAX_CAPS 10
|
||||
/**
|
||||
* snd_hdac_bus_parse_capabilities - parse capability structure
|
||||
* @bus: the pointer to bus object
|
||||
*
|
||||
* Returns 0 if successful, or a negative error code.
|
||||
*/
|
||||
int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus)
|
||||
{
|
||||
unsigned int cur_cap;
|
||||
unsigned int offset;
|
||||
unsigned int counter = 0;
|
||||
|
||||
offset = snd_hdac_chip_readl(bus, LLCH);
|
||||
|
||||
/* Lets walk the linked capabilities list */
|
||||
do {
|
||||
cur_cap = _snd_hdac_chip_read(l, bus, offset);
|
||||
|
||||
dev_dbg(bus->dev, "Capability version: 0x%x\n",
|
||||
(cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF);
|
||||
|
||||
dev_dbg(bus->dev, "HDA capability ID: 0x%x\n",
|
||||
(cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF);
|
||||
|
||||
switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) {
|
||||
case AZX_ML_CAP_ID:
|
||||
dev_dbg(bus->dev, "Found ML capability\n");
|
||||
bus->mlcap = bus->remap_addr + offset;
|
||||
break;
|
||||
|
||||
case AZX_GTS_CAP_ID:
|
||||
dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset);
|
||||
bus->gtscap = bus->remap_addr + offset;
|
||||
break;
|
||||
|
||||
case AZX_PP_CAP_ID:
|
||||
/* PP capability found, the Audio DSP is present */
|
||||
dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset);
|
||||
bus->ppcap = bus->remap_addr + offset;
|
||||
break;
|
||||
|
||||
case AZX_SPB_CAP_ID:
|
||||
/* SPIB capability found, handler function */
|
||||
dev_dbg(bus->dev, "Found SPB capability\n");
|
||||
bus->spbcap = bus->remap_addr + offset;
|
||||
break;
|
||||
|
||||
case AZX_DRSM_CAP_ID:
|
||||
/* DMA resume capability found, handler function */
|
||||
dev_dbg(bus->dev, "Found DRSM capability\n");
|
||||
bus->drsmcap = bus->remap_addr + offset;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_dbg(bus->dev, "Unknown capability %d\n", cur_cap);
|
||||
break;
|
||||
}
|
||||
|
||||
counter++;
|
||||
|
||||
if (counter > HDAC_MAX_CAPS) {
|
||||
dev_err(bus->dev, "We exceeded HDAC capabilities!!!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* read the offset of next capability */
|
||||
offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK;
|
||||
|
||||
} while (offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_bus_parse_capabilities);
|
||||
|
||||
/*
|
||||
* Lowlevel interface
|
||||
*/
|
||||
|
@ -27,6 +27,12 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
/* for art-tsc conversion */
|
||||
#include <asm/tsc.h>
|
||||
#endif
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include "hda_controller.h"
|
||||
@ -337,12 +343,173 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
azx_get_position(chip, azx_dev));
|
||||
}
|
||||
|
||||
/*
|
||||
* azx_scale64: Scale base by mult/div while not overflowing sanely
|
||||
*
|
||||
* Derived from scale64_check_overflow in kernel/time/timekeeping.c
|
||||
*
|
||||
* The tmestamps for a 48Khz stream can overflow after (2^64/10^9)/48K which
|
||||
* is about 384307 ie ~4.5 days.
|
||||
*
|
||||
* This scales the calculation so that overflow will happen but after 2^64 /
|
||||
* 48000 secs, which is pretty large!
|
||||
*
|
||||
* In caln below:
|
||||
* base may overflow, but since there isn’t any additional division
|
||||
* performed on base it’s OK
|
||||
* rem can’t overflow because both are 32-bit values
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
static u64 azx_scale64(u64 base, u32 num, u32 den)
|
||||
{
|
||||
u64 rem;
|
||||
|
||||
rem = do_div(base, den);
|
||||
|
||||
base *= num;
|
||||
rem *= num;
|
||||
|
||||
do_div(rem, den);
|
||||
|
||||
return base + rem;
|
||||
}
|
||||
|
||||
static int azx_get_sync_time(ktime_t *device,
|
||||
struct system_counterval_t *system, void *ctx)
|
||||
{
|
||||
struct snd_pcm_substream *substream = ctx;
|
||||
struct azx_dev *azx_dev = get_azx_dev(substream);
|
||||
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
|
||||
struct azx *chip = apcm->chip;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
u64 ll_counter, ll_counter_l, ll_counter_h;
|
||||
u64 tsc_counter, tsc_counter_l, tsc_counter_h;
|
||||
u32 wallclk_ctr, wallclk_cycles;
|
||||
bool direction;
|
||||
u32 dma_select;
|
||||
u32 timeout = 200;
|
||||
u32 retry_count = 0;
|
||||
|
||||
runtime = substream->runtime;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
direction = 1;
|
||||
else
|
||||
direction = 0;
|
||||
|
||||
/* 0th stream tag is not used, so DMA ch 0 is for 1st stream tag */
|
||||
do {
|
||||
timeout = 100;
|
||||
dma_select = (direction << GTSCC_CDMAS_DMA_DIR_SHIFT) |
|
||||
(azx_dev->core.stream_tag - 1);
|
||||
snd_hdac_chip_writel(azx_bus(chip), GTSCC, dma_select);
|
||||
|
||||
/* Enable the capture */
|
||||
snd_hdac_chip_updatel(azx_bus(chip), GTSCC, 0, GTSCC_TSCCI_MASK);
|
||||
|
||||
while (timeout) {
|
||||
if (snd_hdac_chip_readl(azx_bus(chip), GTSCC) &
|
||||
GTSCC_TSCCD_MASK)
|
||||
break;
|
||||
|
||||
timeout--;
|
||||
}
|
||||
|
||||
if (!timeout) {
|
||||
dev_err(chip->card->dev, "GTSCC capture Timedout!\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Read wall clock counter */
|
||||
wallclk_ctr = snd_hdac_chip_readl(azx_bus(chip), WALFCC);
|
||||
|
||||
/* Read TSC counter */
|
||||
tsc_counter_l = snd_hdac_chip_readl(azx_bus(chip), TSCCL);
|
||||
tsc_counter_h = snd_hdac_chip_readl(azx_bus(chip), TSCCU);
|
||||
|
||||
/* Read Link counter */
|
||||
ll_counter_l = snd_hdac_chip_readl(azx_bus(chip), LLPCL);
|
||||
ll_counter_h = snd_hdac_chip_readl(azx_bus(chip), LLPCU);
|
||||
|
||||
/* Ack: registers read done */
|
||||
snd_hdac_chip_writel(azx_bus(chip), GTSCC, GTSCC_TSCCD_SHIFT);
|
||||
|
||||
tsc_counter = (tsc_counter_h << TSCCU_CCU_SHIFT) |
|
||||
tsc_counter_l;
|
||||
|
||||
ll_counter = (ll_counter_h << LLPC_CCU_SHIFT) | ll_counter_l;
|
||||
wallclk_cycles = wallclk_ctr & WALFCC_CIF_MASK;
|
||||
|
||||
/*
|
||||
* An error occurs near frame "rollover". The clocks in
|
||||
* frame value indicates whether this error may have
|
||||
* occurred. Here we use the value of 10 i.e.,
|
||||
* HDA_MAX_CYCLE_OFFSET
|
||||
*/
|
||||
if (wallclk_cycles < HDA_MAX_CYCLE_VALUE - HDA_MAX_CYCLE_OFFSET
|
||||
&& wallclk_cycles > HDA_MAX_CYCLE_OFFSET)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Sleep before we read again, else we may again get
|
||||
* value near to MAX_CYCLE. Try to sleep for different
|
||||
* amount of time so we dont hit the same number again
|
||||
*/
|
||||
udelay(retry_count++);
|
||||
|
||||
} while (retry_count != HDA_MAX_CYCLE_READ_RETRY);
|
||||
|
||||
if (retry_count == HDA_MAX_CYCLE_READ_RETRY) {
|
||||
dev_err_ratelimited(chip->card->dev,
|
||||
"Error in WALFCC cycle count\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*device = ns_to_ktime(azx_scale64(ll_counter,
|
||||
NSEC_PER_SEC, runtime->rate));
|
||||
*device = ktime_add_ns(*device, (wallclk_cycles * NSEC_PER_SEC) /
|
||||
((HDA_MAX_CYCLE_VALUE + 1) * runtime->rate));
|
||||
|
||||
*system = convert_art_to_tsc(tsc_counter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
static int azx_get_sync_time(ktime_t *device,
|
||||
struct system_counterval_t *system, void *ctx)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int azx_get_crosststamp(struct snd_pcm_substream *substream,
|
||||
struct system_device_crosststamp *xtstamp)
|
||||
{
|
||||
return get_device_system_crosststamp(azx_get_sync_time,
|
||||
substream, NULL, xtstamp);
|
||||
}
|
||||
|
||||
static inline bool is_link_time_supported(struct snd_pcm_runtime *runtime,
|
||||
struct snd_pcm_audio_tstamp_config *ts)
|
||||
{
|
||||
if (runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME)
|
||||
if (ts->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int azx_get_time_info(struct snd_pcm_substream *substream,
|
||||
struct timespec *system_ts, struct timespec *audio_ts,
|
||||
struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
|
||||
struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
|
||||
{
|
||||
struct azx_dev *azx_dev = get_azx_dev(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct system_device_crosststamp xtstamp;
|
||||
int ret;
|
||||
u64 nsec;
|
||||
|
||||
if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) &&
|
||||
@ -361,8 +528,37 @@ static int azx_get_time_info(struct snd_pcm_substream *substream,
|
||||
audio_tstamp_report->accuracy_report = 1; /* rest of structure is valid */
|
||||
audio_tstamp_report->accuracy = 42; /* 24 MHz WallClock == 42ns resolution */
|
||||
|
||||
} else
|
||||
} else if (is_link_time_supported(runtime, audio_tstamp_config)) {
|
||||
|
||||
ret = azx_get_crosststamp(substream, &xtstamp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (runtime->tstamp_type) {
|
||||
case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC:
|
||||
return -EINVAL;
|
||||
|
||||
case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW:
|
||||
*system_ts = ktime_to_timespec(xtstamp.sys_monoraw);
|
||||
break;
|
||||
|
||||
default:
|
||||
*system_ts = ktime_to_timespec(xtstamp.sys_realtime);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
*audio_ts = ktime_to_timespec(xtstamp.device);
|
||||
|
||||
audio_tstamp_report->actual_type =
|
||||
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED;
|
||||
audio_tstamp_report->accuracy_report = 1;
|
||||
/* 24 MHz WallClock == 42ns resolution */
|
||||
audio_tstamp_report->accuracy = 42;
|
||||
|
||||
} else {
|
||||
audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -412,6 +608,11 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
|
||||
goto unlock;
|
||||
}
|
||||
runtime->private_data = azx_dev;
|
||||
|
||||
if (chip->gts_present)
|
||||
azx_pcm_hw.info = azx_pcm_hw.info |
|
||||
SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME;
|
||||
|
||||
runtime->hw = azx_pcm_hw;
|
||||
runtime->hw.channels_min = hinfo->channels_min;
|
||||
runtime->hw.channels_max = hinfo->channels_max;
|
||||
|
@ -159,6 +159,9 @@ struct azx {
|
||||
unsigned int region_requested:1;
|
||||
unsigned int disabled:1; /* disabled by vga_switcheroo */
|
||||
|
||||
/* GTS present */
|
||||
unsigned int gts_present:1;
|
||||
|
||||
#ifdef CONFIG_SND_HDA_DSP_LOADER
|
||||
struct azx_dev saved_azx_dev;
|
||||
#endif
|
||||
|
@ -54,6 +54,7 @@
|
||||
/* for snoop control */
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#endif
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
@ -1655,6 +1656,22 @@ static int azx_first_init(struct azx *chip)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (IS_SKL_PLUS(pci))
|
||||
snd_hdac_bus_parse_capabilities(bus);
|
||||
|
||||
/*
|
||||
* Some Intel CPUs has always running timer (ART) feature and
|
||||
* controller may have Global time sync reporting capability, so
|
||||
* check both of these before declaring synchronized time reporting
|
||||
* capability SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME
|
||||
*/
|
||||
chip->gts_present = false;
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
if (bus->ppcap && boot_cpu_has(X86_FEATURE_ART))
|
||||
chip->gts_present = true;
|
||||
#endif
|
||||
|
||||
if (chip->msi) {
|
||||
if (chip->driver_caps & AZX_DCAPS_NO_MSI64) {
|
||||
dev_dbg(card->dev, "Disabling 64bit MSI\n");
|
||||
|
@ -300,7 +300,7 @@ int skl_suspend_dsp(struct skl *skl)
|
||||
int ret;
|
||||
|
||||
/* if ppcap is not supported return 0 */
|
||||
if (!skl->ebus.ppcap)
|
||||
if (!skl->ebus.bus.ppcap)
|
||||
return 0;
|
||||
|
||||
ret = skl_dsp_sleep(ctx->dsp);
|
||||
@ -320,7 +320,7 @@ int skl_resume_dsp(struct skl *skl)
|
||||
int ret;
|
||||
|
||||
/* if ppcap is not supported return 0 */
|
||||
if (!skl->ebus.ppcap)
|
||||
if (!skl->ebus.bus.ppcap)
|
||||
return 0;
|
||||
|
||||
/* enable ppcap interrupt */
|
||||
|
@ -106,7 +106,7 @@ static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus,
|
||||
|
||||
static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *ebus)
|
||||
{
|
||||
if (ebus->ppcap)
|
||||
if ((ebus_to_hbus(ebus))->ppcap)
|
||||
return HDAC_EXT_STREAM_TYPE_HOST;
|
||||
else
|
||||
return HDAC_EXT_STREAM_TYPE_COUPLED;
|
||||
@ -188,7 +188,7 @@ static int skl_get_format(struct snd_pcm_substream *substream,
|
||||
struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
|
||||
int format_val = 0;
|
||||
|
||||
if (ebus->ppcap) {
|
||||
if ((ebus_to_hbus(ebus))->ppcap) {
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
format_val = snd_hdac_calc_stream_format(runtime->rate,
|
||||
@ -1020,7 +1020,7 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
|
||||
{
|
||||
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
|
||||
|
||||
if (!ebus->ppcap)
|
||||
if ((ebus_to_hbus(ebus))->ppcap)
|
||||
return skl_coupled_trigger(substream, cmd);
|
||||
|
||||
return 0;
|
||||
@ -1146,7 +1146,7 @@ static int skl_platform_soc_probe(struct snd_soc_platform *platform)
|
||||
int ret;
|
||||
|
||||
pm_runtime_get_sync(platform->dev);
|
||||
if (ebus->ppcap) {
|
||||
if ((ebus_to_hbus(ebus))->ppcap) {
|
||||
ret = skl_tplg_init(platform, ebus);
|
||||
if (ret < 0) {
|
||||
dev_err(platform->dev, "Failed to init topology!\n");
|
||||
|
@ -587,7 +587,7 @@ static int skl_first_init(struct hdac_ext_bus *ebus)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
snd_hdac_ext_bus_parse_capabilities(ebus);
|
||||
snd_hdac_bus_parse_capabilities(bus);
|
||||
|
||||
if (skl_acquire_irq(ebus, 0) < 0)
|
||||
return -EBUSY;
|
||||
@ -682,7 +682,7 @@ static int skl_probe(struct pci_dev *pci,
|
||||
skl_dmic_data.dmic_num = skl_get_dmic_geo(skl);
|
||||
|
||||
/* check if dsp is there */
|
||||
if (ebus->ppcap) {
|
||||
if (bus->ppcap) {
|
||||
err = skl_machine_device_register(skl,
|
||||
(void *)pci_id->driver_data);
|
||||
if (err < 0)
|
||||
@ -696,7 +696,7 @@ static int skl_probe(struct pci_dev *pci,
|
||||
skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge;
|
||||
|
||||
}
|
||||
if (ebus->mlcap)
|
||||
if (bus->mlcap)
|
||||
snd_hdac_ext_bus_get_ml_capabilities(ebus);
|
||||
|
||||
/* create device for soc dmic */
|
||||
|
Loading…
Reference in New Issue
Block a user