extcon: max77693: Add support dock device and buttons
authorChanwoo Choi <cw00.choi@samsung.com>
Mon, 3 Dec 2012 04:09:41 +0000 (13:09 +0900)
committerChanwoo Choi <cw00.choi@samsung.com>
Tue, 15 Jan 2013 06:42:15 +0000 (15:42 +0900)
This patch support detection of dock device with extcon and
buttons of dock device for playing music with input subsystem.

Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Myungjoo Ham <myungjoo.ham@samsung.com>
drivers/extcon/extcon-max77693.c

index 26ce4dfeda10a707ddb53f3efabcb5fe2079baa9..07ea96bfd0cbbf3dbae05f4cba91dc192ab4690f 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
+#include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/err.h>
 #include <linux/platform_device.h>
@@ -44,11 +45,15 @@ struct max77693_muic_info {
        int prev_cable_type;
        int prev_cable_type_gnd;
        int prev_chg_type;
+       int prev_button_type;
        u8 status[2];
 
        int irq;
        struct work_struct irq_work;
        struct mutex mutex;
+
+       /* Button of dock device */
+       struct input_dev *dock;
 };
 
 enum max77693_muic_cable_group {
@@ -156,7 +161,10 @@ enum {
        EXTCON_CABLE_JIG_USB_ON,
        EXTCON_CABLE_JIG_USB_OFF,
        EXTCON_CABLE_JIG_UART_OFF,
-       EXTCON_CABLE_AUDIO_VIDEO_LOAD,
+       EXTCON_CABLE_JIG_UART_ON,
+       EXTCON_CABLE_DOCK_SMART,
+       EXTCON_CABLE_DOCK_DESK,
+       EXTCON_CABLE_DOCK_AUDIO,
 
        _EXTCON_CABLE_NUM,
 };
@@ -173,7 +181,10 @@ const char *max77693_extcon_cable[] = {
        [EXTCON_CABLE_JIG_USB_ON]               = "JIG-USB-ON",
        [EXTCON_CABLE_JIG_USB_OFF]              = "JIG-USB-OFF",
        [EXTCON_CABLE_JIG_UART_OFF]             = "JIG-UART-OFF",
-       [EXTCON_CABLE_AUDIO_VIDEO_LOAD]         = "Audio-video-load",
+       [EXTCON_CABLE_JIG_UART_ON]              = "Dock-Car",
+       [EXTCON_CABLE_DOCK_SMART]               = "Dock-Smart",
+       [EXTCON_CABLE_DOCK_DESK]                = "Dock-Desk",
+       [EXTCON_CABLE_DOCK_AUDIO]               = "Dock-Audio",
 
        NULL,
 };
@@ -411,6 +422,96 @@ static int max77693_muic_get_cable_type(struct max77693_muic_info *info,
        return cable_type;
 }
 
+static int max77693_muic_dock_handler(struct max77693_muic_info *info,
+               int cable_type, bool attached)
+{
+       int ret = 0;
+       char dock_name[CABLE_NAME_MAX];
+
+       dev_info(info->dev,
+               "external connector is %s (adc:0x%02x)\n",
+               attached ? "attached" : "detached", cable_type);
+
+       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);
+               if (ret < 0)
+                       goto out;
+
+               /* Dock-Smart */
+               extcon_set_cable_state(info->edev, "Dock-Smart", attached);
+               goto out;
+       case MAX77693_MUIC_ADC_FACTORY_MODE_UART_ON:    /* Dock-Car */
+               strcpy(dock_name, "Dock-Car");
+               break;
+       case MAX77693_MUIC_ADC_AUDIO_MODE_REMOTE:       /* Dock-Desk */
+               strcpy(dock_name, "Dock-Desk");
+               break;
+       case MAX77693_MUIC_ADC_AV_CABLE_NOLOAD:         /* Dock-Audio */
+               strcpy(dock_name, "Dock-Audio");
+               if (!attached)
+                       extcon_set_cable_state(info->edev, "USB", false);
+               break;
+       }
+
+       /* Dock-Car/Desk/Audio, PATH:AUDIO */
+       ret = max77693_muic_set_path(info, CONTROL1_SW_AUDIO, attached);
+       if (ret < 0)
+               goto out;
+       extcon_set_cable_state(info->edev, dock_name, attached);
+
+out:
+       return ret;
+}
+
+static int max77693_muic_dock_button_handler(struct max77693_muic_info *info,
+               int button_type, bool attached)
+{
+       struct input_dev *dock = info->dock;
+       unsigned int code;
+       int ret = 0;
+
+       switch (button_type) {
+       case MAX77693_MUIC_ADC_REMOTE_S3_BUTTON-1
+               ... MAX77693_MUIC_ADC_REMOTE_S3_BUTTON+1:
+               /* DOCK_KEY_PREV */
+               code = KEY_PREVIOUSSONG;
+               break;
+       case MAX77693_MUIC_ADC_REMOTE_S7_BUTTON-1
+               ... MAX77693_MUIC_ADC_REMOTE_S7_BUTTON+1:
+               /* DOCK_KEY_NEXT */
+               code = KEY_NEXTSONG;
+               break;
+       case MAX77693_MUIC_ADC_REMOTE_S9_BUTTON:
+               /* DOCK_VOL_DOWN */
+               code = KEY_VOLUMEDOWN;
+               break;
+       case MAX77693_MUIC_ADC_REMOTE_S10_BUTTON:
+               /* DOCK_VOL_UP */
+               code = KEY_VOLUMEUP;
+               break;
+       case MAX77693_MUIC_ADC_REMOTE_S12_BUTTON-1
+               ... MAX77693_MUIC_ADC_REMOTE_S12_BUTTON+1:
+               /* DOCK_KEY_PLAY_PAUSE */
+               code = KEY_PLAYPAUSE;
+               break;
+       default:
+               dev_err(info->dev,
+                       "failed to detect %s key (adc:0x%x)\n",
+                       attached ? "pressed" : "released", button_type);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       input_event(dock, EV_KEY, code, attached);
+       input_sync(dock);
+
+out:
+       return 0;
+}
+
 static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info)
 {
        int cable_type_gnd;
@@ -494,6 +595,7 @@ out:
 static int max77693_muic_adc_handler(struct max77693_muic_info *info)
 {
        int cable_type;
+       int button_type;
        bool attached;
        int ret = 0;
 
@@ -519,31 +621,58 @@ static int max77693_muic_adc_handler(struct max77693_muic_info *info)
                if (ret < 0)
                        goto out;
                break;
-       case MAX77693_MUIC_ADC_FACTORY_MODE_UART_ON:
-       case MAX77693_MUIC_ADC_AUDIO_MODE_REMOTE:
-               /* Audio Video cable with no-load */
-               ret = max77693_muic_set_path(info, CONTROL1_SW_AUDIO, attached);
+       case MAX77693_MUIC_ADC_RESERVED_ACC_3:          /* Dock-Smart */
+       case MAX77693_MUIC_ADC_FACTORY_MODE_UART_ON:    /* Dock-Car */
+       case MAX77693_MUIC_ADC_AUDIO_MODE_REMOTE:       /* Dock-Desk */
+       case MAX77693_MUIC_ADC_AV_CABLE_NOLOAD:         /* Dock-Audio */
+               /*
+                * DOCK device
+                *
+                * The MAX77693 MUIC device can detect total 34 cable type
+                * except of charger cable and MUIC device didn't define
+                * specfic role of cable in the range of from 0x01 to 0x12
+                * of ADC value. So, can use/define cable with no role according
+                * to schema of hardware board.
+                */
+               ret = max77693_muic_dock_handler(info, cable_type, attached);
+               if (ret < 0)
+                       goto out;
+               break;
+       case MAX77693_MUIC_ADC_REMOTE_S3_BUTTON:        /* DOCK_KEY_PREV */
+       case MAX77693_MUIC_ADC_REMOTE_S7_BUTTON:        /* DOCK_KEY_NEXT */
+       case MAX77693_MUIC_ADC_REMOTE_S9_BUTTON:        /* DOCK_VOL_DOWN */
+       case MAX77693_MUIC_ADC_REMOTE_S10_BUTTON:       /* DOCK_VOL_UP */
+       case MAX77693_MUIC_ADC_REMOTE_S12_BUTTON:       /* DOCK_KEY_PLAY_PAUSE */
+               /*
+                * Button of DOCK device
+                * - the Prev/Next/Volume Up/Volume Down/Play-Pause button
+                *
+                * The MAX77693 MUIC device can detect total 34 cable type
+                * except of charger cable and MUIC device didn't define
+                * specfic role of cable in the range of from 0x01 to 0x12
+                * of ADC value. So, can use/define cable with no role according
+                * to schema of hardware board.
+                */
+               if (attached)
+                       button_type = info->prev_button_type = cable_type;
+               else
+                       button_type = info->prev_button_type;
+
+               ret = max77693_muic_dock_button_handler(info, button_type,
+                                                       attached);
                if (ret < 0)
                        goto out;
-               extcon_set_cable_state(info->edev,
-                               "Audio-video-noload", attached);
                break;
        case MAX77693_MUIC_ADC_SEND_END_BUTTON:
        case MAX77693_MUIC_ADC_REMOTE_S1_BUTTON:
        case MAX77693_MUIC_ADC_REMOTE_S2_BUTTON:
-       case MAX77693_MUIC_ADC_REMOTE_S3_BUTTON:
        case MAX77693_MUIC_ADC_REMOTE_S4_BUTTON:
        case MAX77693_MUIC_ADC_REMOTE_S5_BUTTON:
        case MAX77693_MUIC_ADC_REMOTE_S6_BUTTON:
-       case MAX77693_MUIC_ADC_REMOTE_S7_BUTTON:
        case MAX77693_MUIC_ADC_REMOTE_S8_BUTTON:
-       case MAX77693_MUIC_ADC_REMOTE_S9_BUTTON:
-       case MAX77693_MUIC_ADC_REMOTE_S10_BUTTON:
        case MAX77693_MUIC_ADC_REMOTE_S11_BUTTON:
-       case MAX77693_MUIC_ADC_REMOTE_S12_BUTTON:
        case MAX77693_MUIC_ADC_RESERVED_ACC_1:
        case MAX77693_MUIC_ADC_RESERVED_ACC_2:
-       case MAX77693_MUIC_ADC_RESERVED_ACC_3:
        case MAX77693_MUIC_ADC_RESERVED_ACC_4:
        case MAX77693_MUIC_ADC_RESERVED_ACC_5:
        case MAX77693_MUIC_ADC_CEA936_AUDIO:
@@ -551,11 +680,12 @@ static int max77693_muic_adc_handler(struct max77693_muic_info *info)
        case MAX77693_MUIC_ADC_TTY_CONVERTER:
        case MAX77693_MUIC_ADC_UART_CABLE:
        case MAX77693_MUIC_ADC_CEA936A_TYPE1_CHG:
-       case MAX77693_MUIC_ADC_AV_CABLE_NOLOAD:
        case MAX77693_MUIC_ADC_CEA936A_TYPE2_CHG:
-               /* This accessory isn't used in general case if it is specially
-                  needed to detect additional accessory, should implement
-                  proper operation when this accessory is attached/detached. */
+               /*
+                * This accessory isn't used in general case if it is specially
+                * needed to detect additional accessory, should implement
+                * proper operation when this accessory is attached/detached.
+                */
                dev_info(info->dev,
                        "accessory is %s but it isn't used (adc:0x%x)\n",
                        attached ? "attached" : "detached", cable_type);
@@ -576,6 +706,7 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
 {
        int chg_type;
        int cable_type_gnd;
+       int cable_type;
        bool attached;
        bool cable_attached;
        int ret = 0;
@@ -590,35 +721,52 @@ 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.
+                */
                cable_type_gnd = max77693_muic_get_cable_type(info,
                                        MAX77693_CABLE_GROUP_ADC_GND,
                                        &cable_attached);
-
-               switch (cable_type_gnd) {
-               case MAX77693_MUIC_GND_MHL:
-               case MAX77693_MUIC_GND_MHL_VB:
-                       /*
-                        * USB/TA with MHL cable
-                        * - MHL cable, which connect micro USB or TA cable,
-                        * is used to charging battery. So, extcon driver check
-                        * charging type whether micro USB or TA cable is
-                        * connected to MHL cable when extcon driver detect MHL
-                        * cable.
-                        */
+               if (cable_type_gnd == MAX77693_MUIC_GND_MHL
+                       || cable_type_gnd == MAX77693_MUIC_GND_MHL_VB) {
                        extcon_set_cable_state(info->edev, "MHL_TA", attached);
 
                        if (!cable_attached)
                                extcon_set_cable_state(info->edev,
-                                               "MHL", false);
-                       break;
-               default:
-                       /* Only USB cable, PATH:AP_USB */
-                       ret = max77693_muic_set_path(info, CONTROL1_SW_USB,
-                                               attached);
-                       if (ret < 0)
-                               goto out;
+                                       "MHL", false);
+                       goto out;
+               }
+
+               /*
+                * 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.
+                */
+               cable_type = max77693_muic_get_cable_type(info,
+                                       MAX77693_CABLE_GROUP_ADC,
+                                       &cable_attached);
+               if (cable_type == MAX77693_MUIC_ADC_AV_CABLE_NOLOAD) {
                        extcon_set_cable_state(info->edev, "USB", attached);
+
+                       if (!cable_attached)
+                               extcon_set_cable_state(info->edev,
+                                               "Dock-Audio", false);
+                       goto out;
                }
+
+               /* 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);
                break;
        case MAX77693_CHARGER_TYPE_DOWNSTREAM_PORT:
                extcon_set_cable_state(info->edev,
@@ -794,6 +942,32 @@ static int max77693_muic_probe(struct platform_device *pdev)
                        return ret;
                }
        }
+
+       /* Register input device for button of dock device */
+       info->dock = input_allocate_device();
+       if (!info->dock) {
+               dev_err(&pdev->dev, "%s: failed to allocate input\n", __func__);
+               return -ENOMEM;
+       }
+       info->dock->name = "max77693-muic/dock";
+       info->dock->phys = "max77693-muic/extcon";
+       info->dock->dev.parent = &pdev->dev;
+
+       __set_bit(EV_REP, info->dock->evbit);
+
+       input_set_capability(info->dock, EV_KEY, KEY_VOLUMEUP);
+       input_set_capability(info->dock, EV_KEY, KEY_VOLUMEDOWN);
+       input_set_capability(info->dock, EV_KEY, KEY_PLAYPAUSE);
+       input_set_capability(info->dock, EV_KEY, KEY_PREVIOUSSONG);
+       input_set_capability(info->dock, EV_KEY, KEY_NEXTSONG);
+
+       ret = input_register_device(info->dock);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Cannot register input device error(%d)\n",
+                               ret);
+               return ret;
+       }
+
        platform_set_drvdata(pdev, info);
        mutex_init(&info->mutex);
 
@@ -870,7 +1044,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
                        MAX77693_MUIC_REG_ID, &id);
        if (ret < 0) {
                dev_err(&pdev->dev, "failed to read revision number\n");
-               goto err_irq;
+               goto err_extcon;
        }
        dev_info(info->dev, "device ID : 0x%x\n", id);
 
@@ -882,6 +1056,8 @@ static int max77693_muic_probe(struct platform_device *pdev)
 
        return ret;
 
+err_extcon:
+       extcon_dev_unregister(info->edev);
 err_irq:
        while (--i >= 0)
                free_irq(muic_irqs[i].virq, info);
@@ -896,6 +1072,7 @@ static int max77693_muic_remove(struct platform_device *pdev)
        for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
                free_irq(muic_irqs[i].virq, info);
        cancel_work_sync(&info->irq_work);
+       input_unregister_device(info->dock);
        extcon_dev_unregister(info->edev);
 
        return 0;