/* drivers/misc/sec_jack.c * * Copyright (C) 2010 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 #define MAX_ZONE_LIMIT 10 #define SEND_KEY_CHECK_TIME_MS 30 /* 30ms */ #define DET_CHECK_TIME_MS 200 /* 200ms */ #define WAKE_LOCK_TIME (HZ * 5) /* 5 sec */ struct sec_jack_info { struct sec_jack_platform_data *pdata; struct delayed_work jack_detect_work; struct work_struct buttons_work; struct work_struct detect_work; struct workqueue_struct *queue; struct input_dev *input_dev; struct wake_lock det_wake_lock; struct sec_jack_zone *zone; struct input_handler handler; struct input_handle handle; struct input_device_id ids; int det_irq; int dev_id; int pressed; int pressed_code; struct platform_device *send_key_dev; unsigned int cur_jack_type; }; /* 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", }; 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 = SEND_KEY_CHECK_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), }; /* 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 *handle, unsigned int type, unsigned int code, int value) { struct sec_jack_info *hi = 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->queue, &hi->buttons_work); return true; } static int sec_jack_buttons_connect(struct input_handler *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; /* bind input_handler to input device related to only sec_jack */ if (dev->name != sec_jack_input_data.name) return -ENODEV; hi = handler->private; pdata = hi->pdata; btn_zones = pdata->buttons_zones; hi->input_dev = dev; hi->handle.dev = dev; hi->handle.handler = handler; hi->handle.open = 0; hi->handle.name = "sec_jack_buttons"; err = input_register_handle(&hi->handle); if (err) { pr_err("%s: Failed to register sec_jack buttons handle, " "error %d\n", __func__, err); goto err_register_handle; } err = input_open_device(&hi->handle); if (err) { pr_err("%s: Failed to open input device, error %d\n", __func__, err); goto err_open_device; } for (i = 0; i < pdata->num_buttons_zones; i++) input_set_capability(dev, EV_KEY, btn_zones[i].code); return 0; err_open_device: input_unregister_handle(&hi->handle); err_register_handle: return err; } static void sec_jack_buttons_disconnect(struct input_handle *handle) { input_close_device(handle); input_unregister_handle(handle); } static void sec_jack_set_type(struct sec_jack_info *hi, int jack_type) { struct sec_jack_platform_data *pdata = hi->pdata; /* 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) pdata->set_micbias_state(false); return; } if (jack_type == SEC_HEADSET_4POLE) { /* 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)); } else { /* 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; } /* micbias is left enabled for 4pole and disabled otherwise */ pdata->set_micbias_state(false); } hi->cur_jack_type = jack_type; pr_info("%s : jack_type = %d\n", __func__, jack_type); /* prevent suspend to allow user space to respond to switch */ wake_lock_timeout(&hi->det_wake_lock, WAKE_LOCK_TIME); switch_set_state(&switch_jack_detection, jack_type); } static void handle_jack_not_inserted(struct sec_jack_info *hi) { sec_jack_set_type(hi, SEC_JACK_NO_DEVICE); hi->pdata->set_micbias_state(false); } static void determine_jack_type(struct sec_jack_info *hi) { struct sec_jack_zone *zones = hi->pdata->zones; int size = hi->pdata->num_zones; int count[MAX_ZONE_LIMIT] = {0}; int adc; int i; unsigned npolarity = !hi->pdata->det_active_high; while (gpio_get_value(hi->pdata->det_gpio) ^ npolarity) { adc = hi->pdata->get_adc_value(); pr_debug("%s: adc = %d\n", __func__, adc); /* 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; } msleep(zones[i].delay_ms); break; } } } /* jack removed before detection complete */ pr_debug("%s : jack removed before detection complete\n", __func__); handle_jack_not_inserted(hi); } /* 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->queue, &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; int time_left_ms = DET_CHECK_TIME_MS; unsigned npolarity = !hi->pdata->det_active_high; /* set mic bias to enable adc */ pdata->set_micbias_state(true); /* 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; } msleep(10); 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->buttons_zones; int adc; int i; /* when button is released */ if (hi->pressed == 0) { input_report_key(hi->input_dev, hi->pressed_code, 0); input_sync(hi->input_dev); pr_debug("%s: keycode=%d, is released\n", __func__, hi->pressed_code); return; } /* when button is pressed */ adc = pdata->get_adc_value(); for (i = 0; i < pdata->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); pr_debug("%s: keycode=%d, is pressed\n", __func__, btn_zones[i].code); return; } pr_warn("%s: key is skipped. ADC value is %d\n", __func__, adc); } static int sec_jack_probe(struct platform_device *pdev) { struct sec_jack_info *hi; struct sec_jack_platform_data *pdata = pdev->dev.platform_data; int ret; pr_info("%s : Registering jack driver\n", __func__); if (!pdata) { pr_err("%s : pdata is NULL.\n", __func__); return -ENODEV; } if (!pdata->get_adc_value || !pdata->zones || !pdata->set_micbias_state || pdata->num_zones > MAX_ZONE_LIMIT) { pr_err("%s : need to check pdata\n", __func__); return -ENODEV; } if (atomic_xchg(&instantiated, 1)) { pr_err("%s : already instantiated, can only have one\n", __func__); return -ENODEV; } sec_jack_key_map[0].gpio = pdata->send_end_gpio; hi = kzalloc(sizeof(struct sec_jack_info), GFP_KERNEL); if (hi == NULL) { pr_err("%s : Failed to allocate memory.\n", __func__); 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; ret = gpio_request(pdata->det_gpio, "ear_jack_detect"); if (ret) { pr_err("%s : gpio_request failed for %d\n", __func__, pdata->det_gpio); goto err_gpio_request; } ret = switch_dev_register(&switch_jack_detection); if (ret < 0) { pr_err("%s : Failed to register switch device\n", __func__); goto err_switch_dev_register; } wake_lock_init(&hi->det_wake_lock, WAKE_LOCK_SUSPEND, "sec_jack_det"); INIT_WORK(&hi->buttons_work, sec_jack_buttons_work); INIT_WORK(&hi->detect_work, sec_jack_detect_work); hi->queue = create_freezable_workqueue("sec_jack_wq"); if (hi->queue == NULL) { ret = -ENOMEM; pr_err("%s: Failed to create workqueue\n", __func__); goto err_create_wq_failed; } queue_work(hi->queue, &hi->detect_work); hi->det_irq = gpio_to_irq(pdata->det_gpio); set_bit(EV_KEY, hi->ids.evbit); hi->ids.flags = INPUT_DEVICE_ID_MATCH_EVBIT; hi->handler.filter = sec_jack_buttons_filter; hi->handler.connect = sec_jack_buttons_connect; hi->handler.disconnect = sec_jack_buttons_disconnect; hi->handler.name = "sec_jack_buttons"; hi->handler.id_table = &hi->ids; hi->handler.private = hi; ret = input_register_handler(&hi->handler); if (ret) { pr_err("%s : Failed to register_handler\n", __func__); goto err_register_input_handler; } ret = request_irq(hi->det_irq, sec_jack_detect_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "sec_headset_detect", hi); if (ret) { pr_err("%s : Failed to request_irq.\n", __func__); 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) { pr_err("%s : Failed to enable_irq_wake.\n", __func__); goto err_enable_irq_wake; } dev_set_drvdata(&pdev->dev, hi); return 0; err_enable_irq_wake: free_irq(hi->det_irq, hi); err_request_detect_irq: input_unregister_handler(&hi->handler); err_register_input_handler: destroy_workqueue(hi->queue); err_create_wq_failed: wake_lock_destroy(&hi->det_wake_lock); switch_dev_unregister(&switch_jack_detection); err_switch_dev_register: gpio_free(pdata->det_gpio); err_gpio_request: 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); pr_info("%s :\n", __func__); disable_irq_wake(hi->det_irq); free_irq(hi->det_irq, hi); destroy_workqueue(hi->queue); if (hi->send_key_dev) { platform_device_unregister(hi->send_key_dev); hi->send_key_dev = NULL; } input_unregister_handler(&hi->handler); wake_lock_destroy(&hi->det_wake_lock); switch_dev_unregister(&switch_jack_detection); gpio_free(hi->pdata->det_gpio); kfree(hi); atomic_set(&instantiated, 0); return 0; } static struct platform_driver sec_jack_driver = { .probe = sec_jack_probe, .remove = sec_jack_remove, .driver = { .name = "sec_jack", .owner = THIS_MODULE, }, }; 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); } module_init(sec_jack_init); module_exit(sec_jack_exit); MODULE_AUTHOR("ms17.kim@samsung.com"); MODULE_DESCRIPTION("Samsung Electronics Corp Ear-Jack detection driver"); MODULE_LICENSE("GPL");