From: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
To: Niranjan H Y <niranjan.hy@ti.com>, linux-sound@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, broonie@kernel.org,
ckeepax@opensource.cirrus.com, lgirdwood@gmail.com,
perex@perex.cz, tiwai@suse.com, cezary.rojewski@intel.com,
peter.ujfalusi@linux.intel.com, yung-chuan.liao@linux.intel.com,
ranjani.sridharan@linux.intel.com, kai.vehmanen@linux.intel.com,
baojun.xu@ti.com, shenghao-ding@ti.com, sandeepk@ti.com,
v-hampiholi@ti.com
Subject: Re: [PATCH v11 2/4] ASoC: tac5xx2-sdw: add soundwire based codec driver
Date: Tue, 28 Apr 2026 21:57:45 +0200 [thread overview]
Message-ID: <7137df6a-5e49-4516-a356-3414f7d14c33@linux.dev> (raw)
In-Reply-To: <20260427082401.2118-2-niranjan.hy@ti.com>
> +struct tac5xx2_prv {
> + struct snd_soc_component *component;
> + struct sdw_slave *sdw_peripheral;
> + struct sdca_function_data *sa_func_data;
> + struct sdca_function_data *sm_func_data;
> + struct sdca_function_data *uaj_func_data;
> + struct sdca_function_data *hid_func_data;
> + enum sdw_slave_status status;
> + /* serialize potential concurrent uaj detection in .hw_params
> + * during playback session and .interrupt_callback.
> + */
> + struct mutex uaj_lock;
is this uaj_lock really needed, if you look below at hw_params there's nothing that looks even remotely tied to jack detection...
> + struct regmap *regmap;
> + struct device *dev;
> + bool hw_init;
> + bool first_hw_init;
> + u32 part_id;
> + struct snd_soc_jack *hs_jack;
> + int jack_type;
> + /* Custom fw binary. UMP File Download is not used. */
> + unsigned int fw_file_cnt;
> + struct tac_fw_file *fw_files;
> + struct completion fw_caching_complete;
> + bool fw_dl_success;
> + u8 fw_binaryname[64];
> +};
> +static int tac_sdw_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params,
> + struct snd_soc_dai *dai)
> +{
> + struct snd_soc_component *component = dai->component;
> + struct tac5xx2_prv *tac_dev = snd_soc_component_get_drvdata(component);
> + struct sdw_stream_config stream_config = {0};
> + struct sdw_port_config port_config = {0};
> + struct sdw_stream_runtime *sdw_stream;
> + struct sdw_slave *sdw_peripheral = tac_dev->sdw_peripheral;
> + int ret;
> + int function_id;
> + int pde_entity;
> + int port_num;
> + u8 sample_rate_idx = 0;
> +
> + if (!tac_dev->hw_init) {
> + dev_err(tac_dev->dev,
> + "error: operation without hw initialization");
> + return -EINVAL;
> + }
> +
> + sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
> + if (!sdw_stream) {
> + dev_err(tac_dev->dev, "failed to get dma data");
> + return -EINVAL;
> + }
> +
> + ret = tac_clear_latch(tac_dev);
> + if (ret)
> + dev_warn(tac_dev->dev, "clear latch failed, err=%d", ret);
> +
> + switch (dai->id) {
> + case TAC5XX2_DMIC:
> + function_id = TAC_FUNCTION_ID_SM;
> + pde_entity = TAC_SDCA_ENT_PDE11;
> + port_num = TAC_SDW_PORT_NUM_DMIC;
> + break;
> + case TAC5XX2_UAJ:
> + function_id = TAC_FUNCTION_ID_UAJ;
> + pde_entity = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
> + TAC_SDCA_ENT_PDE47 : TAC_SDCA_ENT_PDE34;
> + port_num = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
> + TAC_SDW_PORT_NUM_UAJ_PLAYBACK :
> + TAC_SDW_PORT_NUM_UAJ_CAPTURE;
> + break;
> + case TAC5XX2_SPK:
> + function_id = TAC_FUNCTION_ID_SA;
> + pde_entity = TAC_SDCA_ENT_PDE23;
> + port_num = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
> + TAC_SDW_PORT_NUM_SPK_PLAYBACK :
> + TAC_SDW_PORT_NUM_SPK_CAPTURE;
> + break;
> + default:
> + dev_err(tac_dev->dev, "Invalid dai id: %d", dai->id);
> + return -EINVAL;
> + }
> +
> + snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
> + port_config.num = port_num;
> + ret = sdw_stream_add_slave(sdw_peripheral, &stream_config,
> + &port_config, 1, sdw_stream);
> + if (ret) {
> + dev_err(dai->dev,
> + "Unable to configure port %d: %d\n", port_num, ret);
> + return ret;
> + }
> +
> + switch (params_rate(params)) {
> + case 48000:
> + sample_rate_idx = 0x01;
> + break;
> + case 44100:
> + sample_rate_idx = 0x02;
> + break;
> + case 96000:
> + sample_rate_idx = 0x03;
> + break;
> + case 88200:
> + sample_rate_idx = 0x04;
> + break;
> + default:
> + dev_dbg(tac_dev->dev, "Unsupported sample rate: %d Hz",
> + params_rate(params));
> + return -EINVAL;
> + }
> +
> + switch (function_id) {
> + case TAC_FUNCTION_ID_SM:
> + ret = regmap_write(tac_dev->regmap,
> + SDW_SDCA_CTL(function_id, TAC_SDCA_ENT_CS113,
> + TAC_SDCA_CTL_CS_SAMP_RATE_IDX, 0),
> + sample_rate_idx);
> + if (ret) {
> + dev_err(tac_dev->dev, "Failed to set CS113 sample rate: %d", ret);
> + return ret;
> + }
> +
> + break;
> + case TAC_FUNCTION_ID_UAJ:
> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> + ret = regmap_write(tac_dev->regmap,
> + SDW_SDCA_CTL(function_id, TAC_SDCA_ENT_CS41,
> + TAC_SDCA_CTL_CS_SAMP_RATE_IDX, 0),
> + sample_rate_idx);
> + if (ret) {
> + dev_err(tac_dev->dev, "Failed to set CS41 sample rate: %d", ret);
> + return ret;
> + }
> + } else {
> + ret = regmap_write(tac_dev->regmap,
> + SDW_SDCA_CTL(function_id, TAC_SDCA_ENT_CS36,
> + TAC_SDCA_CTL_CS_SAMP_RATE_IDX, 0),
> + sample_rate_idx);
> + if (ret) {
> + dev_err(tac_dev->dev, "Failed to set CS36 sample rate: %d", ret);
> + return ret;
> + }
> + }
> + break;
> + case TAC_FUNCTION_ID_SA:
> + /* SmartAmp: no additional sample rate configuration needed */
> + break;
> + }
> +
> + ret = regmap_write(tac_dev->regmap, SDW_SDCA_CTL(function_id, pde_entity,
> + TAC_SDCA_REQUESTED_PS, 0), 0);
> + if (ret) {
> + dev_err(tac_dev->dev, "failed to set PS to 0: %d\n", ret);
> + return ret;
> + }
> +
> + ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, pde_entity,
> + SDCA_PDE_PS3, SDCA_PDE_PS0, NULL, 0);
> + if (ret)
> + dev_err(tac_dev->dev,
> + "failed to transition to PS0, err= %d\n", ret);
> + return ret;
so as I said above there's nothing here that might conflict with the interrupt callback?
What am I missing?
> +}
> +
> +static s32 tac_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + s32 ret;
style nit-pick: reverse xmas-tree
> + struct snd_soc_component *component = dai->component;
> + struct tac5xx2_prv *tac_dev =
> + snd_soc_component_get_drvdata(component);
> + struct sdw_stream_runtime *sdw_stream =
> + snd_soc_dai_get_dma_data(dai, substream);
> + int pde_entity, function_id;
> +
> + sdw_stream_remove_slave(tac_dev->sdw_peripheral, sdw_stream);
> +
> + switch (dai->id) {
> + case TAC5XX2_DMIC:
> + pde_entity = TAC_SDCA_ENT_PDE11;
> + function_id = TAC_FUNCTION_ID_SM;
> + break;
> + case TAC5XX2_UAJ:
> + function_id = TAC_FUNCTION_ID_UAJ;
> + pde_entity = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
> + TAC_SDCA_ENT_PDE47 : TAC_SDCA_ENT_PDE34;
> + break;
> + default:
> + function_id = TAC_FUNCTION_ID_SA;
> + pde_entity = TAC_SDCA_ENT_PDE23;
> + break;
> + }
> +
> + ret = regmap_write(tac_dev->regmap, SDW_SDCA_CTL(function_id, pde_entity,
> + TAC_SDCA_REQUESTED_PS, 0),
> + SDCA_PDE_PS3);
> + if (ret)
> + return ret;
> +
> + ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id,
> + pde_entity, SDCA_PDE_PS0, SDCA_PDE_PS3,
> + NULL, 0);
> +
> + return ret;
> +}
> +
> +static const struct snd_soc_dai_ops tac_dai_ops = {
> + .hw_params = tac_sdw_hw_params,
> + .hw_free = tac_sdw_pcm_hw_free,
> + .set_stream = tac_set_sdw_stream,
> + .shutdown = tac_sdw_shutdown,
so set_jack is now dynamically added...
> +};
> +
> +static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev)
> +{
> + int val, ret;
> +
> + if (!tac_has_uaj_support(tac_dev))
> + return 0;
... but here the test for uaj_support is still there? Is this needed?
> +
> + ret = regmap_read(tac_dev->regmap,
> + SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_GE35,
> + TAC_SDCA_CTL_DET_MODE, 0), &val);
> + if (ret) {
> + dev_err(tac_dev->dev, "Failed to read the detect mode");
> + return ret;
> + }
> +
> + switch (val) {
> + case 4:
> + tac_dev->jack_type = SND_JACK_MICROPHONE;
> + break;
> + case 5:
> + tac_dev->jack_type = SND_JACK_HEADPHONE;
> + break;
> + case 6:
> + tac_dev->jack_type = SND_JACK_HEADSET;
> + break;
> + case 0:
> + default:
> + tac_dev->jack_type = 0;
> + break;
> + }
> +
> + ret = regmap_write(tac_dev->regmap,
> + SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_GE35,
> + TAC_SDCA_CTL_SEL_MODE, 0), val);
> + if (ret)
> + dev_err(tac_dev->dev, "Failed to update the jack type to device");
> +
> + return 0;
> +}
> +
> +static int tac5xx2_set_jack(struct snd_soc_component *component,
> + struct snd_soc_jack *hs_jack, void *data)
> +{
> + struct tac5xx2_prv *tac_dev = snd_soc_component_get_drvdata(component);
> + int ret;
> +
> + guard(mutex)(&tac_dev->uaj_lock);
> + if (!hs_jack) {
> + if (tac_dev->hs_jack) {
> + tac_dev->hs_jack = NULL;
> + ret = 0;
> + goto disable_interrupts;
> + }
> + return 0;
> + }
> +
> + tac_dev->hs_jack = hs_jack;
> + if (!tac_dev->hw_init) {
> + dev_err(tac_dev->dev, "jack init failed, hw not initialized");
> + return 0;
> + }
should this test go up to the start of the function?
And isn't there a case where .set_jack can be invoked before the device is fully initialized?
I have a vague memory this was the case with other codecs, please double-check.
> +
> + ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2,
> + SDW_SCP_SDCA_INTMASK_SDCA_11);
> + if (ret) {
> + dev_warn(tac_dev->dev,
> + "Failed to register jack detection interrupt");
> + goto disable_interrupts;
> + }
> +
> + ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3,
> + SDW_SCP_SDCA_INTMASK_SDCA_16);
> + if (ret) {
> + dev_warn(tac_dev->dev,
> + "Failed to register for button detect interrupt");
> + goto disable_interrupts;
> + }
> +
> + return 0;
> +
> +disable_interrupts:
> + /* ignore errors while disabling interrupts */
> + regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2, 0);
> + regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3, 0);
> +
> + return ret;
> +}
> +
> +static int tac_interrupt_callback(struct sdw_slave *slave,
> + struct sdw_slave_intr_status *status)
> +{
> + struct tac5xx2_prv *tac_dev = dev_get_drvdata(&slave->dev);
> + struct device *dev = &slave->dev;
> + int ret = 0;
> + int btn_type = 0;
> + unsigned int sdca_int2, sdca_int3, jack_report_mask = 0;
> +
> + guard(mutex)(&tac_dev->uaj_lock);
again what does this uaj_lock protect?
> + if (status->control_port) {
> + if (status->control_port & SDW_SCP_INT1_PARITY)
> + dev_warn(dev, "SCP: Parity error interrupt");
> + if (status->control_port & SDW_SCP_INT1_BUS_CLASH)
> + dev_warn(dev, "SCP: Bus clash interrupt");
> + }
> +
> + ret = regmap_read(tac_dev->regmap, SDW_SCP_SDCA_INT2, &sdca_int2);
> + if (ret) {
> + dev_err(dev, "Failed to read UAJ Interrupt, reg:%#x err=%d\n",
> + SDW_SCP_SDCA_INT2, ret);
> + return ret;
> + }
> +
> + ret = regmap_read(tac_dev->regmap, SDW_SCP_SDCA_INT3, &sdca_int3);
> + if (ret) {
> + dev_err(dev, "Failed to read HID interrupt reg=%#x: err=%d",
> + SDW_SCP_SDCA_INT3, ret);
> + return ret;
> + }
> +
> + dev_dbg(dev, "SDCA_INT2: 0x%02x, SDCA_INT3: 0x%02x\n",
> + sdca_int2, sdca_int3);
> +
> + if (sdca_int2 & SDW_SCP_SDCA_INT_SDCA_11) {
> + ret = tac5xx2_sdca_headset_detect(tac_dev);
> + if (ret < 0)
> + goto clear;
> + jack_report_mask |= SND_JACK_HEADSET;
> + }
> +
> + if (sdca_int3 & SDW_SCP_SDCA_INT_SDCA_16) {
> + btn_type = tac5xx2_sdca_button_detect(tac_dev);
> + if (btn_type < 0)
> + btn_type = 0;
> + jack_report_mask |= SND_JACK_BTN_0 | SND_JACK_BTN_1 |
> + SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4;
> + }
> +
> + if (tac_dev->jack_type == 0)
> + btn_type = 0;
> +
> + dev_dbg(tac_dev->dev, "in %s, jack_type=%d\n", __func__, tac_dev->jack_type);
> + dev_dbg(tac_dev->dev, "in %s, btn_type=0x%x\n", __func__, btn_type);
> +
> + if (!tac_dev->hs_jack)
> + goto clear;
> +
> + snd_soc_jack_report(tac_dev->hs_jack, tac_dev->jack_type | btn_type,
> + jack_report_mask);
> +
> +clear:
> + if (sdca_int2) {
> + ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INT2, sdca_int2);
> + if (ret)
> + dev_dbg(tac_dev->dev, "Failed to clear jack interrupt\n");
> + }
> +
> + if (sdca_int3) {
> + ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INT3, sdca_int3);
> + if (ret)
> + dev_dbg(tac_dev->dev, "failed to clear hid interrupt\n");
> + }
> +
> + return 0;
> +}
> +static s32 tac_component_probe(struct snd_soc_component *component)
> +{
> + struct tac5xx2_prv *tac_dev =
> + snd_soc_component_get_drvdata(component);
> + struct device *dev = tac_dev->dev;
> + struct sdw_slave *slave = tac_dev->sdw_peripheral;
> + unsigned long time;
> + int ret;
> +
> + /* Wait for SoundWire hw initialization to complete */
> + time = wait_for_completion_timeout(&slave->initialization_complete,
> + msecs_to_jiffies(TAC5XX2_PROBE_TIMEOUT_MS));
> + if (!time) {
> + dev_warn(dev, "%s: hw initialization timeout\n", __func__);
> + return -ETIMEDOUT;
> + }
> +
> + if (!tac_has_uaj_support(tac_dev))
> + goto done_comp_probe;
> +
> + ret = snd_soc_dapm_new_controls(snd_soc_component_to_dapm(component),
> + tac_uaj_widgets,
> + ARRAY_SIZE(tac_uaj_widgets));
> + if (ret) {
> + dev_err(component->dev, "Failed to add UAJ widgets: %d\n", ret);
> + return ret;
> + }
> +
> + ret = snd_soc_dapm_add_routes(snd_soc_component_to_dapm(component),
> + tac_uaj_routes, ARRAY_SIZE(tac_uaj_routes));
> + if (ret) {
> + dev_err(component->dev, "Failed to add UAJ routes: %d\n", ret);
> + return ret;
> + }
> +
> + ret = snd_soc_add_component_controls(component, tac_uaj_controls,
> + ARRAY_SIZE(tac_uaj_controls));
> + if (ret) {
> + dev_err(dev, "Failed to add UAJ controls: %d\n", ret);
> + return ret;
> + }
> +
> +done_comp_probe:
> + tac_dev->component = component;
> + return 0;
> +}
> +
> +static void tac_component_remove(struct snd_soc_component *codec)
> +{
> + struct tac5xx2_prv *tac_dev = snd_soc_component_get_drvdata(codec);
> +
> + tac_dev->component = NULL;
> +}
> +
> +static const struct snd_soc_component_driver soc_codec_driver_tacdevice = {
> + .probe = tac_component_probe,
> + .remove = tac_component_remove,
> + .controls = tac5xx2_snd_controls,
> + .num_controls = ARRAY_SIZE(tac5xx2_snd_controls),
> + .dapm_widgets = tac5xx2_common_widgets,
> + .num_dapm_widgets = ARRAY_SIZE(tac5xx2_common_widgets),
> + .dapm_routes = tac5xx2_common_routes,
> + .num_dapm_routes = ARRAY_SIZE(tac5xx2_common_routes),
> + .idle_bias_on = 0,
> + .endianness = 1,
> +};
> +
> +static s32 tac_init(struct tac5xx2_prv *tac_dev)
> +{
> + s32 ret;
> + struct snd_soc_dai_driver *dai_drv;
> + struct snd_soc_component_driver *component_driver;
> + int num_dais;
> +
> + dev_set_drvdata(tac_dev->dev, tac_dev);
> +
> + switch (tac_dev->part_id) {
> + case 0x5572:
> + dai_drv = tac5572_dai_driver;
> + num_dais = ARRAY_SIZE(tac5572_dai_driver);
> + break;
> + case 0x5672:
> + dai_drv = tac5672_dai_driver;
> + num_dais = ARRAY_SIZE(tac5672_dai_driver);
> + break;
> + case 0x5682:
> + dai_drv = tac5682_dai_driver;
> + num_dais = ARRAY_SIZE(tac5682_dai_driver);
> + break;
> + case 0x2883:
> + dai_drv = tas2883_dai_driver;
> + num_dais = ARRAY_SIZE(tas2883_dai_driver);
> + break;
> + default:
> + dev_err(tac_dev->dev, "Unsupported device: 0x%x\n",
> + tac_dev->part_id);
> + return -EINVAL;
> + }
> +
> + component_driver = devm_kzalloc(tac_dev->dev, sizeof(*component_driver),
> + GFP_KERNEL);
> + if (!component_driver)
> + return -ENOMEM;
> +
> + memcpy(component_driver, &soc_codec_driver_tacdevice, sizeof(*component_driver));
> + if (tac_has_uaj_support(tac_dev))
> + component_driver->set_jack = tac5xx2_set_jack;
> +
> + ret = devm_snd_soc_register_component(tac_dev->dev, component_driver,
> + dai_drv, num_dais);
> + if (ret) {
> + dev_err(tac_dev->dev, "%s: codec register error:%d.\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static s32 tac5xx2_sdca_dev_suspend(struct device *dev)
> +{
> + struct tac5xx2_prv *tac_dev = dev_get_drvdata(dev);
> +
> + if (!tac_dev->hw_init)
> + return 0;
> +
> + regcache_cache_only(tac_dev->regmap, true);
> + return 0;
> +}
> +
> +static s32 tac5xx2_sdca_dev_system_suspend(struct device *dev)
> +{
> + return tac5xx2_sdca_dev_suspend(dev);
> +}
> +
> +static s32 tac5xx2_sdca_dev_resume(struct device *dev)
> +{
> + struct sdw_slave *slave = dev_to_sdw_dev(dev);
> + struct tac5xx2_prv *tac_dev = dev_get_drvdata(dev);
> + unsigned long t;
> + int ret;
> + bool had_unattached = false;
> +
> + if (!tac_dev->first_hw_init) {
> + dev_dbg(dev, "Device not initialized yet, skipping resume sync\n");
> + return 0;
> + }
> +
> + if (!slave->unattach_request)
> + goto regmap_sync;
> +
> + had_unattached = true;
> + t = wait_for_completion_timeout(&slave->initialization_complete,
> + msecs_to_jiffies(TAC5XX2_PROBE_TIMEOUT_MS));
> + if (!t) {
> + dev_err(&slave->dev, "resume: initialization timed out\n");
> + sdw_show_ping_status(slave->bus, true);
> + return -ETIMEDOUT;
> + }
> + slave->unattach_request = 0;
> + dev_dbg(tac_dev->dev, "%s wait for resume complete, starting writes\n", __func__);
> +
> + /* Detect and set jack type for UAJ path before playback.
> + * This is required as jack detection does not trigger interrupt
> + * when device is in runtime_pm suspend with bus in clock stop mode.
> + */
> + mutex_lock(&tac_dev->uaj_lock);
> + tac5xx2_sdca_headset_detect(tac_dev);
> + mutex_unlock(&tac_dev->uaj_lock);
again what does this mutex protect? there is no interrupt so there cannot be any conflict/race with the interrupt callback and even less with hw_params.
> +
> +regmap_sync:
> + regcache_cache_only(tac_dev->regmap, false);
> + if (!had_unattached) {
> + regcache_mark_dirty(tac_dev->regmap);
> + ret = regcache_sync(tac_dev->regmap);
> + if (ret < 0)
> + dev_warn(dev, "Failed to sync regcache: %d\n", ret);
> + }
> +
> + return 0;
> +}
> +static struct pci_dev *tac_get_pci_dev(struct sdw_slave *peripheral)
> +{
> + struct device *dev = &peripheral->dev;
> +
> + for (; dev; dev = dev->parent) {
> + if (dev_is_pci(dev))
> + return to_pci_dev(dev);
I find this surprising, what is the purpose of going up in the device hierarchy to find a PCI device?
Why would this driver care?
> + }
> +
> + return NULL;
> +}
> +static int tac_update_status(struct sdw_slave *slave,
> + enum sdw_slave_status status)
> +{
> + int ret;
> + bool first = false;
> + struct tac5xx2_prv *tac_dev = dev_get_drvdata(&slave->dev);
> + struct device *dev = &slave->dev;
> +
> + tac_dev->status = status;
> + if (status == SDW_SLAVE_UNATTACHED) {
> + tac_dev->hw_init = false;
> + tac_dev->fw_dl_success = false;
> + }
> +
> + if (tac_dev->hw_init || tac_dev->status != SDW_SLAVE_ATTACHED) {
> + dev_dbg(dev, "%s: early return, hw_init=%d, status=%d",
> + __func__, tac_dev->hw_init, tac_dev->status);
> + return 0;
> + }
> +
> + if (!tac_dev->first_hw_init) {
> + pm_runtime_set_autosuspend_delay(tac_dev->dev, 3000);
> + pm_runtime_use_autosuspend(tac_dev->dev);
> + pm_runtime_mark_last_busy(tac_dev->dev);
> + pm_runtime_set_active(tac_dev->dev);
> + pm_runtime_enable(tac_dev->dev);
I suggest you look at other codec drivers, all the pm_runtime configuration is done in the .probe callback and only set_active done upon a change in status.
> + tac_dev->first_hw_init = true;
> + first = true;
> + }
> +
> + pm_runtime_get_noresume(tac_dev->dev);
> +
> + regcache_mark_dirty(tac_dev->regmap);
> + regcache_cache_only(tac_dev->regmap, false);
> + ret = tac_io_init(&slave->dev, slave, first);
> + if (ret) {
> + dev_err(dev, "Device initialization failed: %d\n", ret);
> + goto err_out;
> + }
> +
> + ret = regcache_sync(tac_dev->regmap);
> + if (ret)
> + dev_warn(dev, "Failed to sync regcache after init: %d\n", ret);
> +
> +err_out:
> + pm_runtime_mark_last_busy(tac_dev->dev);
> + pm_runtime_put_autosuspend(tac_dev->dev);
> +
> + return ret;
> +}
> +
> +static int tac5xx2_sdw_clk_stop(struct sdw_slave *peripheral,
> + enum sdw_clk_stop_mode mode,
> + enum sdw_clk_stop_type type)
> +{
> + struct tac5xx2_prv *tac_dev = dev_get_drvdata(&peripheral->dev);
> +
> + dev_dbg(tac_dev->dev, "%s: mode:%d type:%d", __func__, mode, type);
> + return 0;
this looks rather useless, no?
> +}
> +
> +static int tac5xx2_sdw_read_prop(struct sdw_slave *peripheral)
> +{
> + struct device *dev = &peripheral->dev;
> + int ret;
> +
> + ret = sdw_slave_read_prop(peripheral);
> + if (ret) {
> + dev_err(dev, "sdw_slave_read_prop failed: %d", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +static s32 tac_sdw_probe(struct sdw_slave *peripheral,
> + const struct sdw_device_id *id)
> +{
> + struct regmap *regmap;
> + struct device *dev = &peripheral->dev;
> + struct tac5xx2_prv *tac_dev;
> + struct sdca_function_data *function_data = NULL;
> + int ret, i;
> +
> + tac_dev = devm_kzalloc(dev, sizeof(*tac_dev), GFP_KERNEL);
> + if (!tac_dev)
> + return dev_err_probe(dev, -ENOMEM,
> + "Failed devm_kzalloc");
> +
> + if (peripheral->sdca_data.num_functions > 0) {
> + dev_dbg(dev, "SDCA functions found: %d",
> + peripheral->sdca_data.num_functions);
> +
> + for (i = 0; i < peripheral->sdca_data.num_functions; i++) {
> + struct sdca_function_data **func_ptr;
> + const char *func_name;
> +
> + switch (peripheral->sdca_data.function[i].type) {
> + case SDCA_FUNCTION_TYPE_SMART_AMP:
> + func_ptr = &tac_dev->sa_func_data;
> + func_name = "smartamp";
> + break;
> + case SDCA_FUNCTION_TYPE_SMART_MIC:
> + func_ptr = &tac_dev->sm_func_data;
> + func_name = "smartmic";
> + break;
> + case SDCA_FUNCTION_TYPE_UAJ:
> + func_ptr = &tac_dev->uaj_func_data;
> + func_name = "uaj";
> + break;
> + case SDCA_FUNCTION_TYPE_HID:
> + func_ptr = &tac_dev->hid_func_data;
> + func_name = "hid";
> + break;
> + default:
> + continue;
> + }
> +
> + function_data = devm_kzalloc(dev, sizeof(*function_data),
> + GFP_KERNEL);
> + if (!function_data)
> + return dev_err_probe(dev, -ENOMEM,
> + "failed to allocate %s function data",
> + func_name);
> +
> + ret = sdca_parse_function(dev, peripheral,
> + &peripheral->sdca_data.function[i],
> + function_data);
> + if (!ret)
> + *func_ptr = function_data;
> + else
> + devm_kfree(dev, function_data);
> + }
> + }
> +
> + dev_dbg(dev, "SDCA functions enabled: SA=%s SM=%s UAJ=%s HID=%s",
> + tac_dev->sa_func_data ? "yes" : "no",
> + tac_dev->sm_func_data ? "yes" : "no",
> + tac_dev->uaj_func_data ? "yes" : "no",
> + tac_dev->hid_func_data ? "yes" : "no");
> +
> + tac_dev->dev = dev;
> + tac_dev->sdw_peripheral = peripheral;
> + tac_dev->hw_init = false;
> + tac_dev->first_hw_init = false;
> + mutex_init(&tac_dev->uaj_lock);
> + tac_dev->part_id = id->part_id;
> + dev_set_drvdata(dev, tac_dev);
> +
> + regmap = devm_regmap_init_sdw_mbq_cfg(&peripheral->dev, peripheral,
> + &tac_regmap, &tac_mbq_cfg);
> + if (IS_ERR(regmap))
> + return dev_err_probe(dev, PTR_ERR(regmap),
> + "Failed devm_regmap_init_sdw\n");
> +
> + regcache_cache_only(regmap, true);
> + tac_dev->regmap = regmap;
> + tac_dev->jack_type = 0;
> + init_completion(&tac_dev->fw_caching_complete);
> +
> + if (tac_has_dsp_algo(tac_dev)) {
> + tac_generate_fw_name(peripheral, tac_dev->fw_binaryname,
> + sizeof(tac_dev->fw_binaryname));
> +
> + ret = tac_load_and_cache_firmware_async(tac_dev);
> + if (ret) {
> + complete_all(&tac_dev->fw_caching_complete);
> + dev_dbg(dev, "failed to load fw: %d, use rom mode\n", ret);
> + }
> + } else {
> + complete_all(&tac_dev->fw_caching_complete);
> + }
> +
> + ret = tac_init(tac_dev);
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "failed to initialize tac device\n");
> +
> + return 0;
> +}
> +
> +static void tac_sdw_remove(struct sdw_slave *peripheral)
> +{
> + struct tac5xx2_prv *tac_dev = dev_get_drvdata(&peripheral->dev);
> +
> + mutex_destroy(&tac_dev->uaj_lock);
> + dev_set_drvdata(&peripheral->dev, NULL);
if you move all the runtime_pm stuff to the probe above, this remove() would also need to be modified to add a pm_runtime_disable()
> +}
next prev parent reply other threads:[~2026-04-28 20:02 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-27 8:23 [PATCH v11 1/4] ASoC: SDCA: Add PDE verification reusable helper Niranjan H Y
2026-04-27 8:23 ` [PATCH v11 2/4] ASoC: tac5xx2-sdw: add soundwire based codec driver Niranjan H Y
2026-04-28 0:52 ` Mark Brown
2026-04-28 19:57 ` Pierre-Louis Bossart [this message]
2026-04-29 4:06 ` Holalu Yogendra, Niranjan
2026-04-30 7:59 ` Pierre-Louis Bossart
2026-04-27 8:24 ` [PATCH v11 3/4] ASoC: sdw_utils: TI amp utility for tac5xx2 family Niranjan H Y
2026-04-27 8:24 ` [PATCH v11 4/4] ASoC: tac5xx2-sdw: ACPI match for intel mtl platform Niranjan H Y
2026-04-27 8:47 ` [PATCH v11 1/4] ASoC: SDCA: Add PDE verification reusable helper Charles Keepax
2026-04-28 0:45 ` Mark Brown
2026-04-28 12:05 ` Charles Keepax
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=7137df6a-5e49-4516-a356-3414f7d14c33@linux.dev \
--to=pierre-louis.bossart@linux.dev \
--cc=baojun.xu@ti.com \
--cc=broonie@kernel.org \
--cc=cezary.rojewski@intel.com \
--cc=ckeepax@opensource.cirrus.com \
--cc=kai.vehmanen@linux.intel.com \
--cc=lgirdwood@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sound@vger.kernel.org \
--cc=niranjan.hy@ti.com \
--cc=perex@perex.cz \
--cc=peter.ujfalusi@linux.intel.com \
--cc=ranjani.sridharan@linux.intel.com \
--cc=sandeepk@ti.com \
--cc=shenghao-ding@ti.com \
--cc=tiwai@suse.com \
--cc=v-hampiholi@ti.com \
--cc=yung-chuan.liao@linux.intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.