]> Pileus Git - ~andy/linux/blobdiff - drivers/extcon/extcon-max77693.c
Merge tag 'virtio-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[~andy/linux] / drivers / extcon / extcon-max77693.c
index abab068adc358eb820c357f632be4dec96895954..b70e3815c45932a3b76f7f30fff98d69907a8483 100644 (file)
@@ -185,7 +185,7 @@ enum {
        _EXTCON_CABLE_NUM,
 };
 
-const char *max77693_extcon_cable[] = {
+static const char *max77693_extcon_cable[] = {
        [EXTCON_CABLE_USB]                      = "USB",
        [EXTCON_CABLE_USB_HOST]                 = "USB-Host",
        [EXTCON_CABLE_TA]                       = "TA",
@@ -224,16 +224,17 @@ static int max77693_muic_set_debounce_time(struct max77693_muic_info *info,
                                          MAX77693_MUIC_REG_CTRL3,
                                          time << CONTROL3_ADCDBSET_SHIFT,
                                          CONTROL3_ADCDBSET_MASK);
-               if (ret)
+               if (ret) {
                        dev_err(info->dev, "failed to set ADC debounce time\n");
+                       return -EAGAIN;
+               }
                break;
        default:
                dev_err(info->dev, "invalid ADC debounce time\n");
-               ret = -EINVAL;
-               break;
+               return -EINVAL;
        }
 
-       return ret;
+       return 0;
 };
 
 /*
@@ -261,7 +262,7 @@ static int max77693_muic_set_path(struct max77693_muic_info *info,
                        MAX77693_MUIC_REG_CTRL1, ctrl1, COMP_SW_MASK);
        if (ret < 0) {
                dev_err(info->dev, "failed to update MUIC register\n");
-               goto out;
+               return -EAGAIN;
        }
 
        if (attached)
@@ -274,14 +275,14 @@ static int max77693_muic_set_path(struct max77693_muic_info *info,
                        CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK);
        if (ret < 0) {
                dev_err(info->dev, "failed to update MUIC register\n");
-               goto out;
+               return -EAGAIN;
        }
 
        dev_info(info->dev,
                "CONTROL1 : 0x%02x, CONTROL2 : 0x%02x, state : %s\n",
                ctrl1, ctrl2, attached ? "attached" : "detached");
-out:
-       return ret;
+
+       return 0;
 }
 
 /*
@@ -442,6 +443,8 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info,
                int cable_type, bool attached)
 {
        int ret = 0;
+       int vbvolt;
+       bool cable_attached;
        char dock_name[CABLE_NAME_MAX];
 
        dev_info(info->dev,
@@ -450,14 +453,45 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info,
 
        switch (cable_type) {
        case MAX77693_MUIC_ADC_RESERVED_ACC_3:          /* Dock-Smart */
-               /* PATH:AP_USB */
-               ret = max77693_muic_set_path(info,
-                               CONTROL1_SW_USB, attached);
+               /*
+                * Check power cable whether attached or detached state.
+                * The Dock-Smart device need surely external power supply.
+                * If power cable(USB/TA) isn't connected to Dock device,
+                * user can't use Dock-Smart for desktop mode.
+                */
+               vbvolt = max77693_muic_get_cable_type(info,
+                               MAX77693_CABLE_GROUP_VBVOLT, &cable_attached);
+               if (attached && !vbvolt) {
+                       dev_warn(info->dev,
+                               "Cannot detect external power supply\n");
+                       return 0;
+               }
+
+               /*
+                * Notify Dock-Smart/MHL state.
+                * - Dock-Smart device include three type of cable which
+                * are HDMI, USB for mouse/keyboard and micro-usb port
+                * for USB/TA cable. Dock-Smart device need always exteranl
+                * power supply(USB/TA cable through micro-usb cable). Dock-
+                * Smart device support screen output of target to separate
+                * monitor and mouse/keyboard for desktop mode.
+                *
+                * Features of 'USB/TA cable with Dock-Smart device'
+                * - Support MHL
+                * - Support external output feature of audio
+                * - Support charging through micro-usb port without data
+                *           connection if TA cable is connected to target.
+                * - Support charging and data connection through micro-usb port
+                *           if USB cable is connected between target and host
+                *           device.
+                * - Support OTG device (Mouse/Keyboard)
+                */
+               ret = max77693_muic_set_path(info, info->path_usb, attached);
                if (ret < 0)
-                       goto out;
+                       return ret;
 
-               /* Dock-Smart */
                extcon_set_cable_state(info->edev, "Dock-Smart", attached);
+               extcon_set_cable_state(info->edev, "MHL", attached);
                goto out;
        case MAX77693_MUIC_ADC_FACTORY_MODE_UART_ON:    /* Dock-Car */
                strcpy(dock_name, "Dock-Car");
@@ -470,16 +504,20 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info,
                if (!attached)
                        extcon_set_cable_state(info->edev, "USB", false);
                break;
+       default:
+               dev_err(info->dev, "failed to detect %s dock device\n",
+                       attached ? "attached" : "detached");
+               return -EINVAL;
        }
 
        /* Dock-Car/Desk/Audio, PATH:AUDIO */
        ret = max77693_muic_set_path(info, CONTROL1_SW_AUDIO, attached);
        if (ret < 0)
-               goto out;
+               return ret;
        extcon_set_cable_state(info->edev, dock_name, attached);
 
 out:
-       return ret;
+       return 0;
 }
 
 static int max77693_muic_dock_button_handler(struct max77693_muic_info *info,
@@ -487,7 +525,6 @@ static int max77693_muic_dock_button_handler(struct max77693_muic_info *info,
 {
        struct input_dev *dock = info->dock;
        unsigned int code;
-       int ret = 0;
 
        switch (button_type) {
        case MAX77693_MUIC_ADC_REMOTE_S3_BUTTON-1
@@ -517,14 +554,12 @@ static int max77693_muic_dock_button_handler(struct max77693_muic_info *info,
                dev_err(info->dev,
                        "failed to detect %s key (adc:0x%x)\n",
                        attached ? "pressed" : "released", button_type);
-               ret = -EINVAL;
-               goto out;
+               return -EINVAL;
        }
 
        input_event(dock, EV_KEY, code, attached);
        input_sync(dock);
 
-out:
        return 0;
 }
 
@@ -543,14 +578,14 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info)
                /* USB_OTG, PATH: AP_USB */
                ret = max77693_muic_set_path(info, CONTROL1_SW_USB, attached);
                if (ret < 0)
-                       goto out;
+                       return ret;
                extcon_set_cable_state(info->edev, "USB-Host", attached);
                break;
        case MAX77693_MUIC_GND_AV_CABLE_LOAD:
                /* Audio Video Cable with load, PATH:AUDIO */
                ret = max77693_muic_set_path(info, CONTROL1_SW_AUDIO, attached);
                if (ret < 0)
-                       goto out;
+                       return ret;
                extcon_set_cable_state(info->edev,
                                "Audio-video-load", attached);
                break;
@@ -560,14 +595,12 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info)
                extcon_set_cable_state(info->edev, "MHL", attached);
                break;
        default:
-               dev_err(info->dev, "failed to detect %s accessory\n",
+               dev_err(info->dev, "failed to detect %s cable of gnd type\n",
                        attached ? "attached" : "detached");
-               ret = -EINVAL;
-               break;
+               return -EINVAL;
        }
 
-out:
-       return ret;
+       return 0;
 }
 
 static int max77693_muic_jig_handler(struct max77693_muic_info *info,
@@ -597,15 +630,19 @@ static int max77693_muic_jig_handler(struct max77693_muic_info *info,
                strcpy(cable_name, "JIG-UART-OFF");
                path = CONTROL1_SW_UART;
                break;
+       default:
+               dev_err(info->dev, "failed to detect %s jig cable\n",
+                       attached ? "attached" : "detached");
+               return -EINVAL;
        }
 
        ret = max77693_muic_set_path(info, path, attached);
        if (ret < 0)
-               goto out;
+               return ret;
 
        extcon_set_cable_state(info->edev, cable_name, attached);
-out:
-       return ret;
+
+       return 0;
 }
 
 static int max77693_muic_adc_handler(struct max77693_muic_info *info)
@@ -635,7 +672,7 @@ static int max77693_muic_adc_handler(struct max77693_muic_info *info)
                /* JIG */
                ret = max77693_muic_jig_handler(info, cable_type, attached);
                if (ret < 0)
-                       goto out;
+                       return ret;
                break;
        case MAX77693_MUIC_ADC_RESERVED_ACC_3:          /* Dock-Smart */
        case MAX77693_MUIC_ADC_FACTORY_MODE_UART_ON:    /* Dock-Car */
@@ -652,7 +689,7 @@ static int max77693_muic_adc_handler(struct max77693_muic_info *info)
                 */
                ret = max77693_muic_dock_handler(info, cable_type, attached);
                if (ret < 0)
-                       goto out;
+                       return ret;
                break;
        case MAX77693_MUIC_ADC_REMOTE_S3_BUTTON:        /* DOCK_KEY_PREV */
        case MAX77693_MUIC_ADC_REMOTE_S7_BUTTON:        /* DOCK_KEY_NEXT */
@@ -677,7 +714,7 @@ static int max77693_muic_adc_handler(struct max77693_muic_info *info)
                ret = max77693_muic_dock_button_handler(info, button_type,
                                                        attached);
                if (ret < 0)
-                       goto out;
+                       return ret;
                break;
        case MAX77693_MUIC_ADC_SEND_END_BUTTON:
        case MAX77693_MUIC_ADC_REMOTE_S1_BUTTON:
@@ -705,17 +742,15 @@ static int max77693_muic_adc_handler(struct max77693_muic_info *info)
                dev_info(info->dev,
                        "accessory is %s but it isn't used (adc:0x%x)\n",
                        attached ? "attached" : "detached", cable_type);
-               goto out;
+               return -EAGAIN;
        default:
                dev_err(info->dev,
                        "failed to detect %s accessory (adc:0x%x)\n",
                        attached ? "attached" : "detached", cable_type);
-               ret = -EINVAL;
-               goto out;
+               return -EINVAL;
        }
 
-out:
-       return ret;
+       return 0;
 }
 
 static int max77693_muic_chg_handler(struct max77693_muic_info *info)
@@ -737,60 +772,125 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
 
        switch (chg_type) {
        case MAX77693_CHARGER_TYPE_USB:
-               /*
-                * MHL_TA(USB/TA) with MHL cable
-                * - MHL cable include two port(HDMI line and separate micro
-                * -usb port. When the target connect MHL cable, extcon driver
-                * check whether MHL_TA(USB/TA) cable is connected. If MHL_TA
-                * cable is connected, extcon driver notify state to notifiee
-                * for charging battery.
-                */
+       case MAX77693_CHARGER_TYPE_DEDICATED_CHG:
+       case MAX77693_CHARGER_TYPE_NONE:
+               /* Check MAX77693_CABLE_GROUP_ADC_GND type */
                cable_type_gnd = max77693_muic_get_cable_type(info,
                                        MAX77693_CABLE_GROUP_ADC_GND,
                                        &cable_attached);
-               if (cable_type_gnd == MAX77693_MUIC_GND_MHL
-                       || cable_type_gnd == MAX77693_MUIC_GND_MHL_VB) {
+               switch (cable_type_gnd) {
+               case MAX77693_MUIC_GND_MHL:
+               case MAX77693_MUIC_GND_MHL_VB:
+                       /*
+                        * MHL cable with MHL_TA(USB/TA) cable
+                        * - MHL cable include two port(HDMI line and separate micro-
+                        * usb port. When the target connect MHL cable, extcon driver
+                        * check whether MHL_TA(USB/TA) cable is connected. If MHL_TA
+                        * cable is connected, extcon driver notify state to notifiee
+                        * for charging battery.
+                        *
+                        * Features of 'MHL_TA(USB/TA) with MHL cable'
+                        * - Support MHL
+                        * - Support charging through micro-usb port without data connection
+                        */
                        extcon_set_cable_state(info->edev, "MHL_TA", attached);
-
                        if (!cable_attached)
-                               extcon_set_cable_state(info->edev,
-                                       "MHL", false);
-                       goto out;
+                               extcon_set_cable_state(info->edev, "MHL", cable_attached);
+                       break;
                }
 
-               /*
-                * USB/TA cable with Dock-Audio device
-                * - Dock device include two port(Dock-Audio and micro-usb
-                * port). When the target connect Dock-Audio device, extcon
-                * driver check whether USB/TA cable is connected.
-                * If USB/TA cable is connected, extcon driver notify state
-                * to notifiee for charging battery.
-                */
+               /* Check MAX77693_CABLE_GROUP_ADC type */
                cable_type = max77693_muic_get_cable_type(info,
                                        MAX77693_CABLE_GROUP_ADC,
                                        &cable_attached);
-               if (cable_type == MAX77693_MUIC_ADC_AV_CABLE_NOLOAD) {
+               switch (cable_type) {
+               case MAX77693_MUIC_ADC_AV_CABLE_NOLOAD:         /* Dock-Audio */
+                       /*
+                        * Dock-Audio device with USB/TA cable
+                        * - Dock device include two port(Dock-Audio and micro-usb
+                        * port). When the target connect Dock-Audio device, extcon
+                        * driver check whether USB/TA cable is connected. If USB/TA
+                        * cable is connected, extcon driver notify state to notifiee
+                        * for charging battery.
+                        *
+                        * Features of 'USB/TA cable with Dock-Audio device'
+                        * - Support external output feature of audio.
+                        * - Support charging through micro-usb port without data
+                        *           connection.
+                        */
                        extcon_set_cable_state(info->edev, "USB", attached);
 
                        if (!cable_attached)
-                               extcon_set_cable_state(info->edev,
-                                               "Dock-Audio", false);
-                       goto out;
+                               extcon_set_cable_state(info->edev, "Dock-Audio", cable_attached);
+                       break;
+               case MAX77693_MUIC_ADC_RESERVED_ACC_3:          /* Dock-Smart */
+                       /*
+                        * Dock-Smart device with USB/TA cable
+                        * - Dock-Desk device include three type of cable which
+                        * are HDMI, USB for mouse/keyboard and micro-usb port
+                        * for USB/TA cable. Dock-Smart device need always exteranl
+                        * power supply(USB/TA cable through micro-usb cable). Dock-
+                        * Smart device support screen output of target to separate
+                        * monitor and mouse/keyboard for desktop mode.
+                        *
+                        * Features of 'USB/TA cable with Dock-Smart device'
+                        * - Support MHL
+                        * - Support external output feature of audio
+                        * - Support charging through micro-usb port without data
+                        *           connection if TA cable is connected to target.
+                        * - Support charging and data connection through micro-usb port
+                        *           if USB cable is connected between target and host
+                        *           device.
+                        * - Support OTG device (Mouse/Keyboard)
+                        */
+                       ret = max77693_muic_set_path(info, info->path_usb, attached);
+                       if (ret < 0)
+                               return ret;
+
+                       extcon_set_cable_state(info->edev, "Dock-Smart", attached);
+                       extcon_set_cable_state(info->edev, "MHL", attached);
+
+                       break;
                }
 
-               /* Only USB cable, PATH:AP_USB */
-               ret = max77693_muic_set_path(info, CONTROL1_SW_USB, attached);
-               if (ret < 0)
-                       goto out;
-               extcon_set_cable_state(info->edev, "USB", attached);
+               /* Check MAX77693_CABLE_GROUP_CHG type */
+               switch (chg_type) {
+               case MAX77693_CHARGER_TYPE_NONE:
+                       /*
+                        * When MHL(with USB/TA cable) or Dock-Audio with USB/TA cable
+                        * is attached, muic device happen below two interrupt.
+                        * - 'MAX77693_MUIC_IRQ_INT1_ADC' for detecting MHL/Dock-Audio.
+                        * - 'MAX77693_MUIC_IRQ_INT2_CHGTYP' for detecting USB/TA cable
+                        *   connected to MHL or Dock-Audio.
+                        * Always, happen eariler MAX77693_MUIC_IRQ_INT1_ADC interrupt
+                        * than MAX77693_MUIC_IRQ_INT2_CHGTYP interrupt.
+                        *
+                        * If user attach MHL (with USB/TA cable and immediately detach
+                        * MHL with USB/TA cable before MAX77693_MUIC_IRQ_INT2_CHGTYP
+                        * interrupt is happened, USB/TA cable remain connected state to
+                        * target. But USB/TA cable isn't connected to target. The user
+                        * be face with unusual action. So, driver should check this
+                        * situation in spite of, that previous charger type is N/A.
+                        */
+                       break;
+               case MAX77693_CHARGER_TYPE_USB:
+                       /* Only USB cable, PATH:AP_USB */
+                       ret = max77693_muic_set_path(info, info->path_usb, attached);
+                       if (ret < 0)
+                               return ret;
+
+                       extcon_set_cable_state(info->edev, "USB", attached);
+                       break;
+               case MAX77693_CHARGER_TYPE_DEDICATED_CHG:
+                       /* Only TA cable */
+                       extcon_set_cable_state(info->edev, "TA", attached);
+                       break;
+               }
                break;
        case MAX77693_CHARGER_TYPE_DOWNSTREAM_PORT:
                extcon_set_cable_state(info->edev,
                                "Charge-downstream", attached);
                break;
-       case MAX77693_CHARGER_TYPE_DEDICATED_CHG:
-               extcon_set_cable_state(info->edev, "TA", attached);
-               break;
        case MAX77693_CHARGER_TYPE_APPLE_500MA:
                extcon_set_cable_state(info->edev, "Slow-charger", attached);
                break;
@@ -803,12 +903,10 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
                dev_err(info->dev,
                        "failed to detect %s accessory (chg_type:0x%x)\n",
                        attached ? "attached" : "detached", chg_type);
-               ret = -EINVAL;
-               goto out;
+               return -EINVAL;
        }
 
-out:
-       return ret;
+       return 0;
 }
 
 static void max77693_muic_irq_work(struct work_struct *work)
@@ -863,7 +961,8 @@ static void max77693_muic_irq_work(struct work_struct *work)
        default:
                dev_err(info->dev, "muic interrupt: irq %d occurred\n",
                                irq_type);
-               break;
+               mutex_unlock(&info->mutex);
+               return;
        }
 
        if (ret < 0)
@@ -911,21 +1010,27 @@ static int max77693_muic_detect_accessory(struct max77693_muic_info *info)
                                        &attached);
        if (attached && adc != MAX77693_MUIC_ADC_OPEN) {
                ret = max77693_muic_adc_handler(info);
-               if (ret < 0)
+               if (ret < 0) {
                        dev_err(info->dev, "Cannot detect accessory\n");
+                       mutex_unlock(&info->mutex);
+                       return ret;
+               }
        }
 
        chg_type = max77693_muic_get_cable_type(info, MAX77693_CABLE_GROUP_CHG,
                                        &attached);
        if (attached && chg_type != MAX77693_CHARGER_TYPE_NONE) {
                ret = max77693_muic_chg_handler(info);
-               if (ret < 0)
+               if (ret < 0) {
                        dev_err(info->dev, "Cannot detect charger accessory\n");
+                       mutex_unlock(&info->mutex);
+                       return ret;
+               }
        }
 
        mutex_unlock(&info->mutex);
 
-       return ret;
+       return 0;
 }
 
 static void max77693_muic_detect_cable_wq(struct work_struct *work)
@@ -970,7 +1075,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
        }
 
        /* Register input device for button of dock device */
-       info->dock = input_allocate_device();
+       info->dock = devm_input_allocate_device(&pdev->dev);
        if (!info->dock) {
                dev_err(&pdev->dev, "%s: failed to allocate input\n", __func__);
                return -ENOMEM;