12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271 |
- /* drivers/misc/sec_jack.c
- *
- * Copyright (C) 2012 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 <linux/module.h>
- #include <linux/interrupt.h>
- #include <linux/workqueue.h>
- #include <linux/irq.h>
- #include <linux/delay.h>
- #include <linux/types.h>
- #include <linux/input.h>
- #include <linux/platform_device.h>
- #include <linux/errno.h>
- #include <linux/err.h>
- #include <linux/switch.h>
- #include <linux/input.h>
- #include <linux/timer.h>
- #include <linux/wakelock.h>
- #include <linux/slab.h>
- #include <linux/gpio.h>
- #include <linux/gpio_event.h>
- #include <linux/sec_jack.h>
- #include <linux/of_gpio.h>
- #include <linux/qpnp/qpnp-adc.h>
- #include <linux/regulator/consumer.h>
- #if defined(CONFIG_MACH_VIENNA) || defined(CONFIG_MACH_PICASSO) || defined(CONFIG_MACH_MONDRIAN) || defined(CONFIG_MACH_LT03) || defined(CONFIG_SEC_H_PROJECT) || defined(CONFIG_SEC_FRESCO_PROJECT)
- #include <linux/qpnp/pin.h>
- #endif
- #define NUM_INPUT_DEVICE_ID 2
- #define MAX_ZONE_LIMIT 10
- #ifdef CONFIG_MACH_MILLET3G_CHN_OPEN
- #define SEND_KEY_CHECK_TIME_MS 40 /* 40ms */
- #else
- #define SEND_KEY_CHECK_TIME_MS 30 /* 30ms */
- #endif
- #define DET_CHECK_TIME_MS 100 /* 100ms */
- #define DET_CHECK_TIME_MS_WITH_FSA 50 /* 50ms */
- #define WAKE_LOCK_TIME (HZ * 5) /* 5 sec */
- struct sec_jack_info {
- struct platform_device *client;
- 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 workqueue_struct *buttons_queue;
- struct timer_list timer;
- 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[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;
- };
- #if defined(CONFIG_MACH_VIENNA) || defined(CONFIG_MACH_PICASSO) || defined(CONFIG_MACH_MONDRIAN) || defined(CONFIG_MACH_LT03) || defined(CONFIG_SEC_H_PROJECT) || defined(CONFIG_SEC_FRESCO_PROJECT)
- int pm8941_mpp4;
- static struct qpnp_pin_cfg pm8941_mpp4_endis = {
- .mode = QPNP_PIN_MODE_AIN,
- .ain_route = QPNP_PIN_AIN_AMUX_CH8,
- .src_sel = QPNP_PIN_SEL_FUNC_CONSTANT, /* Function constant */
- .master_en = QPNP_PIN_MASTER_ENABLE,
- };
- #endif
- /* 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 = 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),
- };
- #ifdef CONFIG_ARCH_MSM8226
- /*Enabling Ear Mic Bias of WCD Codec*/
- extern void msm8226_enable_ear_micbias(bool state);
- #endif
- #if defined(CONFIG_MACH_KLTE_JPN)
- #define MAX_DETECT_LIMIT 3
- static void determine_jack_type(struct sec_jack_info *hi);
- static int detect_count = 0;
- #endif
- #if defined(CONFIG_SAMSUNG_JACK_READ_BTN_ADC)
- static int ear_adc_value = 0;
- #endif
- #if defined(CONFIG_MACH_VIENNA) || defined(CONFIG_MACH_PICASSO) || defined(CONFIG_MACH_MONDRIAN) || defined(CONFIG_MACH_LT03) || defined(CONFIG_SEC_H_PROJECT) || defined(CONFIG_SEC_FRESCO_PROJECT)
- static void mpp_control(bool onoff)
- {
- if(onoff) {
- pr_info("%s : mpp enable =======\n",__func__);
- pm8941_mpp4_endis.master_en = QPNP_PIN_MASTER_ENABLE;
- qpnp_pin_config(pm8941_mpp4, &pm8941_mpp4_endis);
- } else {
- pr_info("%s : mpp diable =======\n",__func__);
- pm8941_mpp4_endis.master_en = QPNP_PIN_MASTER_DISABLE;
- qpnp_pin_config(pm8941_mpp4, &pm8941_mpp4_endis);
- }
- }
- #endif
- static void sec_jack_gpio_init(struct sec_jack_platform_data *pdata)
- {
- int ret;
- #ifdef CONFIG_ARCH_MSM8226
- if(pdata->ear_micbias_gpio > 0) {
- ret = gpio_request(pdata->ear_micbias_gpio, "ear_micbias_en");
- if (ret) {
- pr_err("%s : gpio_request failed for %d\n", __func__,
- pdata->ear_micbias_gpio);
- return;
- }
- gpio_direction_output(pdata->ear_micbias_gpio, 0);
- }
- #else
- #ifdef CONFIG_MACH_HEAT_AIO
- gpio_free(pdata->ear_micbias_gpio);
- pr_err("%s HEAT_AIO: gpio_free for %d\n", __func__,
- pdata->ear_micbias_gpio);
- #endif
- if (pdata->ear_micbias_gpio > 0) {
- ret = gpio_request(pdata->ear_micbias_gpio, "ear_micbias_en");
- if (ret) {
- pr_err("%s : gpio_request failed for %d\n", __func__,
- pdata->ear_micbias_gpio);
- return;
- }
- gpio_direction_output(pdata->ear_micbias_gpio, 0);
- }
- #endif
- if (pdata->fsa_en_gpio > 0) {
- ret = gpio_request(pdata->fsa_en_gpio, "fsa_en");
- if (ret) {
- pr_err("%s : gpio_request failed for %d\n", __func__,
- pdata->fsa_en_gpio);
- return;
- }
- gpio_direction_output(pdata->fsa_en_gpio, 1);
- }
- }
- static int sec_jack_get_adc_value(struct sec_jack_info *hi)
- {
- struct qpnp_vadc_result result;
- struct sec_jack_platform_data *pdata = hi->pdata;
- int retVal;
- int rc = 0;
- uint32_t mpp_ch;
-
- /* Initialize mpp_ch default setting
- * default mpp scale is < 4 1 3 >
- */
- mpp_ch = pdata->mpp_ch_scale[0] + P_MUX1_1_3 - 1;
-
- /* To get proper mpp_ch,
- * If scaling is 1:1 then add (P_MUX1_1_1 - 1)
- * If scaling is 1:3 then add (P_MUX1_1_3 - 1)
- */
- if (pdata->mpp_ch_scale[2] == 1)
- mpp_ch = pdata->mpp_ch_scale[0] + P_MUX1_1_1 - 1;
- else if (pdata->mpp_ch_scale[2] == 3)
- mpp_ch = pdata->mpp_ch_scale[0] + P_MUX1_1_3 - 1;
- else
- pr_err("%s - invalid channel scale=%d\n", __func__, pdata->mpp_ch_scale[2]);
- qpnp_get_vadc(&hi->client->dev, "earjack-read");
- #ifdef CONFIG_ARCH_MSM8226
- // Read the MPP4 VADC channel with 1:3 scaling
- rc = qpnp_vadc_read(pdata->vadc_dev, mpp_ch, &result);
- #else
- rc = qpnp_vadc_read(NULL, mpp_ch, &result);
- #endif
- if(rc)
- pr_err("%s: qpnp_vadc_read failed: %d\n", __func__, rc);
- // Get voltage in microvolts
- retVal = ((int)result.physical)/1000;
- return retVal;
- }
- static void set_sec_micbias_state(struct sec_jack_info *hi, bool state)
- {
- struct sec_jack_platform_data *pdata = hi->pdata;
- #ifdef CONFIG_ARCH_MSM8226
- if(pdata->ear_micbias_gpio > 0)
- gpio_set_value_cansleep(pdata->ear_micbias_gpio, state); /*Uses external Mic Bias*/
- else
- msm8226_enable_ear_micbias(state); /* Uses WCD Mic Bias*/
- #else
- static struct regulator *ear_micbias_regulator = NULL;
- static int micbias_state_count = 0;
- if (pdata->ear_micbias_gpio > 0) {
- gpio_set_value_cansleep(pdata->ear_micbias_gpio, state);
- } else if (pdata->ear_micbias_ldo != NULL) {
- if (ear_micbias_regulator == NULL) {
- ear_micbias_regulator = regulator_get(NULL, pdata->ear_micbias_ldo);
- if (IS_ERR(ear_micbias_regulator)) {
- ear_micbias_regulator = NULL;
- pr_err("%s: regulator_get failed for %s\n", __func__, pdata->ear_micbias_ldo);
- return;
- }
- regulator_set_voltage(ear_micbias_regulator, 2800000, 2800000);
- }
- if (state == true && micbias_state_count == 0) {
- if (!regulator_enable(ear_micbias_regulator))
- micbias_state_count = 1;
- } else if (state == false && micbias_state_count == 1) {
- if (!regulator_disable(ear_micbias_regulator))
- micbias_state_count = 0;
- }
- }
- #endif
- }
- /* 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->buttons_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;
- int num_buttons_zones;
- /* 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->jack_buttons_zones;
- num_buttons_zones = ARRAY_SIZE(pdata->jack_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 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 < 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 defined(CONFIG_MACH_KLTE_JPN)
- if ((jack_type != SEC_HEADSET_4POLE) || (jack_type != SEC_EXTERNAL_ANTENNA))
- #else
- if (jack_type != SEC_HEADSET_4POLE)
- #endif
- set_sec_micbias_state(hi, false);
- return;
- }
- #if defined(CONFIG_MACH_KLTE_JPN)
- if ((jack_type == SEC_HEADSET_4POLE) || (jack_type == SEC_EXTERNAL_ANTENNA)) {
- #else
- if (jack_type == SEC_HEADSET_4POLE) {
- #endif
- /* 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 {
- /* 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;
- }
- /* micbias is left enabled for 4pole and disabled otherwise */
- set_sec_micbias_state(hi, false);
- }
- hi->cur_jack_type = jack_type;
- pr_info("%s : jack_type = %d\n", __func__, jack_type);
- #if defined(CONFIG_MACH_KLTE_JPN)
- if (jack_type == SEC_EXTERNAL_ANTENNA) {
- if(++detect_count > MAX_DETECT_LIMIT) {
- detect_count = 0;
- return;
- }
- else {
- int time_left_ms = 100;
-
- while (time_left_ms > 0) {
- usleep_range(10000, 10000);
- time_left_ms -= 10;
- }
- hi->cur_jack_type = SEC_JACK_NO_DEVICE;
- determine_jack_type(hi);
-
- return;
- }
- }
- else
- detect_count = 0;
- #endif
- 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);
- set_sec_micbias_state(hi, false);
- }
- 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);
- #if defined(CONFIG_MACH_VIENNA) || defined(CONFIG_MACH_PICASSO) || defined(CONFIG_MACH_MONDRIAN) || defined(CONFIG_MACH_LT03) || defined(CONFIG_SEC_H_PROJECT) || defined(CONFIG_SEC_FRESCO_PROJECT)
- mpp_control(1);
- #endif
- while (gpio_get_value(pdata->det_gpio) ^ npolarity) {
- adc = sec_jack_get_adc_value(hi);
- pr_info("%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);
- #if defined(CONFIG_MACH_VIENNA) || defined(CONFIG_MACH_PICASSO) || defined(CONFIG_MACH_MONDRIAN) || defined(CONFIG_MACH_LT03) || defined(CONFIG_SEC_H_PROJECT) || defined(CONFIG_SEC_FRESCO_PROJECT)
- mpp_control(0);
- #endif
- return;
- }
- if (zones[i].delay_us > 0)
- usleep_range(zones[i].delay_us, zones[i].delay_us);
- break;
- }
- }
- }
- #if defined(CONFIG_MACH_VIENNA) || defined(CONFIG_MACH_PICASSO) || defined(CONFIG_MACH_MONDRIAN) || defined(CONFIG_MACH_LT03) || defined(CONFIG_SEC_H_PROJECT) || defined(CONFIG_SEC_FRESCO_PROJECT)
- mpp_control(0);
- #endif
- /* jack removed before detection complete */
- pr_debug("%s : jack removed before detection complete\n", __func__);
- 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, 0664 , 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, 0664 , earjack_state_onoff_show,
- NULL);
- #if defined(CONFIG_SAMSUNG_JACK_READ_BTN_ADC)
- static ssize_t mic_adc_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- int value = 0;
- value = ear_adc_value;
- return snprintf(buf, 10, "%d\n", value);
- }
- static DEVICE_ATTR(mic_adc, 0444 , mic_adc_show, NULL);
- #endif
- #if defined (CONFIG_EARJACK_ADC_SYSFS)
- static ssize_t jack_adc_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct sec_jack_info *hi = dev_get_drvdata(dev);
- int val[4] = {0,};
- val[0] = hi->pdata->jack_zones[0].adc_high;
- val[1] = hi->pdata->jack_zones[1].adc_high;
- val[2] = hi->pdata->jack_zones[2].adc_high;
- val[3] = hi->pdata->jack_zones[3].adc_high;
- return sprintf(buf, "%d %d %d %d\n",val[0],val[1],val[2],val[3]);
- }
- static ssize_t jack_adc_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- struct sec_jack_info *hi = dev_get_drvdata(dev);
- char *buffer = (char*)buf;
- char *token;
- int val[4] = {0,};
- token = strsep(&buffer, " ");
- if(kstrtoint(token,0,&val[0]) != 0)
- return -EINVAL;
- token = strsep(&buffer, " ");
- if(kstrtoint(token,0,&val[1]) != 0)
- return -EINVAL;
- token = strsep(&buffer, " ");
- if(kstrtoint(token,0,&val[2]) != 0)
- return -EINVAL;
- token = strsep(&buffer, " ");
- if(kstrtoint(token,0,&val[3]) != 0)
- return -EINVAL;
- hi->pdata->jack_zones[0].adc_high = val[0];
- hi->pdata->jack_zones[1].adc_high = val[1];
- hi->pdata->jack_zones[2].adc_high = val[2];
- hi->pdata->jack_zones[3].adc_high = val[3];
- return count;
- }
- static DEVICE_ATTR(jack_adc, 0666, jack_adc_show,
- jack_adc_store);
- static ssize_t send_end_btn_adc_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct sec_jack_info *hi = dev_get_drvdata(dev);
- int val[2] = {0,};
- val[0] = hi->pdata->jack_buttons_zones[0].adc_low;
- val[1] = hi->pdata->jack_buttons_zones[0].adc_high;
- return sprintf(buf, "%d %d\n",val[0],val[1]);
- }
- static ssize_t send_end_btn_adc_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- struct sec_jack_info *hi = dev_get_drvdata(dev);
- char *buffer = (char*)buf;
- char *token;
- int val[2] = {0,};
- token = strsep(&buffer, " ");
- if(kstrtoint(token,0,&val[0]) != 0)
- return -EINVAL;
- token = strsep(&buffer, " ");
- if(kstrtoint(token,0,&val[1]) != 0)
- return -EINVAL;
- hi->pdata->jack_buttons_zones[0].adc_low = val[0];
- hi->pdata->jack_buttons_zones[0].adc_high = val[1];
- return count;
- }
- static DEVICE_ATTR(send_end_btn_adc, 0666, send_end_btn_adc_show,
- send_end_btn_adc_store);
- static ssize_t vol_up_btn_adc_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct sec_jack_info *hi = dev_get_drvdata(dev);
- int val[2] = {0,};
- val[0] = hi->pdata->jack_buttons_zones[1].adc_low;
- val[1] = hi->pdata->jack_buttons_zones[1].adc_high;
- return sprintf(buf, "%d %d\n",val[0],val[1]);
- }
- static ssize_t vol_up_btn_adc_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- struct sec_jack_info *hi = dev_get_drvdata(dev);
- char *buffer = (char*)buf;
- char *token;
- int val[2] = {0,};
- token = strsep(&buffer, " ");
- if(kstrtoint(token,0,&val[0]) != 0)
- return -EINVAL;
- token = strsep(&buffer, " ");
- if(kstrtoint(token,0,&val[1]) != 0)
- return -EINVAL;
- hi->pdata->jack_buttons_zones[1].adc_low = val[0];
- hi->pdata->jack_buttons_zones[1].adc_high = val[1];
- return count;
- }
- static DEVICE_ATTR(vol_up_btn_adc, 0666, vol_up_btn_adc_show,
- vol_up_btn_adc_store);
- static ssize_t vol_down_btn_adc_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct sec_jack_info *hi = dev_get_drvdata(dev);
- int val[2] = {0,};
- val[0] = hi->pdata->jack_buttons_zones[2].adc_low;
- val[1] = hi->pdata->jack_buttons_zones[2].adc_high;
- return sprintf(buf, "%d %d\n",val[0],val[1]);
- }
- static ssize_t vol_down_btn_adc_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- struct sec_jack_info *hi = dev_get_drvdata(dev);
- char *buffer = (char*)buf;
- char *token;
- int val[2] = {0,};
- token = strsep(&buffer, " ");
- if(kstrtoint(token,0,&val[0]) != 0)
- return -EINVAL;
- token = strsep(&buffer, " ");
- if(kstrtoint(token,0,&val[1]) != 0)
- return -EINVAL;
- hi->pdata->jack_buttons_zones[2].adc_low = val[0];
- hi->pdata->jack_buttons_zones[2].adc_high = val[1];
- return count;
- }
- static DEVICE_ATTR(vol_down_btn_adc, 0666, vol_down_btn_adc_show,
- vol_down_btn_adc_store);
- #endif
- 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)
- {
- #if defined(CONFIG_MACH_S3VE3G_EUR)
- struct sec_jack_info *hi = dev_id;
- disable_irq_wake(hi->det_irq);
- queue_work(hi->queue, &hi->detect_work);
- mdelay(100);
- enable_irq_wake(hi->det_irq);
- pr_info("%s: Debug code for removing flooding of jack irq\n", __func__);
- return IRQ_HANDLED;
- #else
- struct sec_jack_info *hi = dev_id;
- queue_work(hi->queue, &hi->detect_work);
- return IRQ_HANDLED;
- #endif
- }
- 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->fsa_en_gpio < 0)
- time_left_ms = DET_CHECK_TIME_MS;
- else
- time_left_ms = DET_CHECK_TIME_MS_WITH_FSA;
- /* prevent suspend to allow user space to respond to switch */
- wake_lock_timeout(&hi->det_wake_lock, WAKE_LOCK_TIME);
- pr_info("%s: detect_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, 10000);
- 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 defined(CONFIG_SAMSUNG_JACK_READ_BTN_ADC)
- ear_adc_value = 0;
- #endif
- if (!hi->buttons_enable) {
- pr_info("%s: BTN %d is skipped\n", __func__,
- 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);
- pr_info("%s: BTN %d is released\n", __func__,
- hi->pressed_code);
- return;
- }
- #if defined(CONFIG_MACH_VIENNA) || defined(CONFIG_MACH_PICASSO) || defined(CONFIG_MACH_MONDRIAN) || defined(CONFIG_MACH_LT03) || defined(CONFIG_SEC_H_PROJECT) || defined(CONFIG_SEC_FRESCO_PROJECT)
- mpp_control(1);
- #endif
- /* when button is pressed */
- adc = sec_jack_get_adc_value(hi);
- #if defined(CONFIG_SAMSUNG_JACK_READ_BTN_ADC)
- ear_adc_value = adc;
- #endif
- #if defined(CONFIG_MACH_VIENNA) || defined(CONFIG_MACH_PICASSO) || defined(CONFIG_MACH_MONDRIAN) || defined(CONFIG_MACH_LT03) || defined(CONFIG_SEC_H_PROJECT) || defined(CONFIG_SEC_FRESCO_PROJECT)
- mpp_control(0);
- #endif
- #if defined(CONFIG_MACH_KLTE_JPN)
- if (hi->cur_jack_type == SEC_EXTERNAL_ANTENNA) {
-
- int time_left_ms = 1000;
-
- while (time_left_ms > 0) {
- usleep_range(10000, 10000);
- time_left_ms -= 10;
- }
- hi->cur_jack_type = SEC_JACK_NO_DEVICE;
- determine_jack_type(hi);
- return;
- }
- #endif
- 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);
- pr_info("%s: adc = %d, BTN %d is pressed\n", __func__,
- adc, btn_zones[i].code);
- return;
- }
- pr_warn("%s: key is skipped. ADC value is %d\n", __func__, adc);
- }
- 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;
- int ret = 0;
- pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- pr_err("%s : could not allocate memory for platform data\n", __func__);
- goto alloc_err;
- }
- pdata->det_gpio = of_get_named_gpio(dev->of_node, "qcom,earjack-detect-gpio", 0);
- if (pdata->det_gpio < 0) {
- pr_err("%s : can not find the earjack-detect-gpio in the dt\n", __func__);
- } else
- pr_info("%s : earjack-detect-gpio =%d\n", __func__, pdata->det_gpio);
- pdata->send_end_gpio = of_get_named_gpio(dev->of_node, "qcom,earjack-sendend-gpio", 0);
- if (pdata->send_end_gpio < 0) {
- pr_err("%s : can not find the earjack-detect-gpio in the dt\n", __func__);
- } else
- pr_info("%s : earjack-sendend-gpio =%d\n", __func__, pdata->send_end_gpio);
- pdata->ear_micbias_gpio = of_get_named_gpio(dev->of_node, "qcom,earjack-micbias-gpio", 0);
- if (pdata->ear_micbias_gpio < 0)
- of_property_read_u32(dev->of_node, "qcom,earjack-micbias-expander-gpio", &pdata->ear_micbias_gpio);
- if (pdata->ear_micbias_gpio < 0) {
- pr_err("%s : can not find the earjack-micbias-gpio in the dt\n", __func__);
- if (of_property_read_string(dev->of_node, "qcom,earjack-micbias-ldo", &pdata->ear_micbias_ldo) < 0)
- pr_err("%s: can not find earjack-micbias-ldo in the dt\n", __func__);
- else
- pr_info("%s : earjack-micbias-ldo=%s\n", __func__, pdata->ear_micbias_ldo);
- } else
- pr_info("%s : earjack-micbias-gpio =%d\n", __func__, pdata->ear_micbias_gpio);
- pdata->fsa_en_gpio = of_get_named_gpio(dev->of_node, "qcom,earjack-fsa_en-gpio", 0);
- if (pdata->fsa_en_gpio < 0)
- of_property_read_u32(dev->of_node, "qcom,earjack-fsa_en-expander-gpio", &pdata->fsa_en_gpio);
- if (pdata->fsa_en_gpio < 0)
- pr_info("%s : No support FSA8038 chip\n", __func__);
- else
- pr_info("%s : earjack-fsa_en-gpio =%d\n", __func__, pdata->fsa_en_gpio);
-
- 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];
- #if defined(CONFIG_MACH_KLTE_JPN)
- pdata->jack_zones[i].jack_type = args.args[3];
- #else
- pdata->jack_zones[i].jack_type = args.args[3]==0?SEC_HEADSET_3POLE:SEC_HEADSET_4POLE;
- #endif
- pr_info("%s : %d, %d, %d, %d, %d \n",
- __func__, args.args_count, args.args[0],
- args.args[1], args.args[2],args.args[3]);
- }
- #if defined(CONFIG_SAMSUNG_JACK_VOICE_BTN)
- for( i=0; i<4; i++)
- #else
- for( i=0; i<3; i++)
- #endif
- {
- of_parse_phandle_with_args(dev->of_node, "but-zones-list","#list-but-cells", i, &args);
- #if defined(CONFIG_SAMSUNG_JACK_VOICE_BTN)
- pdata->jack_buttons_zones[i].code = args.args[0]==0?KEY_MEDIA:args.args[0]==1?KEY_VOLUMEUP:args.args[0]==2?KEY_VOLUMEDOWN:KEY_VOICECOMMAND;
- #else
- pdata->jack_buttons_zones[i].code = args.args[0]==0?KEY_MEDIA:args.args[0]==1?KEY_VOLUMEUP:KEY_VOLUMEDOWN;
- #endif
- pdata->jack_buttons_zones[i].adc_low = args.args[1];
- pdata->jack_buttons_zones[i].adc_high = args.args[2];
- pr_info("%s : %d, %d, %d, %d\n",
- __func__, args.args_count, args.args[0],
- args.args[1], args.args[2]);
- }
- if (of_find_property(dev->of_node, "qcom,send-end-active-high", NULL))
- pdata->send_end_active_high = true;
-
- ret = of_property_read_u32_array(dev->of_node, "mpp-channel-scaling", pdata->mpp_ch_scale, 3);
- if (ret < 0) {
- pr_err("%s : cannot find mpp-channel-scaling in the dt - using default MPP6_1_3\n",
- __func__);
- pdata->mpp_ch_scale[0] = 6;
- pdata->mpp_ch_scale[1] = 1;
- pdata->mpp_ch_scale[2] = 3;
- }
- pr_info("%s - mpp-channel-scaling - %d %d %d\n", __func__, pdata->mpp_ch_scale[0], pdata->mpp_ch_scale[1], pdata->mpp_ch_scale[2]);
- #if defined(CONFIG_MACH_VIENNA) || defined(CONFIG_MACH_PICASSO) || defined(CONFIG_MACH_MONDRIAN) || defined(CONFIG_MACH_LT03) || defined(CONFIG_SEC_H_PROJECT) || defined(CONFIG_SEC_FRESCO_PROJECT)
- pm8941_mpp4 = of_get_named_gpio(dev->of_node, "pm8941-mpp4", 0);
- #endif
- #ifdef CONFIG_ARCH_MSM8226
- /*Reading vadc ptr from dtsi File*/
- pdata->vadc_dev = qpnp_get_vadc(dev, "earjack");
- if (IS_ERR(pdata->vadc_dev)) {
- int rc;
- rc = PTR_ERR(pdata->vadc_dev);
- if (rc != -EPROBE_DEFER)
- pr_err("vadc property missing, rc=%d\n", rc);
- goto alloc_err;
- }
- #endif
- return pdata;
- alloc_err:
- devm_kfree(dev, pdata);
- return NULL;
- }
- #ifdef CONFIG_ARCH_MSM8226
- extern bool is_codec_probe_done(void);
- #endif
- 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;
- #ifdef CONFIG_ARCH_MSM8226
- if(!is_codec_probe_done()) {
- pr_err("%s - defer as codec_probe_done is false\n", __func__);
- return -EPROBE_DEFER;
- }
- #endif
- pr_info("%s : Registering jack driver\n", __func__);
- pdata = sec_jack_populate_dt_pdata(&pdev->dev);
- if (!pdata) {
- pr_err("%s : pdata is NULL.\n", __func__);
- return -ENODEV;
- } else
- sec_jack_gpio_init(pdata);
- 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;
- /* to read the vadc */
- hi->client = pdev;
-
- #ifdef CONFIG_ARCH_MSM8226
- if(pdata->det_gpio > 0) {
- 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;
- }
- }
- #else
- 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;
- }
- #endif
- 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;
- }
- ret = switch_dev_register(&switch_sendend);
- 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");
- audio = class_create(THIS_MODULE, "audio");
- if (IS_ERR(audio))
- pr_err("Failed to create class(audio)!\n");
- earjack = device_create(audio, NULL, 0, NULL, "earjack");
- if (IS_ERR(earjack))
- pr_err("Failed to create device(earjack)!\n");
- ret = device_create_file(earjack, &dev_attr_key_state);
- if (ret)
- pr_err("Failed to create device file in sysfs entries(%s)!\n",
- dev_attr_key_state.attr.name);
- ret = device_create_file(earjack, &dev_attr_state);
- if (ret)
- pr_err("Failed to create device file in sysfs entries(%s)!\n",
- dev_attr_state.attr.name);
- #if defined(CONFIG_SAMSUNG_JACK_READ_BTN_ADC)
- ret = device_create_file(earjack, &dev_attr_mic_adc);
- if (ret)
- pr_err("Failed to create device file in sysfs entries(%s)!\n",
- dev_attr_key_state.attr.name);
- #endif
- #if defined (CONFIG_EARJACK_ADC_SYSFS)
- ret = device_create_file(earjack, &dev_attr_jack_adc);
- if (ret)
- pr_err("Failed to create device file in sysfs entries(%s)!\n",
- dev_attr_jack_adc.attr.name);
- ret = device_create_file(earjack, &dev_attr_send_end_btn_adc);
- if (ret)
- pr_err("Failed to create device file in sysfs entries(%s)!\n",
- dev_attr_send_end_btn_adc.attr.name);
- ret = device_create_file(earjack, &dev_attr_vol_up_btn_adc);
- if (ret)
- pr_err("Failed to create device file in sysfs entries(%s)!\n",
- dev_attr_vol_up_btn_adc.attr.name);
- ret = device_create_file(earjack, &dev_attr_vol_down_btn_adc);
- if (ret)
- pr_err("Failed to create device file in sysfs entries(%s)!\n",
- dev_attr_vol_down_btn_adc.attr.name);
- #endif
- setup_timer(&hi->timer, sec_jack_timer_handler, (unsigned long)hi);
- INIT_WORK(&hi->buttons_work, sec_jack_buttons_work);
- INIT_WORK(&hi->detect_work, sec_jack_detect_work);
- hi->queue = create_singlethread_workqueue("sec_jack_wq");
- if (hi->queue == NULL) {
- ret = -ENOMEM;
- pr_err("%s: Failed to create workqueue\n", __func__);
- goto err_create_wq_failed;
- }
- hi->buttons_queue = create_singlethread_workqueue("sec_jack_buttons_wq");
- if (hi->buttons_queue == NULL) {
- ret = -ENOMEM;
- pr_err("%s: Failed to create buttons workqueue\n", __func__);
- goto err_create_buttons_wq_failed;
- }
- queue_work(hi->queue, &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->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;
- }
- /* We are going to remove this code later */
- if (pdata->send_end_active_high == true)
- sec_jack_key_info.flags = 1;
- 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);
- dev_set_drvdata(earjack, hi);
- #if defined(CONFIG_MACH_VIENNA) || defined(CONFIG_MACH_PICASSO) || defined(CONFIG_MACH_MONDRIAN) || defined(CONFIG_MACH_LT03) || defined(CONFIG_SEC_H_PROJECT) || defined(CONFIG_SEC_FRESCO_PROJECT)
- mpp_control(0);
- #endif
- 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->buttons_queue);
- err_create_buttons_wq_failed:
- destroy_workqueue(hi->queue);
- err_create_wq_failed:
- device_remove_file(earjack, &dev_attr_state);
- device_remove_file(earjack, &dev_attr_key_state);
- #if defined(CONFIG_SAMSUNG_JACK_READ_BTN_ADC)
- device_remove_file(earjack, &dev_attr_mic_adc);
- #endif
- #if defined (CONFIG_EARJACK_ADC_SYSFS)
- device_remove_file(earjack, &dev_attr_jack_adc);
- device_remove_file(earjack, &dev_attr_send_end_btn_adc);
- device_remove_file(earjack, &dev_attr_vol_up_btn_adc);
- device_remove_file(earjack, &dev_attr_vol_down_btn_adc);
- #endif
- device_destroy(audio, 0);
- class_destroy(audio);
- wake_lock_destroy(&hi->det_wake_lock);
- switch_dev_unregister(&switch_jack_detection);
- switch_dev_unregister(&switch_sendend);
- 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);
- destroy_workqueue(hi->buttons_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);
- switch_dev_unregister(&switch_sendend);
- gpio_free(hi->pdata->det_gpio);
- 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,
- },
- };
- #if defined(CONFIG_MACH_KLTE_KOR) || defined(CONFIG_MACH_KLTE_JPN)
- extern unsigned int system_rev;
- #endif
- static int __init sec_jack_init(void)
- {
- #if defined(CONFIG_MACH_KLTE_KOR)
- if (system_rev >= 13) {
- pr_info("%s: Do not use sec jack in system_rev %d",
- __func__, system_rev);
- return 0;
- } else {
- return platform_driver_register(&sec_jack_driver);
- }
- #elif defined(CONFIG_MACH_KLTE_JPN)
- if (system_rev >= 11) {
- pr_info("%s: Do not use sec jack in system_rev %d",
- __func__, system_rev);
- return 0;
- } else {
- return platform_driver_register(&sec_jack_driver);
- }
- #else
- return platform_driver_register(&sec_jack_driver);
- #endif
- }
- static void __exit sec_jack_exit(void)
- {
- platform_driver_unregister(&sec_jack_driver);
- }
- late_initcall(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");
|