/* drivers/misc/sec_jack.c * * Copyright (C) 2015 Samsung Electronics Co.Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NUM_INPUT_DEVICE_ID 2 #define MAX_ZONE_LIMIT 10 #define DEFAULT_KEY_DEBOUNCE_TIME_MS 30 #define DEFAULT_DET_DEBOUNCE_TIME_MS 100 #define WAKE_LOCK_TIME (HZ * 5) /* 5 sec */ struct sec_jack_info { struct platform_device *client; struct sec_jack_platform_data *pdata; struct work_struct buttons_work; struct work_struct detect_work; struct workqueue_struct *detect_wq; struct workqueue_struct *buttons_wq; struct timer_list timer; struct input_dev *input_dev; struct wake_lock det_wake_lock; struct input_handler jack_handler; struct input_device_id ids[NUM_INPUT_DEVICE_ID]; int det_irq; int dev_id; int pressed; int pressed_code; bool buttons_enable; struct platform_device *send_key_dev; unsigned int cur_jack_type; }; /* define call back function for buuton notifcation */ static sec_jack_button_notify_cb button_notify_cb; /* with some modifications like moving all the gpio structs inside * the platform data and getting the name for the switch and * gpio_event from the platform data, the driver could support more than * one headset jack, but currently user space is looking only for * one key file and switch for a headset so it'd be overkill and * untestable so we limit to one instantiation for now. */ static atomic_t instantiated = ATOMIC_INIT(0); /* sysfs name HeadsetObserver.java looks for to track headset state */ struct switch_dev switch_jack_detection = { .name = "h2w", }; /* Samsung factory test application looks for to track button state */ struct switch_dev switch_sendend = { .name = "send_end", /* /sys/class/switch/send_end/state */ }; static struct gpio_event_direct_entry sec_jack_key_map[] = { { .code = KEY_UNKNOWN, }, }; static struct gpio_event_input_info sec_jack_key_info = { .info.func = gpio_event_input_func, .info.no_suspend = true, .type = EV_KEY, .debounce_time.tv64 = DEFAULT_KEY_DEBOUNCE_TIME_MS * NSEC_PER_MSEC, .keymap = sec_jack_key_map, .keymap_size = ARRAY_SIZE(sec_jack_key_map) }; static struct gpio_event_info *sec_jack_input_info[] = { &sec_jack_key_info.info, }; static struct gpio_event_platform_data sec_jack_input_data = { .name = "sec_jack", .info = sec_jack_input_info, .info_count = ARRAY_SIZE(sec_jack_input_info), }; struct sec_jack_control_data jack_controls; EXPORT_SYMBOL_GPL(jack_controls); int sec_jack_register_button_notify_cb(sec_jack_button_notify_cb func) { if (func == NULL) return -1; button_notify_cb = func; return 0; } EXPORT_SYMBOL_GPL(sec_jack_register_button_notify_cb); int get_jack_state(){ return jack_controls.jack_type; } EXPORT_SYMBOL_GPL(get_jack_state); static int sec_jack_gpio_init(struct sec_jack_platform_data *pdata) { int ret; if (!IS_ERR_OR_NULL(pdata->jack_pinctrl)) { ret = pinctrl_select_state(pdata->jack_pinctrl, pdata->jack_pins_active); if (ret) { pr_err("%s(): Failed to pinctrl set_state\n", __func__); pdata->jack_pinctrl = NULL; return ret; } } if (pdata->det_gpio > 0) { ret = gpio_request(pdata->det_gpio, "jack_detect"); if (ret) { pr_err("%s: failed to request det_gpio\n", __func__); return ret; } } if (pdata->ear_micbias_en_gpio > 0) { ret = gpio_request(pdata->ear_micbias_en_gpio, "ear_micbias_en"); if (ret) { pr_err("%s: failed to request ear_micbias_en\n", __func__); goto err_request_ear_micbias; } gpio_direction_output(pdata->ear_micbias_en_gpio, 0); } return 0; err_request_ear_micbias: if (pdata->det_gpio) gpio_free(pdata->det_gpio); return ret; } static int sec_jack_get_adc_value(struct sec_jack_info *hi) { struct sec_jack_platform_data *pdata = hi->pdata; int adc_val; if (pdata->jack_controls->get_adc) { adc_val = pdata->jack_controls->get_adc(); dev_info(&hi->client->dev, "%s: %d\n", __func__, adc_val); } else { dev_err(&hi->client->dev, "No function for get_adc\n"); return -EFAULT; } return adc_val; } static void set_sec_micbias_state(struct sec_jack_info *hi, bool state) { struct sec_jack_platform_data *pdata = hi->pdata; if (pdata->ear_micbias_en_gpio > 0) gpio_set_value_cansleep(pdata->ear_micbias_en_gpio, state); else if (pdata->jack_controls->set_micbias) pdata->jack_controls->set_micbias(state); else dev_err(&hi->client->dev, "No gpio or function for bias control\n"); } /* gpio_input driver does not support to read adc value. * We use input filter to support 3-buttons of headset * without changing gpio_input driver. */ static bool sec_jack_buttons_filter(struct input_handle *jack_handle, unsigned int type, unsigned int code, int value) { struct sec_jack_info *hi = jack_handle->handler->private; if (type != EV_KEY || code != KEY_UNKNOWN) return false; hi->pressed = value; /* This is called in timer handler of gpio_input driver. * We use workqueue to read adc value. */ queue_work(hi->buttons_wq, &hi->buttons_work); return true; } static int sec_jack_buttons_connect(struct input_handler *jack_handler, struct input_dev *dev, const struct input_device_id *id) { struct sec_jack_info *hi; struct sec_jack_platform_data *pdata; struct sec_jack_buttons_zone *btn_zones; int err; int i; int num_buttons_zones; struct input_handle *jack_handle; /* bind input_handler to input device related to only sec_jack */ if (dev->name != sec_jack_input_data.name) return -ENODEV; hi = jack_handler->private; pdata = hi->pdata; btn_zones = pdata->jack_buttons_zones; num_buttons_zones = ARRAY_SIZE(pdata->jack_buttons_zones); hi->input_dev = dev; jack_handle = kzalloc(sizeof(*jack_handle), GFP_KERNEL); if (!jack_handle) { dev_err(&hi->client->dev, "Failed to alloc jack_handle\n"); return -ENOMEM; } jack_handle->dev = dev; jack_handle->handler = jack_handler; jack_handle->name = "sec_jack_buttons"; err = input_register_handle(jack_handle); if (err) { dev_err(&hi->client->dev, "Failed to input_register_handle %d\n", err); goto err_register_handle; } err = input_open_device(jack_handle); if (err) { dev_err(&hi->client->dev, "Failed to input_open_device %d\n", err); goto err_open_device; } for (i = 0; i < num_buttons_zones; i++) input_set_capability(dev, EV_KEY, btn_zones[i].code); return 0; err_open_device: input_unregister_handle(jack_handle); err_register_handle: kfree(jack_handle); return err; } static void sec_jack_buttons_disconnect(struct input_handle *jack_handle) { input_close_device(jack_handle); input_unregister_handle(jack_handle); kfree(jack_handle); } static void sec_jack_set_type(struct sec_jack_info *hi, enum sec_jack_type jack_type) { /* this can happen during slow inserts where we think we identified * the type but then we get another interrupt and do it again */ if (jack_type == hi->cur_jack_type) { if ((jack_type != SEC_HEADSET_4POLE) && (jack_type != SEC_EXTERNAL_ANTENNA)) set_sec_micbias_state(hi, false); return; } if ((jack_type == SEC_HEADSET_4POLE) || (jack_type == SEC_EXTERNAL_ANTENNA)) { /* for a 4 pole headset, enable detection of send/end key */ if (hi->send_key_dev == NULL) /* enable to get events again */ hi->send_key_dev = platform_device_register_data(NULL, GPIO_EVENT_DEV_NAME, hi->dev_id, &sec_jack_input_data, sizeof(sec_jack_input_data)); mod_timer(&hi->timer, jiffies + msecs_to_jiffies(1000)); } else { /* micbias is left enabled for 4pole and disabled otherwise */ set_sec_micbias_state(hi, false); /* for all other jacks, disable send/end key detection */ if (hi->send_key_dev != NULL) { /* disable to prevent false events on next insert */ platform_device_unregister(hi->send_key_dev); hi->send_key_dev = NULL; del_timer_sync(&hi->timer); hi->buttons_enable = false; } } hi->cur_jack_type = jack_type; jack_controls.jack_type = jack_type; dev_info(&hi->client->dev, "jack_type: %d\n", jack_type); if ((jack_type == SEC_HEADSET_4POLE) || (jack_type == SEC_HEADSET_3POLE)) { if (hi->pdata->jack_controls->hp_imp_detect) hi->pdata->jack_controls->hp_imp_detect(); } switch_set_state(&switch_jack_detection, jack_type); } static void handle_jack_not_inserted(struct sec_jack_info *hi) { if (hi->pdata->jack_controls->hp_imp_unplug) hi->pdata->jack_controls->hp_imp_unplug(); sec_jack_set_type(hi, SEC_JACK_NO_DEVICE); } static void determine_jack_type(struct sec_jack_info *hi) { struct sec_jack_platform_data *pdata = hi->pdata; struct sec_jack_zone *zones = pdata->jack_zones; int size = ARRAY_SIZE(pdata->jack_zones); int count[MAX_ZONE_LIMIT] = {0}; int adc; int i; unsigned npolarity = !pdata->det_active_high; /* set mic bias to enable adc */ set_sec_micbias_state(hi, true); while (gpio_get_value(pdata->det_gpio) ^ npolarity) { adc = sec_jack_get_adc_value(hi); if (adc < 0) { dev_err(&hi->client->dev, "Unavailable ADC %d\n", adc); return; } /* determine the type of headset based on the * adc value. An adc value can fall in various * ranges or zones. Within some ranges, the type * can be returned immediately. Within others, the * value is considered unstable and we need to sample * a few more types (up to the limit determined by * the range) before we return the type for that range. */ for (i = 0; i < size; i++) { if (adc <= zones[i].adc_high) { if (++count[i] > zones[i].check_count) { sec_jack_set_type(hi, zones[i].jack_type); return; } if (zones[i].delay_us > 0) usleep_range(zones[i].delay_us, zones[i].delay_us); break; } } } /* jack removed before detection complete */ dev_info(&hi->client->dev, "jack removed before detection complete\n"); handle_jack_not_inserted(hi); } static ssize_t key_state_onoff_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sec_jack_info *hi = dev_get_drvdata(dev); int value = 0; if (hi->pressed && hi->pressed_code == KEY_MEDIA) value = 1; return snprintf(buf, 4, "%d\n", value); } static DEVICE_ATTR(key_state, 0444 , key_state_onoff_show, NULL); static ssize_t earjack_state_onoff_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sec_jack_info *hi = dev_get_drvdata(dev); int value = 0; if (hi->cur_jack_type == SEC_HEADSET_4POLE) value = 1; return snprintf(buf, 4, "%d\n", value); } static DEVICE_ATTR(state, 0444 , earjack_state_onoff_show, NULL); static ssize_t earjack_mic_adc_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sec_jack_info *hi = dev_get_drvdata(dev); struct sec_jack_platform_data *pdata = hi->pdata; int adc_val = 0; if (pdata->jack_controls->get_adc) { adc_val = pdata->jack_controls->get_adc(); dev_info(&hi->client->dev, "%s: %d\n", __func__, adc_val); } return snprintf(buf, 5, "%d\n", adc_val); } static DEVICE_ATTR(mic_adc, 0444 , earjack_mic_adc_show, NULL); static void sec_jack_timer_handler(unsigned long data) { struct sec_jack_info *hi = (struct sec_jack_info *)data; hi->buttons_enable = true; } /* thread run whenever the headset detect state changes (either insertion * or removal). */ static irqreturn_t sec_jack_detect_irq(int irq, void *dev_id) { struct sec_jack_info *hi = dev_id; queue_work(hi->detect_wq, &hi->detect_work); return IRQ_HANDLED; } void sec_jack_detect_work(struct work_struct *work) { struct sec_jack_info *hi = container_of(work, struct sec_jack_info, detect_work); struct sec_jack_platform_data *pdata = hi->pdata; unsigned npolarity = !hi->pdata->det_active_high; int time_left_ms; if (pdata->det_debounce_time_ms > 0) time_left_ms = pdata->det_debounce_time_ms; else time_left_ms = DEFAULT_DET_DEBOUNCE_TIME_MS; /* prevent suspend to allow user space to respond to switch */ wake_lock_timeout(&hi->det_wake_lock, WAKE_LOCK_TIME); dev_info(&hi->client->dev, "%s: irq(%d)\n", __func__, gpio_get_value(pdata->det_gpio) ^ npolarity); /* debounce headset jack. don't try to determine the type of * headset until the detect state is true for a while. */ while (time_left_ms > 0) { if (!(gpio_get_value(hi->pdata->det_gpio) ^ npolarity)) { /* jack not detected. */ handle_jack_not_inserted(hi); return; } usleep_range(10000, 11000); time_left_ms -= 10; } /* jack presence was detected the whole time, figure out which type */ determine_jack_type(hi); } /* thread run whenever the button of headset is pressed or released */ void sec_jack_buttons_work(struct work_struct *work) { struct sec_jack_info *hi = container_of(work, struct sec_jack_info, buttons_work); struct sec_jack_platform_data *pdata = hi->pdata; struct sec_jack_buttons_zone *btn_zones = pdata->jack_buttons_zones; int num_buttons_zones = ARRAY_SIZE(pdata->jack_buttons_zones); int adc; int i; if (!hi->buttons_enable) { dev_info(&hi->client->dev, "key %d is skipped\n", hi->pressed_code); return; } /* prevent suspend to allow user space to respond to switch */ wake_lock_timeout(&hi->det_wake_lock, WAKE_LOCK_TIME); /* when button is released */ if (hi->pressed == 0) { input_report_key(hi->input_dev, hi->pressed_code, 0); input_sync(hi->input_dev); switch_set_state(&switch_sendend, 0); dev_info(&hi->client->dev, "key %d is released\n", hi->pressed_code); if (button_notify_cb) button_notify_cb(hi->pressed_code, 0); return; } /* when button is pressed */ adc = sec_jack_get_adc_value(hi); if (adc < 0) { dev_err(&hi->client->dev, "Unavailable key ADC %d\n", adc); return; } for (i = 0; i < num_buttons_zones; i++) if (adc >= btn_zones[i].adc_low && adc <= btn_zones[i].adc_high) { hi->pressed_code = btn_zones[i].code; input_report_key(hi->input_dev, btn_zones[i].code, 1); input_sync(hi->input_dev); switch_set_state(&switch_sendend, 1); dev_info(&hi->client->dev, "adc = %d, key %d is pressed\n", adc, btn_zones[i].code); if (button_notify_cb) button_notify_cb(btn_zones[i].code, true); return; } dev_warn(&hi->client->dev, "key skipped. ADC %d\n", adc); } #ifdef CONFIG_OF static struct sec_jack_platform_data *sec_jack_populate_dt_pdata(struct device *dev) { struct sec_jack_platform_data *pdata; struct of_phandle_args args; int i = 0; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { dev_err(dev, "Failed to allocate memory for pdata\n"); goto alloc_err; } pdata->det_gpio = of_get_named_gpio(dev->of_node, "detect-gpio", 0); if (pdata->det_gpio < 0) { dev_err(dev, "Failed to find detect-gpio\n"); goto of_parse_err; } pdata->key_gpio = of_get_named_gpio(dev->of_node, "key-gpio", 0); if (pdata->key_gpio < 0) { dev_err(dev, "Failed to find key-gpio\n"); goto of_parse_err; } pdata->ear_micbias_en_gpio = of_get_named_gpio(dev->of_node, "ear-micbias-en-gpio", 0); if (pdata->ear_micbias_en_gpio < 0) dev_info(dev, "ear-micbias-en-gpio not found\n"); if (of_property_read_u32(dev->of_node, "key-dbtime", &pdata->key_debounce_time_ms)) dev_info(dev, "key-dbtime not found, use default\n"); if (of_property_read_u32(dev->of_node, "det-dbtime", &pdata->det_debounce_time_ms)) dev_info(dev, "det-dbtime not found, use default\n"); for (i = 0; i < 4; i++) { of_parse_phandle_with_args(dev->of_node, "det-zones-list", "#list-det-cells", i, &args); pdata->jack_zones[i].adc_high = args.args[0]; pdata->jack_zones[i].delay_us = args.args[1]; pdata->jack_zones[i].check_count = args.args[2]; pdata->jack_zones[i].jack_type = args.args[3]; dev_dbg(dev, "det-zones-list %d, %d, %d, %d, %d\n", args.args_count, args.args[0], args.args[1], args.args[2], args.args[3]); } for (i = 0; i < 4; i++) { of_parse_phandle_with_args(dev->of_node, "but-zones-list", "#list-but-cells", i, &args); pdata->jack_buttons_zones[i].code = args.args[0]; pdata->jack_buttons_zones[i].adc_low = args.args[1]; pdata->jack_buttons_zones[i].adc_high = args.args[2]; dev_dbg(dev, "but-zones-list %d, %d, %d, %d\n", args.args_count, args.args[0], args.args[1], args.args[2]); } if (of_find_property(dev->of_node, "det-active-high", NULL)) pdata->det_active_high = true; if (of_find_property(dev->of_node, "send-end-active-high", NULL)) pdata->send_end_active_high = true; return pdata; of_parse_err: devm_kfree(dev, pdata); alloc_err: return NULL; } #else static struct sec_jack_platform_data *sec_jack_populate_dt_pdata(struct device *dev) { return NULL; } #endif static int sec_jack_get_pinctrl(struct sec_jack_platform_data *pdata, struct device *dev) { pdata->jack_pinctrl = devm_pinctrl_get(dev); if (IS_ERR_OR_NULL(pdata->jack_pinctrl)) { if (PTR_ERR(pdata->jack_pinctrl) == -EPROBE_DEFER) { pdata->jack_pinctrl = NULL; return -EPROBE_DEFER; } dev_err(dev, "Target does not use pinctrl\n"); goto pinctrl_fail; } pdata->jack_pins_active = pinctrl_lookup_state(pdata->jack_pinctrl, "earjack_gpio_active"); if (IS_ERR(pdata->jack_pins_active)) { dev_err(dev, "could not get active pin state\n"); goto pinctrl_fail; } return 0; pinctrl_fail: pdata->jack_pinctrl = NULL; return -EINVAL; } static int sec_jack_probe(struct platform_device *pdev) { struct sec_jack_info *hi; struct sec_jack_platform_data *pdata; struct class *audio; struct device *earjack; int ret; if (jack_controls.snd_card_registered != 1) { dev_info(&pdev->dev, "defer as sound card not registered\n"); return -EPROBE_DEFER; } dev_info(&pdev->dev, "Registering sec_jack driver\n"); if (dev_get_platdata(&pdev->dev)) pdata = pdev->dev.platform_data; else pdata = sec_jack_populate_dt_pdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "pdata is NULL\n"); return -ENODEV; } ret = sec_jack_get_pinctrl(pdata, &pdev->dev); if (ret) { dev_err(&pdev->dev, "cannot config earjack pinctrl\n"); return -ENODEV; } ret = sec_jack_gpio_init(pdata); if (ret) { dev_err(&pdev->dev, "Failed to init gpio(%d)\n", ret); return -ENODEV; } if (atomic_xchg(&instantiated, 1)) { dev_err(&pdev->dev, "already instantiated, can only have one\n"); return -ENODEV; } sec_jack_key_map[0].gpio = pdata->key_gpio; pdata->jack_controls = &jack_controls; hi = kzalloc(sizeof(struct sec_jack_info), GFP_KERNEL); if (hi == NULL) { dev_err(&pdev->dev, "Failed to allocate memory\n"); ret = -ENOMEM; goto err_kzalloc; } hi->pdata = pdata; /* make the id of our gpi_event device the same as our platform device, * which makes it the responsiblity of the board file to make sure * it is unique relative to other gpio_event devices */ hi->dev_id = pdev->id; /* to read the vadc */ hi->client = pdev; ret = switch_dev_register(&switch_jack_detection); if (ret < 0) { dev_err(&pdev->dev, "Failed to register h2w switch device\n"); goto err_h2w_dev_register; } ret = switch_dev_register(&switch_sendend); if (ret < 0) { dev_err(&pdev->dev, "Failed to register key switch device\n"); goto err_send_end_dev_register; } wake_lock_init(&hi->det_wake_lock, WAKE_LOCK_SUSPEND, "sec_jack_det"); setup_timer(&hi->timer, sec_jack_timer_handler, (unsigned long)hi); INIT_WORK(&hi->detect_work, sec_jack_detect_work); INIT_WORK(&hi->buttons_work, sec_jack_buttons_work); hi->detect_wq = create_singlethread_workqueue("detect_wq"); if (hi->detect_wq == NULL) { ret = -ENOMEM; dev_err(&pdev->dev, "Failed to create detect_wq\n"); goto err_create_detect_wq_failed; } hi->buttons_wq = create_singlethread_workqueue("buttons_wq"); if (hi->buttons_wq == NULL) { ret = -ENOMEM; dev_err(&pdev->dev, "Failed to create buttons_wq\n"); goto err_create_buttons_wq_failed; } queue_work(hi->detect_wq, &hi->detect_work); hi->det_irq = gpio_to_irq(pdata->det_gpio); set_bit(EV_KEY, hi->ids[0].evbit); hi->ids[0].flags = INPUT_DEVICE_ID_MATCH_EVBIT; hi->jack_handler.filter = sec_jack_buttons_filter; hi->jack_handler.connect = sec_jack_buttons_connect; hi->jack_handler.disconnect = sec_jack_buttons_disconnect; hi->jack_handler.name = "sec_jack_buttons"; hi->jack_handler.id_table = hi->ids; hi->jack_handler.private = hi; ret = input_register_handler(&hi->jack_handler); if (ret) { dev_err(&pdev->dev, "Failed to input_register_handler\n"); goto err_input_register_handler; } /* We are going to remove this code later */ if (pdata->send_end_active_high == true) sec_jack_key_info.flags = 1; if (pdata->key_debounce_time_ms > 0) sec_jack_key_info.debounce_time.tv64 = pdata->key_debounce_time_ms * NSEC_PER_MSEC; ret = request_irq(hi->det_irq, sec_jack_detect_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "sec_headset_detect", hi); if (ret) { dev_err(&pdev->dev, "Failed to request_irq\n"); goto err_request_detect_irq; } /* to handle insert/removal when we're sleeping in a call */ ret = enable_irq_wake(hi->det_irq); if (ret) { dev_err(&pdev->dev, "Failed to enable_irq_wake\n"); goto err_enable_irq_wake; } dev_set_drvdata(&pdev->dev, hi); /* Create sysfs to support PBA test */ audio = class_create(THIS_MODULE, "audio"); if (IS_ERR(audio)) { dev_err(&pdev->dev, "Failed to create class audio\n"); ret = -ENODEV; goto err_class_create; } earjack = device_create(audio, NULL, 0, NULL, "earjack"); if (IS_ERR(earjack)) { dev_err(&pdev->dev, "Failed to create device earjack\n"); ret = -ENODEV; goto err_device_create; } ret = device_create_file(earjack, &dev_attr_key_state); if (ret) { dev_err(&pdev->dev, "Failed to create device file in %s\n", dev_attr_key_state.attr.name); goto err_dev_attr_key_state; } ret = device_create_file(earjack, &dev_attr_state); if (ret) { dev_err(&pdev->dev, "Failed to create device file in %s\n", dev_attr_state.attr.name); goto err_dev_attr_state; } ret = device_create_file(earjack, &dev_attr_mic_adc); if (ret) { dev_err(&pdev->dev, "Failed to create device file in %s\n", dev_attr_mic_adc.attr.name); goto err_dev_attr_mic_adc; } dev_set_drvdata(earjack, hi); dev_info(&pdev->dev, "Registering sec_jack driver\n"); return 0; err_dev_attr_mic_adc: device_remove_file(earjack, &dev_attr_state); err_dev_attr_state: device_remove_file(earjack, &dev_attr_key_state); err_dev_attr_key_state: device_destroy(audio, 0); err_device_create: class_destroy(audio); err_class_create: disable_irq_wake(hi->det_irq); err_enable_irq_wake: free_irq(hi->det_irq, hi); err_request_detect_irq: input_unregister_handler(&hi->jack_handler); err_input_register_handler: destroy_workqueue(hi->buttons_wq); err_create_buttons_wq_failed: destroy_workqueue(hi->detect_wq); err_create_detect_wq_failed: wake_lock_destroy(&hi->det_wake_lock); switch_dev_unregister(&switch_sendend); err_send_end_dev_register: switch_dev_unregister(&switch_jack_detection); err_h2w_dev_register: kfree(hi); err_kzalloc: atomic_set(&instantiated, 0); return ret; } static int sec_jack_remove(struct platform_device *pdev) { struct sec_jack_info *hi = dev_get_drvdata(&pdev->dev); disable_irq_wake(hi->det_irq); free_irq(hi->det_irq, hi); destroy_workqueue(hi->detect_wq); destroy_workqueue(hi->buttons_wq); if (hi->send_key_dev) { platform_device_unregister(hi->send_key_dev); hi->send_key_dev = NULL; } input_unregister_handler(&hi->jack_handler); wake_lock_destroy(&hi->det_wake_lock); switch_dev_unregister(&switch_jack_detection); switch_dev_unregister(&switch_sendend); gpio_free(hi->pdata->det_gpio); if (hi->pdata->ear_micbias_en_gpio) gpio_free(hi->pdata->ear_micbias_en_gpio); hi->pdata->jack_pinctrl = NULL; kfree(hi); atomic_set(&instantiated, 0); return 0; } static const struct of_device_id sec_jack_dt_match[] = { { .compatible = "sec_jack" }, { } }; MODULE_DEVICE_TABLE(of, sec_jack_dt_match); static struct platform_driver sec_jack_driver = { .probe = sec_jack_probe, .remove = sec_jack_remove, .driver = { .name = "sec_jack", .owner = THIS_MODULE, .of_match_table = sec_jack_dt_match, }, }; static int __init sec_jack_init(void) { return platform_driver_register(&sec_jack_driver); } static void __exit sec_jack_exit(void) { platform_driver_unregister(&sec_jack_driver); } late_initcall(sec_jack_init); module_exit(sec_jack_exit); MODULE_DESCRIPTION("Samsung Electronics Extcon driver"); MODULE_LICENSE("GPL");