mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 17:30:02 +00:00
ASoC: SOF: Intel: hda-mlink: add helpers to set link SYNC frequency
These helpers configure the ratio between the base clock and the hardware signal used for link synchronization. The SYNCPRD is written before the first sublink is powered-up. The SYNCPU bit is set, but it will only be cleared after the link is powered-up, hence the implementation with a set/wait pattern. These helpers are currently only needed by SoundWire support, where the lock is taken at a higher level, so only the _unlocked versions are exposed for now. Note that the _wait_bit() implementation is similar to previous helpers in drivers/soundwire, but with sleep duration and timeout aligned with hardware recommendations. If desired, this helper could be modified in a second step with e.g. readl_poll_timeout(). Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Reviewed-by: Rander Wang <rander.wang@intel.com> Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com> Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com> Reviewed-by: Takashi Iwai <tiwai@suse.de> Link: https://lore.kernel.org/r/20230404104127.5629-13-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
2e4288319a
commit
02ba1b021c
2 changed files with 117 additions and 0 deletions
|
@ -17,6 +17,12 @@ int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid);
|
||||||
void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable);
|
void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable);
|
||||||
bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid);
|
bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid);
|
||||||
|
|
||||||
|
int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd);
|
||||||
|
int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd);
|
||||||
|
|
||||||
|
int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid);
|
||||||
|
int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus);
|
||||||
|
|
||||||
int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink);
|
int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink);
|
||||||
int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink);
|
int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink);
|
||||||
|
|
||||||
|
@ -47,6 +53,27 @@ hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool ena
|
||||||
static inline bool
|
static inline bool
|
||||||
hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid) { return false; }
|
hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid) { return false; }
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus) { return 0; }
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink)
|
hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink)
|
||||||
{
|
{
|
||||||
|
|
|
@ -254,6 +254,46 @@ static bool hdaml_link_check_interrupt(u32 __iomem *lctl)
|
||||||
return val & AZX_ML_LCTL_INTSTS;
|
return val & AZX_ML_LCTL_INTSTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int hdaml_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
|
||||||
|
{
|
||||||
|
int timeout = HDAML_POLL_DELAY_RETRY;
|
||||||
|
u32 reg_read;
|
||||||
|
|
||||||
|
do {
|
||||||
|
reg_read = readl(base + offset);
|
||||||
|
if ((reg_read & mask) == target)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
timeout--;
|
||||||
|
usleep_range(HDAML_POLL_DELAY_MIN_US,
|
||||||
|
HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
|
||||||
|
} while (timeout != 0);
|
||||||
|
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hdaml_link_set_syncprd(u32 __iomem *lsync, u32 syncprd)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = readl(lsync);
|
||||||
|
val &= ~AZX_REG_ML_LSYNC_SYNCPRD;
|
||||||
|
val |= (syncprd & AZX_REG_ML_LSYNC_SYNCPRD);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set SYNCPU but do not wait. The bit is cleared by hardware when
|
||||||
|
* the link becomes active.
|
||||||
|
*/
|
||||||
|
val |= AZX_REG_ML_LSYNC_SYNCPU;
|
||||||
|
|
||||||
|
writel(val, lsync);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hdaml_link_wait_syncpu(u32 __iomem *lsync)
|
||||||
|
{
|
||||||
|
return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* END HDAML section */
|
/* END HDAML section */
|
||||||
|
|
||||||
static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
|
static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
|
||||||
|
@ -402,6 +442,56 @@ bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, SND_SOC_SOF_HDA_MLINK);
|
EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, SND_SOC_SOF_HDA_MLINK);
|
||||||
|
|
||||||
|
int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd)
|
||||||
|
{
|
||||||
|
struct hdac_ext2_link *h2link;
|
||||||
|
struct hdac_ext_link *hlink;
|
||||||
|
|
||||||
|
h2link = find_ext2_link(bus, alt, elid);
|
||||||
|
if (!h2link)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!h2link->lss)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
hlink = &h2link->hext_link;
|
||||||
|
|
||||||
|
hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);
|
||||||
|
|
||||||
|
int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd)
|
||||||
|
{
|
||||||
|
return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);
|
||||||
|
|
||||||
|
int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid)
|
||||||
|
{
|
||||||
|
struct hdac_ext2_link *h2link;
|
||||||
|
struct hdac_ext_link *hlink;
|
||||||
|
|
||||||
|
h2link = find_ext2_link(bus, alt, elid);
|
||||||
|
if (!h2link)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!h2link->lss)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
hlink = &h2link->hext_link;
|
||||||
|
|
||||||
|
return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
|
||||||
|
|
||||||
|
int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus)
|
||||||
|
{
|
||||||
|
return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
|
||||||
|
|
||||||
static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
|
static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
|
||||||
bool eml_lock)
|
bool eml_lock)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue