Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect SSP port number from NHLT table #4492

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion include/sound/intel-nhlt.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ enum nhlt_device_type {
NHLT_DEVICE_INVALID
};

enum nhlt_direction_type {
NHLT_DIRECTION_RENDER = 0,
NHLT_DIRECTION_CAPTURE = 1,
NHLT_DIRECTION_INVALID
};

struct wav_fmt {
u16 fmt_tag;
u16 channels;
Expand Down Expand Up @@ -112,7 +118,9 @@ struct nhlt_vendor_dmic_array_config {

enum {
NHLT_CONFIG_TYPE_GENERIC = 0,
NHLT_CONFIG_TYPE_MIC_ARRAY = 1
NHLT_CONFIG_TYPE_MIC_ARRAY = 1,
NHLT_CONFIG_TYPE_RENDER_WITH_LOOPBACK = 2,
NHLT_CONFIG_TYPE_RENDER_FEEDBACK = 3,
};

enum {
Expand Down Expand Up @@ -143,6 +151,9 @@ intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt,
u32 bus_id, u8 link_type, u8 vbps, u8 bps,
u8 num_ch, u32 rate, u8 dir, u8 dev_type);

int intel_nhlt_ssp_dir_mask(struct device *dev, struct nhlt_acpi_table *nhlt,
u8 dir);

#else

static inline struct nhlt_acpi_table *intel_nhlt_init(struct device *dev)
Expand Down Expand Up @@ -184,6 +195,12 @@ intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt,
return NULL;
}

static inline int intel_nhlt_ssp_dir_mask(struct device *dev,
struct nhlt_acpi_table *nhlt, u8 dir)
{
return 0;
}

#endif

#endif
46 changes: 46 additions & 0 deletions sound/hda/intel-nhlt.c
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,49 @@ intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt,
return NULL;
}
EXPORT_SYMBOL(intel_nhlt_get_endpoint_blob);

int intel_nhlt_ssp_dir_mask(struct device *dev, struct nhlt_acpi_table *nhlt,
u8 dir)
{
struct nhlt_endpoint *epnt;
struct nhlt_device_specific_config *cfg;
int dir_mask = 0;
int i;

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->direction == dir) {
switch (epnt->direction) {
case NHLT_DIRECTION_CAPTURE:
if (epnt->config.size) {
cfg = (struct nhlt_device_specific_config *)(epnt->config.caps);
/* smartamp feedback or FW-generated echo
* reference do not count
*/
if (cfg->config_type == NHLT_CONFIG_TYPE_RENDER_FEEDBACK)
break;
} else
dev_warn(dev, "%s: missing device specific config, vbus=%d\n",
__func__, epnt->virtual_bus_id);
fallthrough;
case NHLT_DIRECTION_RENDER:
/* for SSP the virtual bus id is the SSP port */
dir_mask |= BIT(epnt->virtual_bus_id);
break;
default:
break;
}
}

epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
}

return dir_mask;
}
EXPORT_SYMBOL(intel_nhlt_ssp_dir_mask);
50 changes: 42 additions & 8 deletions sound/soc/intel/boards/sof_rt5682.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
#define SOF_SSP_HDMI_CAPTURE_PRESENT_MASK (GENMASK(30, 27))
#define SOF_HDMI_CAPTURE_SSP_MASK(quirk) \
(((quirk) << SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT) & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK)
#define SOF_TPLG_NHLT_SSP_DETECT BIT(31)

/* Default: MCLK on, MCLK 19.2M, SSP0 */
static unsigned long sof_rt5682_quirk = SOF_RT5682_MCLK_EN |
Expand Down Expand Up @@ -560,6 +561,22 @@ static int sof_card_late_probe(struct snd_soc_card *card)
return hdac_hdmi_jack_port_init(component, &card->dapm);
}

static int soc_card_add_dai_link(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_link)
{
if (!strcmp(dai_link->name, "NHLT-RENDER-CAPTURE") ||
!strcmp(dai_link->name, "NHLT-RENDER-ONLY") ||
!strcmp(dai_link->name, "NHLT-CAPTURE-ONLY") ||
!strcmp(dai_link->name, "NHLT-BT")) {
/* fixup and register this dai link later when we receive NHLT
* table from IPC4 topology
*/
dai_link->ignore = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what this 'ignore' means but it certainly does not immediately translate as 'fixup and register later'.

Copy link
Author

@brentlu brentlu Jul 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

soc-core(soc_check_tplg_fes() function) uses this flag to skip FE dai links in machine driver. Here we use this flag to make snd_soc_add_pcm_runtime() function skipping this dai link.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"borrow this flag" really means overloading a concept used for FE and extend it for BEs. That's not so good, this will add confusions left and right. We should be really careful with such moves.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked the soc.h and the flag means "do not create pcm". Maybe the reason to use this flag could be different? soc-core uses this flag to skip FE links and we use it to skip links which link/stream names need to be resolved later.

/* Do not create a PCM for this DAI link (Backend link) */
unsigned int ignore:1;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's best to avoid using the same flag for two completely different purposes, which means adding a new one with a clearly defined behavior.

I am still in the dark on what the problem statement is and why the SOF driver is involved.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree we cannot repurpose an existing flag for a compltely different purpose

}

return 0;
}

static const struct snd_kcontrol_new sof_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
Expand Down Expand Up @@ -703,8 +720,12 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
goto devm_err;

/* codec SSP */
links[id].name = devm_kasprintf(dev, GFP_KERNEL,
"SSP%d-Codec", ssp_codec);
if (sof_rt5682_quirk & SOF_TPLG_NHLT_SSP_DETECT)
links[id].name = devm_kasprintf(dev, GFP_KERNEL,
"NHLT-RENDER-CAPTURE");
else
links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

won't this break IPC3?

ssp_codec);
if (!links[id].name)
goto devm_err;

Expand Down Expand Up @@ -830,8 +851,12 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,

/* speaker amp */
if (sof_rt5682_quirk & SOF_SPEAKER_AMP_PRESENT) {
links[id].name = devm_kasprintf(dev, GFP_KERNEL,
"SSP%d-Codec", ssp_amp);
if (sof_rt5682_quirk & SOF_TPLG_NHLT_SSP_DETECT)
links[id].name = devm_kasprintf(dev, GFP_KERNEL,
"NHLT-RENDER-ONLY");
else
links[id].name = devm_kasprintf(dev, GFP_KERNEL,
"SSP%d-Codec", ssp_amp);
if (!links[id].name)
goto devm_err;

Expand Down Expand Up @@ -906,23 +931,29 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
int port = (sof_rt5682_quirk & SOF_BT_OFFLOAD_SSP_MASK) >>
SOF_BT_OFFLOAD_SSP_SHIFT;

if (sof_rt5682_quirk & SOF_TPLG_NHLT_SSP_DETECT)
links[id].name = devm_kasprintf(dev, GFP_KERNEL,
"NHLT-BT");
else
links[id].name = devm_kasprintf(dev, GFP_KERNEL,
"SSP%d-BT", port);
if (!links[id].name)
goto devm_err;

links[id].id = id;
links[id].cpus = &cpus[id];
links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
"SSP%d Pin", port);
if (!links[id].cpus->dai_name)
goto devm_err;
links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port);
if (!links[id].name)
goto devm_err;
links[id].num_cpus = 1;
links[id].codecs = &asoc_dummy_dlc;
links[id].num_codecs = 1;
links[id].platforms = platform_component;
links[id].num_platforms = ARRAY_SIZE(platform_component);
links[id].dpcm_playback = 1;
links[id].dpcm_capture = 1;
links[id].no_pcm = 1;
links[id].num_cpus = 1;
}

/* HDMI-In SSP */
Expand Down Expand Up @@ -1079,6 +1110,9 @@ static int sof_audio_probe(struct platform_device *pdev)
if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_PRESENT)
sof_rt1015_codec_conf(&sof_audio_card_rt5682);

if (sof_rt5682_quirk & SOF_TPLG_NHLT_SSP_DETECT)
sof_audio_card_rt5682.add_dai_link = soc_card_add_dai_link;

INIT_LIST_HEAD(&ctx->hdmi_pcm_list);

sof_audio_card_rt5682.dev = &pdev->dev;
Expand Down
96 changes: 91 additions & 5 deletions sound/soc/sof/ipc4-topology.c
Original file line number Diff line number Diff line change
Expand Up @@ -1297,8 +1297,9 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct nhlt_specific_cfg *cfg;
int sample_rate, channel_count;
int bit_depth, ret;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commit message s/direcion/direction

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

int bit_depth, ret, i;
u32 nhlt_type;
static const u8 nhlt_devices[] = {NHLT_DEVICE_I2S, NHLT_DEVICE_BT};

/* convert to NHLT type */
switch (linktype) {
Expand All @@ -1323,9 +1324,15 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s
dai_index, nhlt_type, dir);

/* find NHLT blob with matching params */
cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt, dai_index, nhlt_type,
bit_depth, bit_depth, channel_count, sample_rate,
dir, 0);
for (i = 0; i < ARRAY_SIZE(nhlt_devices); i++) {
cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt,
dai_index, nhlt_type, bit_depth,
bit_depth, channel_count, sample_rate,
dir, nhlt_devices[i]);
if (cfg || (linktype == SOF_DAI_INTEL_DMIC))
goto cfg_found;
}
cfg_found:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the main issue I have is that this is not backwards compatible. You need a new topology to get this to work. Is this not too late already?

@ranj063 please chime in here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's backward compatible. Because current implementation set the device type of all endpoints to 0 (BT), and use Render(0) and Capture(1) in direction field. So current topologies could work with the code.


if (!cfg) {
dev_err(sdev->dev,
Expand Down Expand Up @@ -2725,6 +2732,78 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
return 0;
}

static int sof_ipc4_nhlt_register_dai_links(struct snd_soc_component *scomp)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding dai links in the SOF ipc4 code is a major layering violation. This belongs in machine drivers IMHO. The SOF driver should only add CPU dais, not muck with dai links.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is topology is loaded after machine driver registers soundcard. If we want to extract ssp numbers from NHLT, then we have to register the dai links in sof-topology layer...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a bit too much for me... I am not hot on hard-coding such names in a generic layer. need to think a bit more on all this.

{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct snd_soc_card *card = scomp->card;
struct snd_soc_dai_link *dai_link = card->dai_link;
int capture_mask, render_mask;
int ssp_mask, ssp_num;
int i, is_bt, ret;

capture_mask = intel_nhlt_ssp_dir_mask(scomp->dev, ipc4_data->nhlt, NHLT_DIRECTION_CAPTURE);
render_mask = intel_nhlt_ssp_dir_mask(scomp->dev, ipc4_data->nhlt, NHLT_DIRECTION_RENDER);

for (i = 0; i < card->num_links; i++, dai_link++) {
if (!strcmp(dai_link->name, "NHLT-RENDER-CAPTURE")) {
/* headset codec */
ssp_mask = render_mask & capture_mask;
is_bt = 0;
} else if (!strcmp(dai_link->name, "NHLT-RENDER-ONLY")) {
/* speaker amplifier */
ssp_mask = render_mask & ~capture_mask;
is_bt = 0;
} else if (!strcmp(dai_link->name, "NHLT-CAPTURE-ONLY")) {
/* HDMI in */
ssp_mask = ~render_mask & capture_mask;
is_bt = 0;
} else if (!strcmp(dai_link->name, "NHLT-BT")) {
/* bluetooth sideband */
ssp_mask = intel_nhlt_ssp_endpoint_mask(ipc4_data->nhlt,
NHLT_DEVICE_BT);
is_bt = 1;
} else
continue;

if (hweight32(ssp_mask) != 1) {
dev_warn(scomp->dev, "Invalid mask 0x%x for dai link %s\n",
ssp_mask, dai_link->name);
continue;
}

ssp_num = ffs(ssp_mask) - 1; /* ffs returns 1-based result */

if (is_bt)
dai_link->name = devm_kasprintf(card->dev, GFP_KERNEL,
"SSP%d-BT", ssp_num);
else
dai_link->name = devm_kasprintf(card->dev, GFP_KERNEL,
"SSP%d-Codec", ssp_num);
if (!dai_link->name)
return -ENOMEM;

/* stream_name is initialized in soc_check_tplg_fes() function
* with dai link name */
dai_link->stream_name = devm_kstrdup(card->dev, dai_link->name,
GFP_KERNEL);
if (!dai_link->stream_name)
return -ENOMEM;

dai_link->cpus->dai_name = devm_kasprintf(card->dev, GFP_KERNEL,
"SSP%d Pin", ssp_num);
if (!dai_link->cpus->dai_name)
return -ENOMEM;

dai_link->ignore = false;
ret = snd_soc_add_pcm_runtimes(card, dai_link, 1);
if (ret < 0)
return ret;
}

return 0;
}

static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_manifest *man)
{
Expand All @@ -2735,7 +2814,7 @@ static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index,
u32 size = le32_to_cpu(man->priv.size);
u8 *man_ptr = man->priv.data;
u32 len_check;
int i;
int i, ret;

if (!size || size < SOF_IPC4_TPLG_ABI_SIZE) {
dev_err(scomp->dev, "%s: Invalid topology ABI size: %u\n",
Expand Down Expand Up @@ -2773,6 +2852,13 @@ static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index,
le32_to_cpu(manifest_tlv->size), GFP_KERNEL);
if (!ipc4_data->nhlt)
return -ENOMEM;

/* register 'ignored' dai links which link name and
* codec dai name need fixup with info from NHLT table
*/
ret = sof_ipc4_nhlt_register_dai_links(scomp);
if (ret)
return ret;
break;
default:
dev_warn(scomp->dev, "Skipping unknown manifest data type %d\n",
Expand Down