Commit da74273c authored by Mark Brown's avatar Mark Brown

Merge branch 'topic/hda-link-time' of...

Merge branch 'topic/hda-link-time' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound into asoc-intel
parents 3d4006cd bfcba288
...@@ -89,6 +89,19 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; ...@@ -89,6 +89,19 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
#define AZX_REG_SD_BDLPL 0x18 #define AZX_REG_SD_BDLPL 0x18
#define AZX_REG_SD_BDLPU 0x1c #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 */ /* Haswell/Broadwell display HD-A controller Extended Mode registers */
#define AZX_REG_HSW_EM4 0x100c #define AZX_REG_HSW_EM4 0x100c
#define AZX_REG_HSW_EM5 0x1010 #define AZX_REG_HSW_EM5 0x1010
...@@ -242,6 +255,29 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; ...@@ -242,6 +255,29 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
/* Interval used to calculate the iterating register offset */ /* Interval used to calculate the iterating register offset */
#define AZX_DRSM_INTERVAL 0x08 #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 * helpers to read the stream position
*/ */
......
...@@ -245,6 +245,12 @@ struct hdac_rb { ...@@ -245,6 +245,12 @@ struct hdac_rb {
/* /*
* HD-audio bus base driver * 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 hdac_bus {
struct device *dev; struct device *dev;
...@@ -256,6 +262,12 @@ struct hdac_bus { ...@@ -256,6 +262,12 @@ struct hdac_bus {
void __iomem *remap_addr; void __iomem *remap_addr;
int irq; int irq;
void __iomem *ppcap;
void __iomem *spbcap;
void __iomem *mlcap;
void __iomem *gtscap;
void __iomem *drsmcap;
/* codec linked list */ /* codec linked list */
struct list_head codec_list; struct list_head codec_list;
unsigned int num_codecs; unsigned int num_codecs;
...@@ -335,6 +347,7 @@ static inline void snd_hdac_codec_link_down(struct hdac_device *codec) ...@@ -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_send_cmd(struct hdac_bus *bus, unsigned int val);
int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
unsigned int *res); unsigned int *res);
int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus);
int snd_hdac_link_power(struct hdac_device *codec, bool enable); int snd_hdac_link_power(struct hdac_device *codec, bool enable);
bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset); bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset);
......
...@@ -8,11 +8,6 @@ ...@@ -8,11 +8,6 @@
* *
* @bus: hdac bus * @bus: hdac bus
* @num_streams: streams supported * @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 * @hlink_list: link list of HDA links
* @lock: lock for link mgmt * @lock: lock for link mgmt
* @cmd_dma_state: state of cmd DMAs: CORB and RIRB * @cmd_dma_state: state of cmd DMAs: CORB and RIRB
...@@ -22,12 +17,6 @@ struct hdac_ext_bus { ...@@ -22,12 +17,6 @@ struct hdac_ext_bus {
int num_streams; int num_streams;
int idx; int idx;
void __iomem *ppcap;
void __iomem *spbcap;
void __iomem *mlcap;
void __iomem *gtscap;
void __iomem *drsmcap;
struct list_head hlink_list; struct list_head hlink_list;
struct mutex lock; struct mutex lock;
...@@ -54,7 +43,6 @@ void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus); ...@@ -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) \ #define HDA_CODEC_EXT_ENTRY(_vid, _revid, _name, _drv_data) \
HDA_CODEC_REV_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_enable(struct hdac_ext_bus *chip, bool enable);
void snd_hdac_ext_bus_ppcap_int_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 @@ ...@@ -29,81 +29,6 @@
*/ */
#define HDAC_MAX_CAPS 10 #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 * processing pipe helpers - these helpers are useful for dealing with HDA
* new capability of processing pipelines * new capability of processing pipelines
...@@ -118,15 +43,15 @@ void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *ebus, bool enable) ...@@ -118,15 +43,15 @@ void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *ebus, bool enable)
{ {
struct hdac_bus *bus = &ebus->bus; struct hdac_bus *bus = &ebus->bus;
if (!ebus->ppcap) { if (!bus->ppcap) {
dev_err(bus->dev, "Address of PP capability is NULL"); dev_err(bus->dev, "Address of PP capability is NULL");
return; return;
} }
if (enable) 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 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); 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) ...@@ -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; struct hdac_bus *bus = &ebus->bus;
if (!ebus->ppcap) { if (!bus->ppcap) {
dev_err(bus->dev, "Address of PP capability is NULL\n"); dev_err(bus->dev, "Address of PP capability is NULL\n");
return; return;
} }
if (enable) 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 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); 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) ...@@ -171,7 +96,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
struct hdac_ext_link *hlink; struct hdac_ext_link *hlink;
struct hdac_bus *bus = &ebus->bus; 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); 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) ...@@ -181,7 +106,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
return -ENOMEM; return -ENOMEM;
hlink->index = idx; hlink->index = idx;
hlink->bus = bus; hlink->bus = bus;
hlink->ml_addr = ebus->mlcap + AZX_ML_BASE + hlink->ml_addr = bus->mlcap + AZX_ML_BASE +
(AZX_ML_INTERVAL * idx); (AZX_ML_INTERVAL * idx);
hlink->lcaps = readl(hlink->ml_addr + AZX_REG_ML_LCAP); hlink->lcaps = readl(hlink->ml_addr + AZX_REG_ML_LCAP);
hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID); 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, ...@@ -40,27 +40,27 @@ void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus,
{ {
struct hdac_bus *bus = &ebus->bus; struct hdac_bus *bus = &ebus->bus;
if (ebus->ppcap) { if (bus->ppcap) {
stream->pphc_addr = ebus->ppcap + AZX_PPHC_BASE + stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
AZX_PPHC_INTERVAL * idx; 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_MULTI * ebus->num_streams +
AZX_PPLC_INTERVAL * idx; AZX_PPLC_INTERVAL * idx;
} }
if (ebus->spbcap) { if (bus->spbcap) {
stream->spib_addr = ebus->spbcap + AZX_SPB_BASE + stream->spib_addr = bus->spbcap + AZX_SPB_BASE +
AZX_SPB_INTERVAL * idx + AZX_SPB_INTERVAL * idx +
AZX_SPB_SPIB; 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_INTERVAL * idx +
AZX_SPB_MAXFIFO; AZX_SPB_MAXFIFO;
} }
if (ebus->drsmcap) if (bus->drsmcap)
stream->dpibr_addr = ebus->drsmcap + AZX_DRSM_BASE + stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE +
AZX_DRSM_INTERVAL * idx; AZX_DRSM_INTERVAL * idx;
stream->decoupled = false; stream->decoupled = false;
...@@ -131,10 +131,10 @@ void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus, ...@@ -131,10 +131,10 @@ void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus,
spin_lock_irq(&bus->reg_lock); spin_lock_irq(&bus->reg_lock);
if (decouple) 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)); AZX_PPCTL_PROCEN(hstream->index));
else 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); AZX_PPCTL_PROCEN(hstream->index), 0);
stream->decoupled = decouple; stream->decoupled = decouple;
spin_unlock_irq(&bus->reg_lock); spin_unlock_irq(&bus->reg_lock);
...@@ -255,7 +255,7 @@ hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus, ...@@ -255,7 +255,7 @@ hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus,
struct hdac_stream *stream = NULL; struct hdac_stream *stream = NULL;
struct hdac_bus *hbus = &ebus->bus; struct hdac_bus *hbus = &ebus->bus;
if (!ebus->ppcap) { if (!hbus->ppcap) {
dev_err(hbus->dev, "stream type not supported\n"); dev_err(hbus->dev, "stream type not supported\n");
return NULL; return NULL;
} }
...@@ -296,7 +296,7 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus, ...@@ -296,7 +296,7 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus,
struct hdac_stream *stream = NULL; struct hdac_stream *stream = NULL;
struct hdac_bus *hbus = &ebus->bus; struct hdac_bus *hbus = &ebus->bus;
if (!ebus->ppcap) { if (!hbus->ppcap) {
dev_err(hbus->dev, "stream type not supported\n"); dev_err(hbus->dev, "stream type not supported\n");
return NULL; return NULL;
} }
...@@ -423,21 +423,21 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus, ...@@ -423,21 +423,21 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus,
u32 register_mask = 0; u32 register_mask = 0;
struct hdac_bus *bus = &ebus->bus; struct hdac_bus *bus = &ebus->bus;
if (!ebus->spbcap) { if (!bus->spbcap) {
dev_err(bus->dev, "Address of SPB capability is NULL"); dev_err(bus->dev, "Address of SPB capability is NULL");
return; return;
} }
mask |= (1 << index); mask |= (1 << index);
register_mask = readl(ebus->spbcap + AZX_REG_SPB_SPBFCCTL); register_mask = readl(bus->spbcap + AZX_REG_SPB_SPBFCCTL);
mask |= register_mask; mask |= register_mask;
if (enable) 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 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); 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, ...@@ -452,7 +452,7 @@ int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus,
{ {
struct hdac_bus *bus = &ebus->bus; struct hdac_bus *bus = &ebus->bus;
if (!ebus->spbcap) { if (!bus->spbcap) {
dev_err(bus->dev, "Address of SPB capability is NULL"); dev_err(bus->dev, "Address of SPB capability is NULL");
return -EINVAL; return -EINVAL;
} }
...@@ -475,7 +475,7 @@ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus, ...@@ -475,7 +475,7 @@ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus,
{ {
struct hdac_bus *bus = &ebus->bus; struct hdac_bus *bus = &ebus->bus;
if (!ebus->spbcap) { if (!bus->spbcap) {
dev_err(bus->dev, "Address of SPB capability is NULL"); dev_err(bus->dev, "Address of SPB capability is NULL");
return -EINVAL; return -EINVAL;
} }
...@@ -515,21 +515,21 @@ void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus, ...@@ -515,21 +515,21 @@ void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus,
u32 register_mask = 0; u32 register_mask = 0;
struct hdac_bus *bus = &ebus->bus; struct hdac_bus *bus = &ebus->bus;
if (!ebus->drsmcap) { if (!bus->drsmcap) {
dev_err(bus->dev, "Address of DRSM capability is NULL"); dev_err(bus->dev, "Address of DRSM capability is NULL");
return; return;
} }
mask |= (1 << index); mask |= (1 << index);
register_mask = readl(ebus->drsmcap + AZX_REG_SPB_SPBFCCTL); register_mask = readl(bus->drsmcap + AZX_REG_SPB_SPBFCCTL);
mask |= register_mask; mask |= register_mask;
if (enable) 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 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); 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, ...@@ -544,7 +544,7 @@ int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus,
{ {
struct hdac_bus *bus = &ebus->bus; struct hdac_bus *bus = &ebus->bus;
if (!ebus->drsmcap) { if (!bus->drsmcap) {
dev_err(bus->dev, "Address of DRSM capability is NULL"); dev_err(bus->dev, "Address of DRSM capability is NULL");
return -EINVAL; return -EINVAL;
} }
......
...@@ -255,6 +255,81 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, ...@@ -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); 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 * Lowlevel interface
*/ */
......
...@@ -27,6 +27,12 @@ ...@@ -27,6 +27,12 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/slab.h> #include <linux/slab.h>
#ifdef CONFIG_X86
/* for art-tsc conversion */
#include <asm/tsc.h>
#endif
#include <sound/core.h> #include <sound/core.h>
#include <sound/initval.h> #include <sound/initval.h>
#include "hda_controller.h" #include "hda_controller.h"
...@@ -337,12 +343,173 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream) ...@@ -337,12 +343,173 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
azx_get_position(chip, azx_dev)); 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, static int azx_get_time_info(struct snd_pcm_substream *substream,
struct timespec *system_ts, struct timespec *audio_ts, struct timespec *system_ts, struct timespec *audio_ts,
struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
struct snd_pcm_audio_tstamp_report *audio_tstamp_report) struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
{ {
struct azx_dev *azx_dev = get_azx_dev(substream); 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; u64 nsec;
if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) && 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, ...@@ -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_report = 1; /* rest of structure is valid */
audio_tstamp_report->accuracy = 42; /* 24 MHz WallClock == 42ns resolution */ 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; audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
}
return 0; return 0;
} }
...@@ -412,6 +608,11 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) ...@@ -412,6 +608,11 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
goto unlock; goto unlock;
} }
runtime->private_data = azx_dev; 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 = azx_pcm_hw;
runtime->hw.channels_min = hinfo->channels_min; runtime->hw.channels_min = hinfo->channels_min;
runtime->hw.channels_max = hinfo->channels_max; runtime->hw.channels_max = hinfo->channels_max;
......
...@@ -159,6 +159,9 @@ struct azx { ...@@ -159,6 +159,9 @@ struct azx {
unsigned int region_requested:1; unsigned int region_requested:1;
unsigned int disabled:1; /* disabled by vga_switcheroo */ unsigned int disabled:1; /* disabled by vga_switcheroo */
/* GTS present */
unsigned int gts_present:1;
#ifdef CONFIG_SND_HDA_DSP_LOADER #ifdef CONFIG_SND_HDA_DSP_LOADER
struct azx_dev saved_azx_dev; struct azx_dev saved_azx_dev;
#endif #endif
......
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
/* for snoop control */ /* for snoop control */
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/cpufeature.h>
#endif #endif
#include <sound/core.h> #include <sound/core.h>
#include <sound/initval.h> #include <sound/initval.h>
...@@ -1655,6 +1656,22 @@ static int azx_first_init(struct azx *chip) ...@@ -1655,6 +1656,22 @@ static int azx_first_init(struct azx *chip)
return -ENXIO; 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->msi) {
if (chip->driver_caps & AZX_DCAPS_NO_MSI64) { if (chip->driver_caps & AZX_DCAPS_NO_MSI64) {
dev_dbg(card->dev, "Disabling 64bit MSI\n"); dev_dbg(card->dev, "Disabling 64bit MSI\n");
......
...@@ -300,7 +300,7 @@ int skl_suspend_dsp(struct skl *skl) ...@@ -300,7 +300,7 @@ int skl_suspend_dsp(struct skl *skl)
int ret; int ret;
/* if ppcap is not supported return 0 */ /* if ppcap is not supported return 0 */
if (!skl->ebus.ppcap) if (!skl->ebus.bus.ppcap)
return 0; return 0;
ret = skl_dsp_sleep(ctx->dsp); ret = skl_dsp_sleep(ctx->dsp);
...@@ -320,7 +320,7 @@ int skl_resume_dsp(struct skl *skl) ...@@ -320,7 +320,7 @@ int skl_resume_dsp(struct skl *skl)
int ret; int ret;
/* if ppcap is not supported return 0 */ /* if ppcap is not supported return 0 */
if (!skl->ebus.ppcap) if (!skl->ebus.bus.ppcap)
return 0; return 0;
/* enable ppcap interrupt */ /* enable ppcap interrupt */
......
...@@ -106,7 +106,7 @@ static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus, ...@@ -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) 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; return HDAC_EXT_STREAM_TYPE_HOST;
else else
return HDAC_EXT_STREAM_TYPE_COUPLED; return HDAC_EXT_STREAM_TYPE_COUPLED;
...@@ -188,7 +188,7 @@ static int skl_get_format(struct snd_pcm_substream *substream, ...@@ -188,7 +188,7 @@ static int skl_get_format(struct snd_pcm_substream *substream,
struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
int format_val = 0; int format_val = 0;
if (ebus->ppcap) { if ((ebus_to_hbus(ebus))->ppcap) {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
format_val = snd_hdac_calc_stream_format(runtime->rate, format_val = snd_hdac_calc_stream_format(runtime->rate,
...@@ -1020,7 +1020,7 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, ...@@ -1020,7 +1020,7 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
{ {
struct hdac_ext_bus *ebus = get_bus_ctx(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 skl_coupled_trigger(substream, cmd);
return 0; return 0;
...@@ -1146,7 +1146,7 @@ static int skl_platform_soc_probe(struct snd_soc_platform *platform) ...@@ -1146,7 +1146,7 @@ static int skl_platform_soc_probe(struct snd_soc_platform *platform)
int ret; int ret;
pm_runtime_get_sync(platform->dev); pm_runtime_get_sync(platform->dev);
if (ebus->ppcap) { if ((ebus_to_hbus(ebus))->ppcap) {
ret = skl_tplg_init(platform, ebus); ret = skl_tplg_init(platform, ebus);
if (ret < 0) { if (ret < 0) {
dev_err(platform->dev, "Failed to init topology!\n"); dev_err(platform->dev, "Failed to init topology!\n");
......
...@@ -587,7 +587,7 @@ static int skl_first_init(struct hdac_ext_bus *ebus) ...@@ -587,7 +587,7 @@ static int skl_first_init(struct hdac_ext_bus *ebus)
return -ENXIO; return -ENXIO;
} }
snd_hdac_ext_bus_parse_capabilities(ebus); snd_hdac_bus_parse_capabilities(bus);
if (skl_acquire_irq(ebus, 0) < 0) if (skl_acquire_irq(ebus, 0) < 0)
return -EBUSY; return -EBUSY;
...@@ -682,7 +682,7 @@ static int skl_probe(struct pci_dev *pci, ...@@ -682,7 +682,7 @@ static int skl_probe(struct pci_dev *pci,
skl_dmic_data.dmic_num = skl_get_dmic_geo(skl); skl_dmic_data.dmic_num = skl_get_dmic_geo(skl);
/* check if dsp is there */ /* check if dsp is there */
if (ebus->ppcap) { if (bus->ppcap) {
err = skl_machine_device_register(skl, err = skl_machine_device_register(skl,
(void *)pci_id->driver_data); (void *)pci_id->driver_data);
if (err < 0) if (err < 0)
...@@ -696,7 +696,7 @@ static int skl_probe(struct pci_dev *pci, ...@@ -696,7 +696,7 @@ static int skl_probe(struct pci_dev *pci,
skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge; skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge;
} }
if (ebus->mlcap) if (bus->mlcap)
snd_hdac_ext_bus_get_ml_capabilities(ebus); snd_hdac_ext_bus_get_ml_capabilities(ebus);
/* create device for soc dmic */ /* create device for soc dmic */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment