fsa4480: Avoid updating settings if unchanged

During audio subsystem restart, the audio driver will unregister
its notifier on shutdown and re-register when subsystem is up again.
If a regular USB device, and not a Type-C analog audio device, is
currently connected, we should not re-write the register settings
over I2C to the FSA4480 IC as that will make the D+/D- pins to
be momentarily switched off an on, which causes the USB controller
to detect a momentary disconnect and re-connect of the attached
peripheral.

Fix this by first reading the switch control and enable registers,
and if being updated with the same values, don't re-write them to
avoid unnecessarily resetting D+/D- or SBU1/2.

Change-Id: I9d4e092e9782ca64caf0982958b3e5d05744aa92
Signed-off-by: Jack Pham <jackp@codeaurora.org>
This commit is contained in:
Jack Pham 2020-10-09 00:38:28 -07:00
commit 559d487878

View file

@ -69,11 +69,21 @@ static const struct fsa4480_reg_val fsa_reg_i2c_defaults[] = {
static void fsa4480_usbc_update_settings(struct fsa4480_priv *fsa_priv,
u32 switch_control, u32 switch_enable)
{
u32 prev_control, prev_enable;
if (!fsa_priv->regmap) {
dev_err(fsa_priv->dev, "%s: regmap invalid\n", __func__);
return;
}
regmap_read(fsa_priv->regmap, FSA4480_SWITCH_CONTROL, &prev_control);
regmap_read(fsa_priv->regmap, FSA4480_SWITCH_SETTINGS, &prev_enable);
if (prev_control == switch_control && prev_enable == switch_enable) {
dev_dbg(fsa_priv->dev, "%s: settings unchanged\n", __func__);
return;
}
regmap_write(fsa_priv->regmap, FSA4480_SWITCH_SETTINGS, 0x80);
regmap_write(fsa_priv->regmap, FSA4480_SWITCH_CONTROL, switch_control);
/* FSA4480 chip hardware requirement */
@ -181,8 +191,6 @@ static int fsa4480_usbc_analog_setup_switches_psupply(
dev_dbg(dev, "%s: setting GPIOs active = %d rcvd intval 0x%X\n",
__func__, mode.intval != TYPEC_ACCESSORY_NONE, mode.intval);
if (atomic_read(&(fsa_priv->usbc_mode)) == mode.intval)
goto done; /* filter notifications received before */
atomic_set(&(fsa_priv->usbc_mode), mode.intval);
switch (mode.intval) {
@ -321,8 +329,6 @@ int fsa4480_unreg_notifier(struct notifier_block *nb,
int rc = 0;
struct i2c_client *client = of_find_i2c_device_by_node(node);
struct fsa4480_priv *fsa_priv;
struct device *dev;
union power_supply_propval mode;
if (!client)
return -EINVAL;
@ -330,33 +336,14 @@ int fsa4480_unreg_notifier(struct notifier_block *nb,
fsa_priv = (struct fsa4480_priv *)i2c_get_clientdata(client);
if (!fsa_priv)
return -EINVAL;
if (fsa_priv->use_powersupply) {
dev = fsa_priv->dev;
if (!dev)
return -EINVAL;
mutex_lock(&fsa_priv->notification_lock);
/* get latest mode within locked context */
mutex_lock(&fsa_priv->notification_lock);
rc = iio_read_channel_processed(fsa_priv->iio_ch, &mode.intval);
if (rc) {
dev_dbg(dev, "%s: Unable to read USB TYPEC_MODE: %d\n",
__func__, rc);
mutex_unlock(&fsa_priv->notification_lock);
return rc;
}
/* Do not reset switch settings for usb digital hs */
if (mode.intval == TYPEC_ACCESSORY_AUDIO)
fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98);
rc = blocking_notifier_chain_unregister
(&fsa_priv->fsa4480_notifier, nb);
mutex_unlock(&fsa_priv->notification_lock);
} else {
fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98);
rc = blocking_notifier_chain_unregister
fsa4480_usbc_update_settings(fsa_priv, 0x18, 0x98);
rc = blocking_notifier_chain_unregister
(&fsa_priv->fsa4480_notifier, nb);
}
mutex_unlock(&fsa_priv->notification_lock);
return rc;
}
EXPORT_SYMBOL(fsa4480_unreg_notifier);