Skip to content

Conversation

@johnylin76
Copy link
Contributor

commit message

At present, dax instance is initialized and allocated inner buffer on module_init(), and released on module_free(). The memory requirement for the inner buffer per instance is quite large.

The existing implementation has 2 pipelines containing dax. Even though the host is restricted from processing stream on both pipelines simultaneously, they may coexist with each other under some circumstances e.g. the interim when switching PCM device from one to the other, which may drain out the memory.

This commit changes the timing of instance allocation/deallocation to module_prepare()/reset() respectively. That is, dax instance only occupies the inner buffer memory when processing.

This commit is to fix the following bug on the targeting Google device:

bug report

Steps to Reproduce:

  1. Play audio file.
  2. Plug headphone/headset in system.
  3. Check sound output from headphone/ headset.
  4. Change output to internal speaker.
  5. Check sound output from internal speaker.

Expected behavior:
Switch to the internal speaker output with sound,when connecting headphones to play music.

What do you see instead?
Switch to the internal speaker output without sound,when connecting headphones to play music.

Fail rate:
2 out of 2 units,6 out of 40 Times

At present, dax instance is initialized and allocated inner buffer
on module_init(), and released on module_free(). The memory
requirement for the inner buffer per instance is quite large.

The existing implementation has 2 pipelines containing dax. Even
though the host is restricted from processing stream on both
pipelines simultaneously, they may coexist with each other under some
circumstances e.g. the interim when switching PCM device from one to
the other, which may drain out the memory.

This commit changes the timing of instance allocation/deallocation to
module_prepare()/reset() respectively. That is, dax instance only
occupies the inner buffer memory when processing.

Signed-off-by: Johny Lin <[email protected]>
@johnylin76
Copy link
Contributor Author

Will need review by Dolby folks @checkupup

Copy link
Member

@lgirdwood lgirdwood left a comment

Choose a reason for hiding this comment

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

@johnylin76 IIUC, you have 2 pipelines today, each with a DAX instance that allocates a large inner buffer. I'm not following why this fixes your issue, I can only assume that this change only allocates 1 mutually exclusive inner buffer as perhaps allocating > 1 inner buffer was failing ?

@@ -476,6 +476,51 @@ static int sof_dax_free(struct processing_module *mod)
dax_buffer_release(mod, &dax_ctx->tuning_file_buffer);
dax_buffer_release(mod, &dax_ctx->input_buffer);
dax_buffer_release(mod, &dax_ctx->output_buffer);
Copy link
Contributor

@checkupup checkupup Jan 30, 2026

Choose a reason for hiding this comment

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

Since only persist and scratch buffers are allocated during entablishment phase, we could be only responsible for releasing them during the destruction phase:

static void destroy_instance(struct processing_module *mod)
{
	struct sof_dax *dax_ctx = module_get_private_data(mod);

	dax_free(dax_ctx);
	dax_buffer_release(mod, &dax_ctx->persist_buffer);
	dax_buffer_release(mod, &dax_ctx->scratch_buffer);
}

{
struct sof_dax *dax_ctx = module_get_private_data(mod);

destroy_instance(mod);
Copy link
Contributor

@checkupup checkupup Jan 30, 2026

Choose a reason for hiding this comment

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

if sof_dax_reset can always be called before sof_dax_free phase, destruction may be not necessary here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Theoretically yes, but I don't feel we should rely on that assumption.

The memory free() function of C causes no harm if the input pointer is null. Your dax_buffer_release() implementation is handled well so they are good to be called more than once.

My only concern is that I am not sure if calling dax_free() twice is also good because this is a private call

Copy link
Contributor

Choose a reason for hiding this comment

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

if so, following releases are also required here:

dax_buffer_release(mod, &dax_ctx->tuning_file_buffer);
dax_buffer_release(mod, &dax_ctx->input_buffer);
dax_buffer_release(mod, &dax_ctx->output_buffer);

dax_free() is safe for multiple frees.

ret = establish_instance(mod);
if (ret != 0)
return ret;

Copy link
Contributor

@checkupup checkupup Jan 30, 2026

Choose a reason for hiding this comment

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

It might be better to place establish_instance after check_media_format to avoid unnecessary destruction if check_media_format fails.
On the other side, we need to add the option CONFIG_IDC_TIMEOUT_US=50000 to the file app/overlays/[ptl/mtl]/dax_overlay.conf to avoid IPC_TIMEOUT errors, because dax_init takes more than 20 ms, exceeding default IDC_TIMEOUT value (10ms).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's also I worried about. But I don't get why dax_init() takes so long because at that moment DAX didn't even get the configuration data (from dax_param.bin). Or does DAX make the advance calculation on dax_init() such as configuration prediction?

We have verified without changing CONFIG_IDC_TIMEOUT_US and didn't get timeout problem (I assume the timeout should lead to a stable failure if it really cares). Could you check with that?

Copy link
Contributor

Choose a reason for hiding this comment

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

You can set DOLBY_DAX_CORE_ID as 1 (or other different cores with pipeline) to trigger IDC timer.
dax_init will perform a large number of initialization operations based on the provided memory and load some static data.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see. You are correct "IDC" is for the inter-CPU communication.

As for dax_init(), do you mean "loading static data" to memory allocated on runtime for speeding the later computation? I remember we didn't use DRAM for ADSP in the current device so it still sounds weird to me.

struct comp_dev *dev = mod->dev;

/* dax instance will be established on prepare(), and destroyed on reset() */
destroy_instance(mod);
Copy link
Contributor

Choose a reason for hiding this comment

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

Simultaneously release following buffers here:

dax_buffer_release(mod, &dax_ctx->tuning_file_buffer);
dax_buffer_release(mod, &dax_ctx->input_buffer);
dax_buffer_release(mod, &dax_ctx->output_buffer);

Becasue these buffers are also allocated in sof_dax_prepare. If a lifecycle of dax instance is from prepare to reset phrase, then I believe that all resources created in sof_dax_prepare should have the same lifecycle.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. The better practice is releasing all runtime buffers when reset.

Copy link
Contributor

@checkupup checkupup left a comment

Choose a reason for hiding this comment

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

Thanks @johnylin76 , I added some comments

@johnylin76
Copy link
Contributor Author

@johnylin76 IIUC, you have 2 pipelines today, each with a DAX instance that allocates a large inner buffer. I'm not following why this fixes your issue, I can only assume that this change only allocates 1 mutually exclusive inner buffer as perhaps allocating > 1 inner buffer was failing ?

Yes. The majority comes from the inner (scratch and persist) buffer. The memory will be drained out when both DAX instances allocate that.

By host control, we can make sure 2 DAX instances won't run process simultaneously, although they may be lived together. Consequently, we let DAX allocate the inner buffer when triggered start to process (and deallocate when stopped)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants