[PATCH 2/4] ALSA: hda: intel-nhlt: add intel_nhlt_ssp_mclk_mask()

Amadeusz Sławiński amadeuszx.slawinski at linux.intel.com
Tue Aug 23 10:33:40 CEST 2022


On 8/22/2022 8:59 PM, Pierre-Louis Bossart wrote:
> SOF topologies hard-code the MCLK used for SSP connections. That was a
> bad idea in hindsight, this information should really come from BIOS
> and/or machine driver.
> 
> This patch introduces a helper to scan all SSP endpoints connected to
> a codec, and all formats to see what MCLK is used. When BIT(0) of the
> mdivc offset if set in the SSP blob, MCLK0 is used, and likewise when
> BIT(1) is set MCLK1 is used.
> 
> The case where both MCLKs are used is possible but has never been seen
> in practice so should be treated as an error by the caller.
> 
> Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart at linux.intel.com>
> Reviewed-by: Kai Vehmanen <kai.vehmanen at linux.intel.com>
> Reviewed-by: Bard Liao <yung-chuan.liao at linux.intel.com>
> ---
>   include/sound/intel-nhlt.h |  7 +++++
>   sound/hda/intel-nhlt.c     | 61 ++++++++++++++++++++++++++++++++++++++
>   2 files changed, 68 insertions(+)
> 
> diff --git a/include/sound/intel-nhlt.h b/include/sound/intel-nhlt.h
> index 3d5cf201cd802..53470d6a28d65 100644
> --- a/include/sound/intel-nhlt.h
> +++ b/include/sound/intel-nhlt.h
> @@ -136,6 +136,8 @@ bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, u8 link_type);
>   
>   int intel_nhlt_ssp_endpoint_mask(struct nhlt_acpi_table *nhlt, u8 device_type);
>   
> +int intel_nhlt_ssp_mclk_mask(struct nhlt_acpi_table *nhlt, int ssp_num);
> +
>   struct nhlt_specific_cfg *
>   intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt,
>   			     u32 bus_id, u8 link_type, u8 vbps, u8 bps,
> @@ -169,6 +171,11 @@ static inline int intel_nhlt_ssp_endpoint_mask(struct nhlt_acpi_table *nhlt, u8
>   	return 0;
>   }
>   
> +static inline int intel_nhlt_ssp_mclk_mask(struct nhlt_acpi_table *nhlt, int ssp_num)
> +{
> +	return 0;
> +}
> +
>   static inline struct nhlt_specific_cfg *
>   intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt,
>   			     u32 bus_id, u8 link_type, u8 vbps, u8 bps,
> diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c
> index 13bb0ccfb36c0..0323aedb6ecf4 100644
> --- a/sound/hda/intel-nhlt.c
> +++ b/sound/hda/intel-nhlt.c
> @@ -157,6 +157,67 @@ int intel_nhlt_ssp_endpoint_mask(struct nhlt_acpi_table *nhlt, u8 device_type)
>   }
>   EXPORT_SYMBOL(intel_nhlt_ssp_endpoint_mask);
>   
> +#define SSP_BLOB_V1_0_SIZE		84
> +#define SSP_BLOB_V1_0_MDIVC_OFFSET	19 /* offset in u32 */
> +#define SSP_BLOB_V1_5_SIZE		96
> +#define SSP_BLOB_V1_5_MDIVC_OFFSET	21 /* offset in u32 */
> +#define SSP_BLOB_VER_1_5		0xEE000105
> +#define SSP_BLOB_V2_0_MDIVC_OFFSET	20 /* offset in u32 */
> +#define SSP_BLOB_VER_2_0		0xEE000200
> +
> +int intel_nhlt_ssp_mclk_mask(struct nhlt_acpi_table *nhlt, int ssp_num)
> +{
> +	struct nhlt_endpoint *epnt;
> +	struct nhlt_fmt *fmt;
> +	struct nhlt_fmt_cfg *cfg;
> +	int mclk_mask = 0;
> +	int i, j;
> +
> +	if (!nhlt)
> +		return 0;
> +
> +	epnt = (struct nhlt_endpoint *)nhlt->desc;
> +	for (i = 0; i < nhlt->endpoint_count; i++) {
> +
> +		/* we only care about endpoints connected to an audio codec over SSP */
> +		if (epnt->linktype == NHLT_LINK_SSP &&
> +		    epnt->device_type == NHLT_DEVICE_I2S &&
> +		    epnt->virtual_bus_id == ssp_num) {

if (epnt->linktype != NHLT_LINK_SSP ||
     epnt->device_type != NHLT_DEVICE_I2S ||
     epnt->virtual_bus_id != ssp_num)
	continue;

and then you can remove one indentation level below?

> +
> +			fmt = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size);
> +			cfg = fmt->fmt_config;
> +
> +			/*
> +			 * In theory all formats should use the same MCLK but it doesn't hurt to
> +			 * double-check that the configuration is consistent
> +			 */
> +			for (j = 0; j < fmt->fmt_count; j++) {
> +				u32 *blob;
> +				int mdivc_offset;
> +
> +				if (cfg->config.size >= SSP_BLOB_V1_0_SIZE) {
> +					blob = (u32 *)cfg->config.caps;
> +
> +					if (blob[1] == SSP_BLOB_VER_2_0)
> +						mdivc_offset = SSP_BLOB_V2_0_MDIVC_OFFSET;
> +					else if (blob[1] == SSP_BLOB_VER_1_5)
> +						mdivc_offset = SSP_BLOB_V1_5_MDIVC_OFFSET;
> +					else
> +						mdivc_offset = SSP_BLOB_V1_0_MDIVC_OFFSET;
> +
> +					mclk_mask |=  blob[mdivc_offset] & GENMASK(1, 0);
> +				}
> +
> +				cfg = (struct nhlt_fmt_cfg *)(cfg->config.caps + cfg->config.size);
> +			}
> +		}
> +		epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
> +	}
> +
> +	return mclk_mask;

Although I understand that it is relegated to the caller, but if both 
mclk being set is considered an error maybe add some kind of check here 
instead and free callers from having to remember about it?

if (hweight_long(mclk_mask) != 1)
	return -EINVAL;

return mclk_mask;

> +}
> +EXPORT_SYMBOL(intel_nhlt_ssp_mclk_mask);
> +
>   static struct nhlt_specific_cfg *
>   nhlt_get_specific_cfg(struct device *dev, struct nhlt_fmt *fmt, u8 num_ch,
>   		      u32 rate, u8 vbps, u8 bps)



More information about the Alsa-devel mailing list