Skip to content

Commit

Permalink
ASoC: rt712-sdca: use generic SDCA interrupt handler
Browse files Browse the repository at this point in the history
Rather than open-code, use the generic SDCA interrupt handler after
registering the interrupt sources and callbacks.

FIXME: Need to figure out what actions need to be taken for INT0 and
INT8.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
  • Loading branch information
plbossart authored and bardliao committed Sep 20, 2024
1 parent 9a3fdbc commit 7a40aaf
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 141 deletions.
152 changes: 29 additions & 123 deletions sound/soc/codecs/rt712-sdca-sdw.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,21 +161,6 @@ static int rt712_sdca_update_status(struct sdw_slave *slave,
if (status == SDW_SLAVE_UNATTACHED)
rt712->hw_init = false;

if (status == SDW_SLAVE_ATTACHED) {
if (rt712->hs_jack) {
/*
* Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then
* if the device attached again, we will need to set the setting back.
* It could avoid losing the jack detection interrupt.
* This also could sync with the cache value as the rt712_sdca_jack_init set.
*/
sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK1,
SDW_SCP_SDCA_INTMASK_SDCA_0);
sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK2,
SDW_SCP_SDCA_INTMASK_SDCA_8);
}
}

/*
* Perform initialization only if slave status is present and
* hw_init flag is false
Expand Down Expand Up @@ -255,99 +240,30 @@ static int rt712_sdca_interrupt_callback(struct sdw_slave *slave,
struct sdw_slave_intr_status *status)
{
struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev);
int ret, stat;
int count = 0, retry = 3;
unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0;
int ret;

dev_dbg(&slave->dev,
"%s control_port_stat=%x, sdca_cascade=%x", __func__,
status->control_port, status->sdca_cascade);

if (cancel_delayed_work_sync(&rt712->jack_detect_work)) {
dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__);
/* avoid the HID owner doesn't change to device */
if (rt712->scp_sdca_stat2)
scp_sdca_stat2 = rt712->scp_sdca_stat2;
/*
* Preserve status for HID, to make sure the UMP interrupt is handled
* and the ownership can be changed in the workqueue back to DEVICE
*/
sdca_interrupt_clear_history(slave, BIT(8));
} else {
/* clear all history */
sdca_interrupt_clear_history(slave, 0);
}

/*
* The critical section below intentionally protects a rather large piece of code.
* We don't want to allow the system suspend to disable an interrupt while we are
* processing it, which could be problematic given the quirky SoundWire interrupt
* scheme. We do want however to prevent new workqueues from being scheduled if
* the disable_irq flag was set during system suspend.
*/
mutex_lock(&rt712->disable_irq_lock);

ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1);
if (ret < 0)
goto io_error;
rt712->scp_sdca_stat1 = ret;
ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2);
ret = sdca_interrupt_handler(slave);
if (ret < 0)
goto io_error;
rt712->scp_sdca_stat2 = ret;
if (scp_sdca_stat2)
rt712->scp_sdca_stat2 |= scp_sdca_stat2;

do {
/* clear flag */
ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1);
if (ret < 0)
goto io_error;
if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) {
ret = sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INT1,
SDW_SCP_SDCA_INTMASK_SDCA_0);
if (ret < 0)
goto io_error;
}
ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2);
if (ret < 0)
goto io_error;
if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) {
ret = sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INT2,
SDW_SCP_SDCA_INTMASK_SDCA_8);
if (ret < 0)
goto io_error;
}

/* check if flag clear or not */
ret = sdw_read_no_pm(rt712->slave, SDW_DP0_INT);
if (ret < 0)
goto io_error;
sdca_cascade = ret & SDW_DP0_SDCA_CASCADE;

ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1);
if (ret < 0)
goto io_error;
scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0;

ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2);
if (ret < 0)
goto io_error;
scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8;
dev_err(&slave->dev,
"%s: error %d in sdca_interrupt_handler\n",
__func__, ret);

stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade;

count++;
} while (stat != 0 && count < retry);

if (stat)
dev_warn(&slave->dev,
"%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
rt712->scp_sdca_stat1, rt712->scp_sdca_stat2);

if (status->sdca_cascade && !rt712->disable_irq)
mod_delayed_work(system_power_efficient_wq,
&rt712->jack_detect_work, msecs_to_jiffies(30));

mutex_unlock(&rt712->disable_irq_lock);

return 0;

io_error:
mutex_unlock(&rt712->disable_irq_lock);
pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
return ret;
}

Expand Down Expand Up @@ -386,7 +302,8 @@ static int rt712_sdca_sdw_remove(struct sdw_slave *slave)
pm_runtime_disable(&slave->dev);

mutex_destroy(&rt712->calibrate_mutex);
mutex_destroy(&rt712->disable_irq_lock);

sdca_interrupt_info_release(slave);

return 0;
}
Expand Down Expand Up @@ -420,28 +337,17 @@ static int __maybe_unused rt712_sdca_dev_system_suspend(struct device *dev)
{
struct rt712_sdca_priv *rt712_sdca = dev_get_drvdata(dev);
struct sdw_slave *slave = dev_to_sdw_dev(dev);
int ret1, ret2;
int ret;

if (!rt712_sdca->hw_init)
return 0;

/*
* prevent new interrupts from being handled after the
* deferred work completes and before the parent disables
* interrupts on the link
*/
mutex_lock(&rt712_sdca->disable_irq_lock);
rt712_sdca->disable_irq = true;
ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1,
SDW_SCP_SDCA_INTMASK_SDCA_0, 0);
ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2,
SDW_SCP_SDCA_INTMASK_SDCA_8, 0);
mutex_unlock(&rt712_sdca->disable_irq_lock);

if (ret1 < 0 || ret2 < 0) {
ret = sdca_interrupt_enable(rt712_sdca->slave,
BIT(8) | BIT(0),
false);
if (ret < 0)
/* log but don't prevent suspend from happening */
dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__);
}
dev_dbg(&slave->dev, "could not disable SDCA interrupts\n:");

return rt712_sdca_dev_suspend(dev);
}
Expand All @@ -453,19 +359,18 @@ static int __maybe_unused rt712_sdca_dev_resume(struct device *dev)
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);
unsigned long time;
int ret;

if (!rt712->first_hw_init)
return 0;

if (!slave->unattach_request) {
mutex_lock(&rt712->disable_irq_lock);
if (rt712->disable_irq == true) {

sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0);
sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
rt712->disable_irq = false;
}
mutex_unlock(&rt712->disable_irq_lock);
ret = sdca_interrupt_enable(rt712->slave,
BIT(8) | BIT(0),
true);
if (ret < 0)
/* log but don't prevent resume from happening */
dev_dbg(&slave->dev, "could not enable SDCA interrupts\n:");
goto regmap_sync;
}

Expand Down Expand Up @@ -508,3 +413,4 @@ MODULE_DESCRIPTION("ASoC RT712 SDCA SDW driver");
MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(SND_SOC_SDCA);
MODULE_IMPORT_NS(SND_SOC_SDCA_IRQ_HANDLER);
100 changes: 86 additions & 14 deletions sound/soc/codecs/rt712-sdca.c
Original file line number Diff line number Diff line change
Expand Up @@ -262,23 +262,28 @@ static void rt712_sdca_jack_detect_handler(struct work_struct *work)
{
struct rt712_sdca_priv *rt712 =
container_of(work, struct rt712_sdca_priv, jack_detect_work.work);
struct sdca_interrupt_info *interrupt_info;
int btn_type = 0, ret;

interrupt_info = rt712->slave->sdca_data.interrupt_info;
if (!interrupt_info)
return;

if (!rt712->hs_jack)
return;

if (!rt712->component->card || !rt712->component->card->instantiated)
return;

/* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */
if (rt712->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) {
if (interrupt_info->detected_interrupt_mask & BIT(0)) {
ret = rt712_sdca_headset_detect(rt712);
if (ret < 0)
return;
}

/* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */
if (rt712->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8)
if (interrupt_info->detected_interrupt_mask & BIT(8))
btn_type = rt712_sdca_button_detect(rt712);

if (rt712->jack_type == 0)
Expand All @@ -289,8 +294,7 @@ static void rt712_sdca_jack_detect_handler(struct work_struct *work)
dev_dbg(&rt712->slave->dev,
"in %s, btn_type=0x%x\n", __func__, btn_type);
dev_dbg(&rt712->slave->dev,
"in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
rt712->scp_sdca_stat1, rt712->scp_sdca_stat2);
"in %s, detected_interrupt_mask=0x%x\n", __func__, interrupt_info->detected_interrupt_mask);

snd_soc_jack_report(rt712->hs_jack, rt712->jack_type | btn_type,
SND_JACK_HEADSET |
Expand Down Expand Up @@ -403,6 +407,8 @@ static void rt712_sdca_btn_check_handler(struct work_struct *work)

static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712)
{
int ret;

mutex_lock(&rt712->calibrate_mutex);

if (rt712->hs_jack) {
Expand Down Expand Up @@ -432,11 +438,15 @@ static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712)
break;
}

/* set SCP_SDCA_IntMask1[0]=1 */
sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0);
/* set SCP_SDCA_IntMask2[0]=1 */
sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
dev_dbg(&rt712->slave->dev, "in %s enable\n", __func__);
ret = sdca_interrupt_enable(rt712->slave,
BIT(0) | BIT(8),
true);
if (ret < 0)
dev_err(&rt712->slave->dev,
"%s: sdca_interrupt_enable failed: %d\n",
__func__, ret);
else
dev_dbg(&rt712->slave->dev, "in %s enable\n", __func__);

/* trigger GE interrupt */
rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
Expand Down Expand Up @@ -1590,6 +1600,32 @@ static struct snd_soc_dai_driver rt712_sdca_dai[] = {
}
};

/* this is called with the irqs_lock mutex held */
static void jack_detection_callback(void *context)
{
struct rt712_sdca_priv *rt712 = context;

/*
* Invoke the delayed work unconditionally, the test on
* the SDCA_0 being enabled was done in the common handler
*/
mod_delayed_work(system_power_efficient_wq,
&rt712->jack_detect_work, msecs_to_jiffies(30));
}

/* this is called with the irqs_lock mutex held */
static void button_detection_callback(void *context)
{
struct rt712_sdca_priv *rt712 = context;

/*
* Invoke the delayed work unconditionally, the test on
* the SDCA_8 being enabled was done in the common handler
*/
mod_delayed_work(system_power_efficient_wq,
&rt712->jack_detect_work, msecs_to_jiffies(30));
}

static struct snd_soc_dai_driver rt712_sdca_dmic_dai[] = {
{
.name = "rt712-sdca-aif3",
Expand All @@ -1608,13 +1644,50 @@ static struct snd_soc_dai_driver rt712_sdca_dmic_dai[] = {
int rt712_sdca_init(struct device *dev, struct regmap *regmap,
struct regmap *mbq_regmap, struct sdw_slave *slave)
{
struct sdca_interrupt_source *sdca_int_0;
struct sdca_interrupt_source *sdca_int_8;
struct rt712_sdca_priv *rt712;
int ret;

rt712 = devm_kzalloc(dev, sizeof(*rt712), GFP_KERNEL);
if (!rt712)
return -ENOMEM;

ret = sdca_interrupt_info_alloc(slave);
if (ret < 0)
return ret;

/* SDCA_INT1 and SDCA_INT2 are supported */
ret = sdca_interrupt_initialize(slave, GENMASK(1, 0));
if (ret < 0)
return ret;

/* alloc and configure SDCA interrupt sources */
sdca_int_0 = devm_kzalloc(dev, sizeof(*sdca_int_0), GFP_KERNEL);
if (!sdca_int_0)
return -ENOMEM;

sdca_int_8 = devm_kzalloc(dev, sizeof(*sdca_int_8), GFP_KERNEL);
if (!sdca_int_8)
return -ENOMEM;

sdca_int_0->index = 0;
sdca_int_0->context = rt712;
sdca_int_0->callback = jack_detection_callback;

sdca_int_8->index = 8;
sdca_int_8->context = rt712;
sdca_int_8->callback = button_detection_callback;

/* now register sources */
ret = sdca_interrupt_register_source(slave, sdca_int_0);
if (ret < 0)
return ret;

ret = sdca_interrupt_register_source(slave, sdca_int_8);
if (ret < 0)
return ret;

dev_set_drvdata(dev, rt712);
rt712->slave = slave;
rt712->regmap = regmap;
Expand All @@ -1624,7 +1697,6 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap,
regcache_cache_only(rt712->mbq_regmap, true);

mutex_init(&rt712->calibrate_mutex);
mutex_init(&rt712->disable_irq_lock);

INIT_DELAYED_WORK(&rt712->jack_detect_work, rt712_sdca_jack_detect_handler);
INIT_DELAYED_WORK(&rt712->jack_btn_check_work, rt712_sdca_btn_check_handler);
Expand Down Expand Up @@ -1813,11 +1885,11 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave)
struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);
unsigned int val;
struct sdw_slave_prop *prop = &slave->prop;
int ret;

rt712->disable_irq = false;

if (rt712->hw_init)
return 0;
ret = sdca_interrupt_info_reset(slave);
if (ret < 0)
return ret;

regcache_cache_only(rt712->regmap, false);
regcache_cache_only(rt712->mbq_regmap, false);
Expand Down
Loading

0 comments on commit 7a40aaf

Please sign in to comment.