usb: typec: fsa4480: rework mux & switch setup to handle more states
authorNeil Armstrong <neil.armstrong@linaro.org>
Wed, 14 Jun 2023 13:10:40 +0000 (15:10 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 21 Jun 2023 16:07:55 +0000 (18:07 +0200)
In order to handle the Audio Accessory mode, refactor the mux
and switch setup in a single function.

The refactor will help add new states and make the process
simpler to understand.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Tested-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Link: https://lore.kernel.org/r/20230614-topic-sm8550-upstream-type-c-audio-v1-2-15a92565146b@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/typec/mux/fsa4480.c

index b201dda..ab3ff71 100644 (file)
@@ -46,8 +46,11 @@ struct fsa4480 {
 
        struct regmap *regmap;
 
+       enum typec_orientation orientation;
+       unsigned long mode;
+       unsigned int svid;
+
        u8 cur_enable;
-       u8 cur_select;
 };
 
 static const struct regmap_config fsa4480_regmap_config = {
@@ -58,19 +61,42 @@ static const struct regmap_config fsa4480_regmap_config = {
        .disable_locking = true,
 };
 
-static int fsa4480_switch_set(struct typec_switch_dev *sw,
-                             enum typec_orientation orientation)
+static int fsa4480_set(struct fsa4480 *fsa)
 {
-       struct fsa4480 *fsa = typec_switch_get_drvdata(sw);
-       u8 new_sel;
-
-       mutex_lock(&fsa->lock);
-       new_sel = FSA4480_SEL_USB;
-       if (orientation == TYPEC_ORIENTATION_REVERSE)
-               new_sel |= FSA4480_SEL_SBU_REVERSE;
-
-       if (new_sel == fsa->cur_select)
-               goto out_unlock;
+       bool reverse = (fsa->orientation == TYPEC_ORIENTATION_REVERSE);
+       u8 enable = FSA4480_ENABLE_DEVICE;
+       u8 sel = 0;
+
+       /* USB Mode */
+       if (fsa->mode < TYPEC_STATE_MODAL ||
+           (!fsa->svid && (fsa->mode == TYPEC_MODE_USB2 ||
+                           fsa->mode == TYPEC_MODE_USB3))) {
+               enable |= FSA4480_ENABLE_USB;
+               sel = FSA4480_SEL_USB;
+       } else if (fsa->svid) {
+               switch (fsa->mode) {
+               /* DP Only */
+               case TYPEC_DP_STATE_C:
+               case TYPEC_DP_STATE_E:
+                       enable |= FSA4480_ENABLE_SBU;
+                       if (reverse)
+                               sel = FSA4480_SEL_SBU_REVERSE;
+                       break;
+
+               /* DP + USB */
+               case TYPEC_DP_STATE_D:
+               case TYPEC_DP_STATE_F:
+                       enable |= FSA4480_ENABLE_USB | FSA4480_ENABLE_SBU;
+                       sel = FSA4480_SEL_USB;
+                       if (reverse)
+                               sel |= FSA4480_SEL_SBU_REVERSE;
+                       break;
+
+               default:
+                       return -EOPNOTSUPP;
+               }
+       } else
+               return -EOPNOTSUPP;
 
        if (fsa->cur_enable & FSA4480_ENABLE_SBU) {
                /* Disable SBU output while re-configuring the switch */
@@ -81,48 +107,59 @@ static int fsa4480_switch_set(struct typec_switch_dev *sw,
                usleep_range(35, 1000);
        }
 
-       regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, new_sel);
-       fsa->cur_select = new_sel;
-
-       if (fsa->cur_enable & FSA4480_ENABLE_SBU) {
-               regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, fsa->cur_enable);
+       regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, sel);
+       regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, enable);
 
+       if (enable & FSA4480_ENABLE_SBU) {
                /* 15us to allow the SBU switch to turn on again */
                usleep_range(15, 1000);
        }
 
-out_unlock:
-       mutex_unlock(&fsa->lock);
+       fsa->cur_enable = enable;
 
        return 0;
 }
 
+static int fsa4480_switch_set(struct typec_switch_dev *sw,
+                             enum typec_orientation orientation)
+{
+       struct fsa4480 *fsa = typec_switch_get_drvdata(sw);
+       int ret = 0;
+
+       mutex_lock(&fsa->lock);
+
+       if (fsa->orientation != orientation) {
+               fsa->orientation = orientation;
+
+               ret = fsa4480_set(fsa);
+       }
+
+       mutex_unlock(&fsa->lock);
+
+       return ret;
+}
+
 static int fsa4480_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
 {
        struct fsa4480 *fsa = typec_mux_get_drvdata(mux);
-       u8 new_enable;
+       int ret = 0;
 
        mutex_lock(&fsa->lock);
 
-       new_enable = FSA4480_ENABLE_DEVICE | FSA4480_ENABLE_USB;
-       if (state->mode >= TYPEC_DP_STATE_A)
-               new_enable |= FSA4480_ENABLE_SBU;
+       if (fsa->mode != state->mode) {
+               fsa->mode = state->mode;
 
-       if (new_enable == fsa->cur_enable)
-               goto out_unlock;
+               if (state->alt)
+                       fsa->svid = state->alt->svid;
+               else
+                       fsa->svid = 0; // No SVID
 
-       regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, new_enable);
-       fsa->cur_enable = new_enable;
-
-       if (new_enable & FSA4480_ENABLE_SBU) {
-               /* 15us to allow the SBU switch to turn off */
-               usleep_range(15, 1000);
+               ret = fsa4480_set(fsa);
        }
 
-out_unlock:
        mutex_unlock(&fsa->lock);
 
-       return 0;
+       return ret;
 }
 
 static int fsa4480_probe(struct i2c_client *client)
@@ -143,8 +180,10 @@ static int fsa4480_probe(struct i2c_client *client)
        if (IS_ERR(fsa->regmap))
                return dev_err_probe(dev, PTR_ERR(fsa->regmap), "failed to initialize regmap\n");
 
+       /* Safe mode */
        fsa->cur_enable = FSA4480_ENABLE_DEVICE | FSA4480_ENABLE_USB;
-       fsa->cur_select = FSA4480_SEL_USB;
+       fsa->mode = TYPEC_STATE_SAFE;
+       fsa->orientation = TYPEC_ORIENTATION_NONE;
 
        /* set default settings */
        regmap_write(fsa->regmap, FSA4480_SLOW_L, 0x00);
@@ -156,7 +195,7 @@ static int fsa4480_probe(struct i2c_client *client)
        regmap_write(fsa->regmap, FSA4480_DELAY_L_MIC, 0x00);
        regmap_write(fsa->regmap, FSA4480_DELAY_L_SENSE, 0x00);
        regmap_write(fsa->regmap, FSA4480_DELAY_L_AGND, 0x09);
-       regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, fsa->cur_select);
+       regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, FSA4480_SEL_USB);
        regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, fsa->cur_enable);
 
        sw_desc.drvdata = fsa;