diff options
Diffstat (limited to 'drivers/soundwire/intel_ace2x.c')
-rw-r--r-- | drivers/soundwire/intel_ace2x.c | 204 |
1 files changed, 143 insertions, 61 deletions
diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 8280baa3254b..e305c6258ca9 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -1,17 +1,20 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) -// Copyright(c) 2023 Intel Corporation. All rights reserved. +// Copyright(c) 2023 Intel Corporation /* * Soundwire Intel ops for LunarLake */ #include <linux/acpi.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/soundwire/sdw_registers.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_intel.h> -#include <sound/pcm_params.h> +#include <sound/hdaudio.h> #include <sound/hda-mlink.h> +#include <sound/hda_register.h> +#include <sound/pcm_params.h> #include "cadence_master.h" #include "bus.h" #include "intel.h" @@ -23,49 +26,101 @@ static void intel_shim_vs_init(struct sdw_intel *sdw) { void __iomem *shim_vs = sdw->link_res->shim_vs; + struct sdw_bus *bus = &sdw->cdns.bus; + struct sdw_intel_prop *intel_prop; + u16 clde; + u16 doaise2; + u16 dodse2; + u16 clds; + u16 clss; + u16 doaise; + u16 doais; + u16 dodse; + u16 dods; u16 act; + intel_prop = bus->vendor_specific_prop; + clde = intel_prop->clde; + doaise2 = intel_prop->doaise2; + dodse2 = intel_prop->dodse2; + clds = intel_prop->clds; + clss = intel_prop->clss; + doaise = intel_prop->doaise; + doais = intel_prop->doais; + dodse = intel_prop->dodse; + dods = intel_prop->dods; + act = intel_readw(shim_vs, SDW_SHIM2_INTEL_VS_ACTMCTL); - u16p_replace_bits(&act, 0x1, SDW_SHIM2_INTEL_VS_ACTMCTL_DOAIS); + u16p_replace_bits(&act, clde, SDW_SHIM3_INTEL_VS_ACTMCTL_CLDE); + u16p_replace_bits(&act, doaise2, SDW_SHIM3_INTEL_VS_ACTMCTL_DOAISE2); + u16p_replace_bits(&act, dodse2, SDW_SHIM3_INTEL_VS_ACTMCTL_DODSE2); + u16p_replace_bits(&act, clds, SDW_SHIM3_INTEL_VS_ACTMCTL_CLDS); + u16p_replace_bits(&act, clss, SDW_SHIM3_INTEL_VS_ACTMCTL_CLSS); + u16p_replace_bits(&act, doaise, SDW_SHIM2_INTEL_VS_ACTMCTL_DOAISE); + u16p_replace_bits(&act, doais, SDW_SHIM2_INTEL_VS_ACTMCTL_DOAIS); + u16p_replace_bits(&act, dodse, SDW_SHIM2_INTEL_VS_ACTMCTL_DODSE); + u16p_replace_bits(&act, dods, SDW_SHIM2_INTEL_VS_ACTMCTL_DODS); act |= SDW_SHIM2_INTEL_VS_ACTMCTL_DACTQE; - act |= SDW_SHIM2_INTEL_VS_ACTMCTL_DODS; intel_writew(shim_vs, SDW_SHIM2_INTEL_VS_ACTMCTL, act); usleep_range(10, 15); } -static int intel_shim_check_wake(struct sdw_intel *sdw) +static void intel_shim_vs_set_clock_source(struct sdw_intel *sdw, u32 source) { - void __iomem *shim_vs; - u16 wake_sts; + void __iomem *shim_vs = sdw->link_res->shim_vs; + u32 val; + + val = intel_readl(shim_vs, SDW_SHIM2_INTEL_VS_LVSCTL); + + u32p_replace_bits(&val, source, SDW_SHIM2_INTEL_VS_LVSCTL_MLCS); - shim_vs = sdw->link_res->shim_vs; - wake_sts = intel_readw(shim_vs, SDW_SHIM2_INTEL_VS_WAKESTS); + intel_writel(shim_vs, SDW_SHIM2_INTEL_VS_LVSCTL, val); - return wake_sts & SDW_SHIM2_INTEL_VS_WAKEEN_PWS; + dev_dbg(sdw->cdns.dev, "clock source %d LVSCTL %#x\n", source, val); +} + +static int intel_shim_check_wake(struct sdw_intel *sdw) +{ + /* + * We follow the HDaudio example and resume unconditionally + * without checking the WAKESTS bit for that specific link + */ + + return 1; } static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) { - void __iomem *shim_vs = sdw->link_res->shim_vs; + u16 lsdiid = 0; u16 wake_en; u16 wake_sts; + int ret; - wake_en = intel_readw(shim_vs, SDW_SHIM2_INTEL_VS_WAKEEN); + mutex_lock(sdw->link_res->shim_lock); + + ret = hdac_bus_eml_sdw_get_lsdiid_unlocked(sdw->link_res->hbus, sdw->instance, &lsdiid); + if (ret < 0) + goto unlock; + + wake_en = snd_hdac_chip_readw(sdw->link_res->hbus, WAKEEN); if (wake_enable) { /* Enable the wakeup */ - wake_en |= SDW_SHIM2_INTEL_VS_WAKEEN_PWE; - intel_writew(shim_vs, SDW_SHIM2_INTEL_VS_WAKEEN, wake_en); + wake_en |= lsdiid; + + snd_hdac_chip_writew(sdw->link_res->hbus, WAKEEN, wake_en); } else { /* Disable the wake up interrupt */ - wake_en &= ~SDW_SHIM2_INTEL_VS_WAKEEN_PWE; - intel_writew(shim_vs, SDW_SHIM2_INTEL_VS_WAKEEN, wake_en); + wake_en &= ~lsdiid; + snd_hdac_chip_writew(sdw->link_res->hbus, WAKEEN, wake_en); /* Clear wake status (W1C) */ - wake_sts = intel_readw(shim_vs, SDW_SHIM2_INTEL_VS_WAKESTS); - wake_sts |= SDW_SHIM2_INTEL_VS_WAKEEN_PWS; - intel_writew(shim_vs, SDW_SHIM2_INTEL_VS_WAKESTS, wake_sts); + wake_sts = snd_hdac_chip_readw(sdw->link_res->hbus, STATESTS); + wake_sts |= lsdiid; + snd_hdac_chip_writew(sdw->link_res->hbus, STATESTS, wake_sts); } +unlock: + mutex_unlock(sdw->link_res->shim_lock); } static int intel_link_power_up(struct sdw_intel *sdw) @@ -74,36 +129,45 @@ static int intel_link_power_up(struct sdw_intel *sdw) struct sdw_master_prop *prop = &bus->prop; u32 *shim_mask = sdw->link_res->shim_mask; unsigned int link_id = sdw->instance; + u32 clock_source; u32 syncprd; int ret; + if (prop->mclk_freq % 6000000) { + if (prop->mclk_freq % 2400000) { + syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24_576; + clock_source = SDW_SHIM2_MLCS_CARDINAL_CLK; + } else { + syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4; + clock_source = SDW_SHIM2_MLCS_XTAL_CLK; + } + } else { + syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_96; + clock_source = SDW_SHIM2_MLCS_AUDIO_PLL_CLK; + } + mutex_lock(sdw->link_res->shim_lock); + ret = hdac_bus_eml_sdw_power_up_unlocked(sdw->link_res->hbus, link_id); + if (ret < 0) { + dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_power_up failed: %d\n", + __func__, ret); + goto out; + } + + intel_shim_vs_set_clock_source(sdw, clock_source); + if (!*shim_mask) { /* we first need to program the SyncPRD/CPU registers */ dev_dbg(sdw->cdns.dev, "first link up, programming SYNCPRD\n"); - if (prop->mclk_freq % 6000000) - syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4; - else - syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24; - ret = hdac_bus_eml_sdw_set_syncprd_unlocked(sdw->link_res->hbus, syncprd); if (ret < 0) { dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_set_syncprd failed: %d\n", __func__, ret); goto out; } - } - - ret = hdac_bus_eml_sdw_power_up_unlocked(sdw->link_res->hbus, link_id); - if (ret < 0) { - dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_power_up failed: %d\n", - __func__, ret); - goto out; - } - if (!*shim_mask) { /* SYNCPU will change once link is active */ ret = hdac_bus_eml_sdw_wait_syncpu_unlocked(sdw->link_res->hbus); if (ret < 0) { @@ -111,6 +175,9 @@ static int intel_link_power_up(struct sdw_intel *sdw) __func__, ret); goto out; } + + hdac_bus_eml_enable_interrupt_unlocked(sdw->link_res->hbus, true, + AZX_REG_ML_LEPTR_ID_SDW, true); } *shim_mask |= BIT(link_id); @@ -137,6 +204,10 @@ static int intel_link_power_down(struct sdw_intel *sdw) *shim_mask &= ~BIT(link_id); + if (!*shim_mask) + hdac_bus_eml_enable_interrupt_unlocked(sdw->link_res->hbus, true, + AZX_REG_ML_LEPTR_ID_SDW, false); + ret = hdac_bus_eml_sdw_power_down_unlocked(sdw->link_res->hbus, link_id); if (ret < 0) { dev_err(sdw->cdns.dev, "%s: hdac_bus_eml_sdw_power_down failed: %d\n", @@ -247,7 +318,6 @@ static int intel_hw_params(struct snd_pcm_substream *substream, struct sdw_cdns_dai_runtime *dai_runtime; struct sdw_cdns_pdi *pdi; struct sdw_stream_config sconfig; - struct sdw_port_config *pconfig; int ch, dir; int ret; @@ -262,11 +332,13 @@ static int intel_hw_params(struct snd_pcm_substream *substream, dir = SDW_DATA_DIR_TX; pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id); + if (!pdi) + return -EINVAL; - if (!pdi) { - ret = -EINVAL; - goto error; - } + /* use same definitions for alh_id as previous generations */ + pdi->intel_alh_id = (sdw->instance * 16) + pdi->num + 3; + if (pdi->num >= 2) + pdi->intel_alh_id += 2; /* the SHIM will be configured in the callback functions */ @@ -282,7 +354,7 @@ static int intel_hw_params(struct snd_pcm_substream *substream, sdw->instance, pdi->intel_alh_id); if (ret) - goto error; + return ret; sconfig.direction = dir; sconfig.ch_count = ch; @@ -292,11 +364,10 @@ static int intel_hw_params(struct snd_pcm_substream *substream, sconfig.bps = snd_pcm_format_width(params_format(params)); /* Port configuration */ - pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL); - if (!pconfig) { - ret = -ENOMEM; - goto error; - } + struct sdw_port_config *pconfig __free(kfree) = kzalloc(sizeof(*pconfig), + GFP_KERNEL); + if (!pconfig) + return -ENOMEM; pconfig->num = pdi->num; pconfig->ch_mask = (1 << ch) - 1; @@ -306,19 +377,18 @@ static int intel_hw_params(struct snd_pcm_substream *substream, if (ret) dev_err(cdns->dev, "add master to stream failed:%d\n", ret); - kfree(pconfig); -error: return ret; } static int intel_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_cdns_dai_runtime *dai_runtime; + struct snd_pcm_hw_params *hw_params; int ch, dir; - int ret = 0; dai_runtime = cdns->dai_runtime_array[dai->id]; if (!dai_runtime) { @@ -327,12 +397,8 @@ static int intel_prepare(struct snd_pcm_substream *substream, return -EIO; } + hw_params = &rtd->dpcm[substream->stream].hw_params; if (dai_runtime->suspended) { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_pcm_hw_params *hw_params; - - hw_params = &rtd->dpcm[substream->stream].hw_params; - dai_runtime->suspended = false; /* @@ -353,15 +419,11 @@ static int intel_prepare(struct snd_pcm_substream *substream, /* the SHIM will be configured in the callback functions */ sdw_cdns_config_stream(cdns, ch, dir, dai_runtime->pdi); - - /* Inform DSP about PDI stream number */ - ret = intel_params_stream(sdw, substream, dai, - hw_params, - sdw->instance, - dai_runtime->pdi->intel_alh_id); } - return ret; + /* Inform DSP about PDI stream number */ + return intel_params_stream(sdw, substream, dai, hw_params, sdw->instance, + dai_runtime->pdi->intel_alh_id); } static int @@ -644,10 +706,30 @@ static void intel_program_sdi(struct sdw_intel *sdw, int dev_num) __func__, sdw->instance, dev_num); } +static int intel_get_link_count(struct sdw_intel *sdw) +{ + int ret; + + ret = hdac_bus_eml_get_count(sdw->link_res->hbus, true, AZX_REG_ML_LEPTR_ID_SDW); + if (!ret) { + dev_err(sdw->cdns.dev, "%s: could not retrieve link count\n", __func__); + return -ENODEV; + } + + if (ret > SDW_INTEL_MAX_LINKS) { + dev_err(sdw->cdns.dev, "%s: link count %d exceed max %d\n", __func__, ret, SDW_INTEL_MAX_LINKS); + return -EINVAL; + } + + return ret; +} + const struct sdw_intel_hw_ops sdw_intel_lnl_hw_ops = { .debugfs_init = intel_ace2x_debugfs_init, .debugfs_exit = intel_ace2x_debugfs_exit, + .get_link_count = intel_get_link_count, + .register_dai = intel_register_dai, .check_clock_stop = intel_check_clock_stop, @@ -672,6 +754,6 @@ const struct sdw_intel_hw_ops sdw_intel_lnl_hw_ops = { .program_sdi = intel_program_sdi, }; -EXPORT_SYMBOL_NS(sdw_intel_lnl_hw_ops, SOUNDWIRE_INTEL); +EXPORT_SYMBOL_NS(sdw_intel_lnl_hw_ops, "SOUNDWIRE_INTEL"); -MODULE_IMPORT_NS(SND_SOC_SOF_HDA_MLINK); +MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK"); |