ASoC: SOF: Intel: hda-mlink: fixes and extensions
Merge series from Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>: With additional testing with multiple links and multiple DAI types, we found a couple of mistakes with refcounts, base address, missing initialization. A new helper was also added due to a change in the SoundWire programming sequences, with the host driver in charge of setting up the DMA channel mapping instead of the firmware.
This commit is contained in:
commit
9be0b3a007
|
@ -44,6 +44,9 @@ int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink);
|
|||
|
||||
int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num);
|
||||
|
||||
int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
|
||||
int channel_mask, int stream_id, int dir);
|
||||
|
||||
void hda_bus_ml_put_all(struct hdac_bus *bus);
|
||||
void hda_bus_ml_reset_losidv(struct hdac_bus *bus);
|
||||
int hda_bus_ml_resume(struct hdac_bus *bus);
|
||||
|
@ -51,6 +54,7 @@ int hda_bus_ml_suspend(struct hdac_bus *bus);
|
|||
|
||||
struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus);
|
||||
struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus);
|
||||
struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus);
|
||||
|
||||
struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid);
|
||||
|
||||
|
@ -144,6 +148,13 @@ hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) { return
|
|||
static inline int
|
||||
hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num) { return 0; }
|
||||
|
||||
static inline int
|
||||
hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
|
||||
int channel_mask, int stream_id, int dir)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { }
|
||||
static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { }
|
||||
static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; }
|
||||
|
@ -155,6 +166,9 @@ hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; }
|
|||
static inline struct hdac_ext_link *
|
||||
hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus) { return NULL; }
|
||||
|
||||
static inline struct hdac_ext_link *
|
||||
hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus) { return NULL; }
|
||||
|
||||
static inline struct mutex *
|
||||
hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid) { return NULL; }
|
||||
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
|
||||
|
||||
/* worst-case number of sublinks is used for sublink refcount array allocation only */
|
||||
#define HDAML_MAX_SUBLINKS (AZX_ML_LCTL_CPA_SHIFT - AZX_ML_LCTL_SPA_SHIFT)
|
||||
|
||||
/**
|
||||
* struct hdac_ext2_link - HDAudio extended+alternate link
|
||||
*
|
||||
|
@ -33,6 +36,7 @@
|
|||
* @leptr: extended link pointer
|
||||
* @eml_lock: mutual exclusion to access shared registers e.g. CPA/SPA bits
|
||||
* in LCTL register
|
||||
* @sublink_ref_count: array of refcounts, required to power-manage sublinks independently
|
||||
* @base_ptr: pointer to shim/ip/shim_vs space
|
||||
* @instance_offset: offset between each of @slcount instances managed by link
|
||||
* @shim_offset: offset to SHIM register base
|
||||
|
@ -53,6 +57,7 @@ struct hdac_ext2_link {
|
|||
u32 leptr;
|
||||
|
||||
struct mutex eml_lock; /* prevent concurrent access to e.g. CPA/SPA */
|
||||
int sublink_ref_count[HDAML_MAX_SUBLINKS];
|
||||
|
||||
/* internal values computed from LCAP contents */
|
||||
void __iomem *base_ptr;
|
||||
|
@ -68,6 +73,7 @@ struct hdac_ext2_link {
|
|||
#define AZX_REG_SDW_SHIM_OFFSET 0x0
|
||||
#define AZX_REG_SDW_IP_OFFSET 0x100
|
||||
#define AZX_REG_SDW_VS_SHIM_OFFSET 0x6000
|
||||
#define AZX_REG_SDW_SHIM_PCMSyCM(y) (0x16 + 0x4 * (y))
|
||||
|
||||
/* only one instance supported */
|
||||
#define AZX_REG_INTEL_DMIC_SHIM_OFFSET 0x0
|
||||
|
@ -91,7 +97,7 @@ struct hdac_ext2_link {
|
|||
*/
|
||||
|
||||
static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
|
||||
void __iomem *ml_addr, int link_idx)
|
||||
void __iomem *remap_addr, void __iomem *ml_addr, int link_idx)
|
||||
{
|
||||
struct hdac_ext_link *hlink = &h2link->hext_link;
|
||||
u32 base_offset;
|
||||
|
@ -126,15 +132,16 @@ static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
|
|||
link_idx, h2link->slcount);
|
||||
|
||||
/* find IP ID and offsets */
|
||||
h2link->leptr = readl(hlink->ml_addr + AZX_REG_ML_LEPTR);
|
||||
h2link->leptr = readl(ml_addr + AZX_REG_ML_LEPTR);
|
||||
|
||||
h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr);
|
||||
|
||||
base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr);
|
||||
h2link->base_ptr = hlink->ml_addr + base_offset;
|
||||
h2link->base_ptr = remap_addr + base_offset;
|
||||
|
||||
switch (h2link->elid) {
|
||||
case AZX_REG_ML_LEPTR_ID_SDW:
|
||||
h2link->instance_offset = AZX_REG_SDW_INSTANCE_OFFSET;
|
||||
h2link->shim_offset = AZX_REG_SDW_SHIM_OFFSET;
|
||||
h2link->ip_offset = AZX_REG_SDW_IP_OFFSET;
|
||||
h2link->shim_vs_offset = AZX_REG_SDW_VS_SHIM_OFFSET;
|
||||
|
@ -149,6 +156,7 @@ static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
|
|||
link_idx, base_offset);
|
||||
break;
|
||||
case AZX_REG_ML_LEPTR_ID_INTEL_SSP:
|
||||
h2link->instance_offset = AZX_REG_INTEL_SSP_INSTANCE_OFFSET;
|
||||
h2link->shim_offset = AZX_REG_INTEL_SSP_SHIM_OFFSET;
|
||||
h2link->ip_offset = AZX_REG_INTEL_SSP_IP_OFFSET;
|
||||
h2link->shim_vs_offset = AZX_REG_INTEL_SSP_VS_SHIM_OFFSET;
|
||||
|
@ -333,6 +341,21 @@ static void hdaml_link_set_lsdiid(u32 __iomem *lsdiid, int dev_num)
|
|||
writel(val, lsdiid);
|
||||
}
|
||||
|
||||
static void hdaml_shim_map_stream_ch(u16 __iomem *pcmsycm, int lchan, int hchan,
|
||||
int stream_id, int dir)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
val = readw(pcmsycm);
|
||||
|
||||
u16p_replace_bits(&val, lchan, GENMASK(3, 0));
|
||||
u16p_replace_bits(&val, hchan, GENMASK(7, 4));
|
||||
u16p_replace_bits(&val, stream_id, GENMASK(13, 8));
|
||||
u16p_replace_bits(&val, dir, BIT(15));
|
||||
|
||||
writew(val, pcmsycm);
|
||||
}
|
||||
|
||||
static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable)
|
||||
{
|
||||
u32 val = readl(lctl);
|
||||
|
@ -364,7 +387,7 @@ static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
|
|||
hlink->bus = bus;
|
||||
hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index);
|
||||
|
||||
ret = hdaml_lnk_enum(bus->dev, h2link, hlink->ml_addr, index);
|
||||
ret = hdaml_lnk_enum(bus->dev, h2link, bus->remap_addr, hlink->ml_addr, index);
|
||||
if (ret < 0) {
|
||||
kfree(h2link);
|
||||
return ret;
|
||||
|
@ -641,8 +664,13 @@ static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid,
|
|||
if (eml_lock)
|
||||
mutex_lock(&h2link->eml_lock);
|
||||
|
||||
if (!alt) {
|
||||
if (++hlink->ref_count > 1)
|
||||
goto skip_init;
|
||||
} else {
|
||||
if (++h2link->sublink_ref_count[sublink] > 1)
|
||||
goto skip_init;
|
||||
}
|
||||
|
||||
ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
|
||||
|
||||
|
@ -684,9 +712,13 @@ static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid
|
|||
if (eml_lock)
|
||||
mutex_lock(&h2link->eml_lock);
|
||||
|
||||
if (!alt) {
|
||||
if (--hlink->ref_count > 0)
|
||||
goto skip_shutdown;
|
||||
|
||||
} else {
|
||||
if (--h2link->sublink_ref_count[sublink] > 0)
|
||||
goto skip_shutdown;
|
||||
}
|
||||
ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
|
||||
|
||||
skip_shutdown:
|
||||
|
@ -740,6 +772,40 @@ int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
|
|||
return 0;
|
||||
} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, SND_SOC_SOF_HDA_MLINK);
|
||||
|
||||
/*
|
||||
* the 'y' parameter comes from the PCMSyCM hardware register naming. 'y' refers to the
|
||||
* PDI index, i.e. the FIFO used for RX or TX
|
||||
*/
|
||||
int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
|
||||
int channel_mask, int stream_id, int dir)
|
||||
{
|
||||
struct hdac_ext2_link *h2link;
|
||||
u16 __iomem *pcmsycm;
|
||||
u16 val;
|
||||
|
||||
h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
|
||||
if (!h2link)
|
||||
return -ENODEV;
|
||||
|
||||
pcmsycm = h2link->base_ptr + h2link->shim_offset +
|
||||
h2link->instance_offset * sublink +
|
||||
AZX_REG_SDW_SHIM_PCMSyCM(y);
|
||||
|
||||
mutex_lock(&h2link->eml_lock);
|
||||
|
||||
hdaml_shim_map_stream_ch(pcmsycm, 0, hweight32(channel_mask),
|
||||
stream_id, dir);
|
||||
|
||||
mutex_unlock(&h2link->eml_lock);
|
||||
|
||||
val = readw(pcmsycm);
|
||||
|
||||
dev_dbg(bus->dev, "channel_mask %#x stream_id %d dir %d pcmscm %#x\n",
|
||||
channel_mask, stream_id, dir, val);
|
||||
|
||||
return 0;
|
||||
} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, SND_SOC_SOF_HDA_MLINK);
|
||||
|
||||
void hda_bus_ml_put_all(struct hdac_bus *bus)
|
||||
{
|
||||
struct hdac_ext_link *hlink;
|
||||
|
@ -836,6 +902,18 @@ struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus)
|
|||
}
|
||||
EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, SND_SOC_SOF_HDA_MLINK);
|
||||
|
||||
struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus)
|
||||
{
|
||||
struct hdac_ext2_link *h2link;
|
||||
|
||||
h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
|
||||
if (!h2link)
|
||||
return NULL;
|
||||
|
||||
return &h2link->hext_link;
|
||||
}
|
||||
EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_hlink, SND_SOC_SOF_HDA_MLINK);
|
||||
|
||||
int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)
|
||||
{
|
||||
struct hdac_ext2_link *h2link;
|
||||
|
|
Loading…
Reference in New Issue