1881 lines
58 KiB
C
Executable file
1881 lines
58 KiB
C
Executable file
/*
|
|
* sound/soc/codec/s5m3700x-usbjack.c
|
|
*
|
|
* ALSA SoC Audio Layer - Samsung Codec Driver
|
|
*
|
|
* Copyright (C) 2020 Samsung Electronics
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/io.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/regmap.h>
|
|
#include <sound/soc.h>
|
|
#include <linux/input.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <uapi/linux/input-event-codes.h>
|
|
|
|
#include "s5m3700x.h"
|
|
#include "s5m3700x-jack.h"
|
|
|
|
#if IS_ENABLED(CONFIG_USB_SW_MAX20328B)
|
|
extern max20328_switch_mode_event(enum max_function event);
|
|
#endif
|
|
|
|
static struct s5m3700x_jack *p_s5m3700x_jack;
|
|
static BLOCKING_NOTIFIER_HEAD(s5m3700x_usbjack_notifier);
|
|
struct notifier_block s5m3700x_usbjack_notifier_block;
|
|
|
|
struct s5m3700x_usbjack_notifier_struct {
|
|
struct s5m3700x_priv *s5m3700x;
|
|
};
|
|
static struct s5m3700x_usbjack_notifier_struct s5m3700x_usbjack_notifier_t;
|
|
|
|
int s5m3700x_usbjack_register_notifier(struct notifier_block *n,
|
|
struct s5m3700x_priv *s5m3700x)
|
|
{
|
|
int ret;
|
|
|
|
s5m3700x_usbjack_notifier_t.s5m3700x = s5m3700x;
|
|
ret = blocking_notifier_chain_register(&s5m3700x_usbjack_notifier, n);
|
|
if (ret < 0)
|
|
pr_err("[IRQ] %s(%d)\n", __func__, __LINE__);
|
|
return ret;
|
|
}
|
|
|
|
static void jack_wake_lock(struct wakeup_source *ws)
|
|
{
|
|
__pm_stay_awake(ws);
|
|
}
|
|
|
|
static void jack_wake_unlock(struct wakeup_source *ws)
|
|
{
|
|
__pm_relax(ws);
|
|
}
|
|
|
|
static int jack_set_wake_lock(struct s5m3700x_jack *jackdet)
|
|
{
|
|
struct wakeup_source *ws = NULL;
|
|
ws = wakeup_source_register(NULL, "s5m3700x-jack");
|
|
|
|
if (ws == NULL)
|
|
goto err;
|
|
|
|
jackdet->jack_wakeup = ws;
|
|
|
|
return 0;
|
|
err:
|
|
return -1;
|
|
}
|
|
/*
|
|
* mute_mic() - Mute mic if it is active
|
|
*
|
|
* @s5m3700x: codec information struct
|
|
* @on: mic mute is true, mic unmute is false
|
|
*
|
|
* Desc: It can mute adc depending on the parameter
|
|
*/
|
|
static void mute_mic(struct s5m3700x_priv *s5m3700x, bool on)
|
|
{
|
|
dev_info(s5m3700x->dev, "%s called, %s\n", __func__, on ? "Mute" : "Unmute");
|
|
|
|
if (on)
|
|
s5m3700x_adc_digital_mute(s5m3700x, ADC_MUTE_ALL, true);
|
|
else
|
|
s5m3700x_adc_digital_mute(s5m3700x, ADC_MUTE_ALL, false);
|
|
}
|
|
|
|
void micbias_switch_unlock(struct s5m3700x_jack *jackdet, bool on)
|
|
{
|
|
struct s5m3700x_priv *s5m3700x = jackdet->p_s5m3700x;
|
|
struct device *dev = jackdet->p_s5m3700x->dev;
|
|
bool skip_ops = true;
|
|
|
|
if (on && !s5m3700x->hp_bias_cnt++) {
|
|
skip_ops = false;
|
|
} else if (!on && !--s5m3700x->hp_bias_cnt) {
|
|
skip_ops = false;
|
|
}
|
|
|
|
if (s5m3700x->hp_bias_cnt < 0) {
|
|
s5m3700x->hp_bias_cnt = 0;
|
|
dev_info(dev, "%s, micbias operation is not match\n", __func__);
|
|
}
|
|
|
|
dev_info(dev, "%s called, micbias turn %s, cnt %d\n",
|
|
__func__, on ? "On" : "Off", s5m3700x->hp_bias_cnt);
|
|
|
|
if (skip_ops)
|
|
return;
|
|
|
|
if (on) {
|
|
/* MICBIAS2 APW On */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_D0_DCTR_CM,
|
|
APW_MCB2_MASK, APW_MCB2_MASK);
|
|
msleep(50);
|
|
} else {
|
|
/* MICBIAS2 APW Off */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_D0_DCTR_CM,
|
|
APW_MCB2_MASK, 0);
|
|
}
|
|
}
|
|
|
|
void s5m3700x_usb_micbias(bool on)
|
|
{
|
|
struct s5m3700x_jack *jackdet = NULL;
|
|
struct s5m3700x_priv *s5m3700x = NULL;
|
|
|
|
if (p_s5m3700x_jack != NULL) {
|
|
jackdet = p_s5m3700x_jack;
|
|
s5m3700x = jackdet->p_s5m3700x;
|
|
|
|
dev_info(s5m3700x->dev, "%s called. Mic Bias: %s\n", __func__, on ? "On" : "Off");
|
|
|
|
mutex_lock(&jackdet->usb_mb_lock);
|
|
regcache_cache_switch(s5m3700x, false);
|
|
|
|
micbias_switch_unlock(jackdet, on);
|
|
|
|
regcache_cache_switch(s5m3700x, true);
|
|
mutex_unlock(&jackdet->usb_mb_lock);
|
|
}
|
|
}
|
|
|
|
static int read_manual_adc(struct s5m3700x_jack *jackdet)
|
|
{
|
|
struct s5m3700x_priv *s5m3700x = jackdet->p_s5m3700x;
|
|
u8 v1, v2;
|
|
unsigned int value;
|
|
|
|
mutex_lock(&jackdet->adc_lock);
|
|
|
|
/* Enable the Testmode */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FE_DCTR_GP1,
|
|
T_CNT_ADC_DIV_MASK, T_CNT_ADC_DIV_MASK);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FF_DCTR_GP2,
|
|
T_ADC_STARTTIME_MASK, T_ADC_STARTTIME_MASK);
|
|
/* ADC Divide Count Test Mode in Pole */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FE_DCTR_GP1,
|
|
CNT_ADC_DIV_MASK, jackdet->t_pole_cnt << CNT_ADC_DIV_SHIFT);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FF_DCTR_GP2,
|
|
ADC_STARTTIME_MASK, jackdet->t_pole_delay << ADC_STARTTIME_SHIFT);
|
|
|
|
/* ADC Start */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FD_ACTR_GP, 0x8C);
|
|
s5m3700x_usleep(500);
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FD_ACTR_GP, 0xCC);
|
|
|
|
msleep(80);
|
|
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_FA_STATUS11, &v1);
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_FB_STATUS12, &v2);
|
|
|
|
v1 = v1 & 0xF0;
|
|
value = (v1 << 4) + v2;
|
|
|
|
/* ADC STOP */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FD_ACTR_GP, 0x8C);
|
|
s5m3700x_usleep(500);
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FD_ACTR_GP, 0x0C);
|
|
|
|
/* Disable the Testmode */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FE_DCTR_GP1,
|
|
T_CNT_ADC_DIV_MASK, 0);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FF_DCTR_GP2,
|
|
T_ADC_STARTTIME_MASK, 0);
|
|
|
|
mutex_unlock(&jackdet->adc_lock);
|
|
dev_info(s5m3700x->dev, "%s called. Value: %d\n", __func__, value);
|
|
|
|
return value;
|
|
}
|
|
|
|
static int read_manual_adc_switch(struct s5m3700x_jack *jackdet)
|
|
{
|
|
struct s5m3700x_priv *s5m3700x = jackdet->p_s5m3700x;
|
|
unsigned int value = 0;
|
|
|
|
regcache_cache_switch(s5m3700x, false);
|
|
value = read_manual_adc(jackdet);
|
|
regcache_cache_switch(s5m3700x, true);
|
|
|
|
return value;
|
|
}
|
|
|
|
static int read_adc(struct s5m3700x_jack *jackdet, int type, int pin, int repeat)
|
|
{
|
|
struct s5m3700x_priv *s5m3700x = jackdet->p_s5m3700x;
|
|
u8 v1, v2;
|
|
unsigned int value = 0, i;
|
|
|
|
mutex_lock(&jackdet->adc_lock);
|
|
|
|
switch (pin) {
|
|
case JACK_LDET_ADC:
|
|
regcache_cache_switch(s5m3700x, false);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E4_DCTR_FSM5,
|
|
GPMUX_POLE_MASK, POLE_HPL_DET << GPMUX_POLE_SHIFT);
|
|
regcache_cache_switch(s5m3700x, true);
|
|
break;
|
|
case JACK_MDET_ADC:
|
|
regcache_cache_switch(s5m3700x, false);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E4_DCTR_FSM5,
|
|
GPMUX_POLE_MASK, POLE_MIC_DET << GPMUX_POLE_SHIFT);
|
|
regcache_cache_switch(s5m3700x, true);
|
|
break;
|
|
case JACK_IMP_ADC:
|
|
regcache_cache_switch(s5m3700x, false);
|
|
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E7_DCTR_IMP1,
|
|
CNT_IMP_OPT1_MASK, 0);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E9_DCTR_IMP3,
|
|
CNT_IMP_OPT5_MASK, 1 << CNT_IMP_OPT5_SHIFT);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_EA_DCTR_IMP4,
|
|
CNT_IMP_OPT7_MASK, 0);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_EB_DCTR_IMP5,
|
|
CNT_IMP_OPT8_MASK, 0);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_EC_DCTR_IMP6,
|
|
CNT_IMP_OPT10_MASK | CNT_IMP_OPT11_MASK, 0);
|
|
|
|
/* HP PULLDN */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_ANALOG_ADDR, S5M3700X_13A_POP_HP,
|
|
EN_HP_PDN_MASK, 0);
|
|
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FD_ACTR_GP, 0x04);
|
|
/* TEST Mode Enable */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_EC_DCTR_IMP6,
|
|
T_IMP_SEQ_MASK, T_IMP_SEQ_MASK);
|
|
regcache_cache_switch(s5m3700x, true);
|
|
|
|
/* It need for measuring impedance */
|
|
msleep(400);
|
|
break;
|
|
}
|
|
|
|
switch (type) {
|
|
case ADC_POLE:
|
|
regcache_cache_switch(s5m3700x, false);
|
|
for (i = 0; i < repeat; i++) {
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E5_DCTR_FSM6,
|
|
AP_REREAD_POLE_MASK, AP_REREAD_POLE_MASK);
|
|
s5m3700x_usleep(100);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E5_DCTR_FSM6,
|
|
AP_REREAD_POLE_MASK, 0);
|
|
jackdet->usbjack_re_read = 1;
|
|
}
|
|
regcache_cache_switch(s5m3700x, true);
|
|
|
|
msleep(30);
|
|
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_F4_STATUS5, &v1);
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_F7_STATUS8, &v2);
|
|
|
|
v1 = v1 & 0x0F;
|
|
value = (v1 << 8) + v2;
|
|
break;
|
|
case ADC_BTN:
|
|
regcache_cache_switch(s5m3700x, false);
|
|
for (i = 0; i < repeat; i++) {
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E5_DCTR_FSM6,
|
|
AP_REREAD_BTN_MASK, AP_REREAD_BTN_MASK);
|
|
s5m3700x_usleep(100);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E5_DCTR_FSM6,
|
|
AP_REREAD_BTN_MASK, 0);
|
|
}
|
|
regcache_cache_switch(s5m3700x, true);
|
|
msleep(10);
|
|
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_F5_STATUS6, &v1);
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_F8_STATUS9, &v2);
|
|
|
|
v1 = v1 & 0xF0;
|
|
value = (v1 << 4) + v2;
|
|
break;
|
|
case ADC_IMP:
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_F5_STATUS6, &v1);
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_F9_STATUS10, &v2);
|
|
|
|
v1 = v1 & 0x0F;
|
|
value = (v1 << 8) + v2;
|
|
|
|
regcache_cache_switch(s5m3700x, false);
|
|
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FD_ACTR_GP, 0x0C);
|
|
|
|
/* HP PULLDN */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_ANALOG_ADDR, S5M3700X_13A_POP_HP,
|
|
EN_HP_PDN_MASK, EN_HP_PDN_MASK);
|
|
regcache_cache_switch(s5m3700x, true);
|
|
break;
|
|
}
|
|
|
|
mutex_unlock(&jackdet->adc_lock);
|
|
dev_info(s5m3700x->dev, "%s called. ADC Pin: %d, Value: %d\n",
|
|
__func__, pin, value);
|
|
return value;
|
|
}
|
|
|
|
static void s5m3700x_read_imp_otp(struct s5m3700x_jack *jackdet)
|
|
{
|
|
struct s5m3700x_priv *s5m3700x = jackdet->p_s5m3700x;
|
|
bool msb;
|
|
u8 jack_otp[4];
|
|
|
|
/* Jack OTP Setting */
|
|
s5m3700x_i2c_read_reg(S5M3700X_OTP_ADDR, S5M3700X_2D2_JACK_OTP02, &jack_otp[0]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_OTP_ADDR, S5M3700X_2D3_JACK_OTP03, &jack_otp[1]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_OTP_ADDR, S5M3700X_2D4_JACK_OTP04, &jack_otp[2]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_OTP_ADDR, S5M3700X_2D5_JACK_OTP05, &jack_otp[3]);
|
|
|
|
jackdet->imp_p[0] = jack_otp[1];
|
|
jackdet->imp_p[1] = ((jack_otp[0] & 0x70) << 4) + jack_otp[2];
|
|
jackdet->imp_p[2] = ((jack_otp[0] & 0x0C) << 6) + jack_otp[3];
|
|
msb = jackdet->imp_p[2] & 0x200;
|
|
|
|
if (msb)
|
|
jackdet->imp_p[2] = -(0x400 - jackdet->imp_p[2]);
|
|
|
|
dev_info(s5m3700x->dev, "%s called. p1: %d, p2: %d, p3: %d\n",
|
|
__func__, jackdet->imp_p[0],
|
|
jackdet->imp_p[1], jackdet->imp_p[2]);
|
|
}
|
|
|
|
static int calculate_imp(struct s5m3700x_jack *jackdet, unsigned int d_out)
|
|
{
|
|
long long p1 = jackdet->imp_p[0];
|
|
long long p2 = jackdet->imp_p[1];
|
|
long long p3 = jackdet->imp_p[2];
|
|
long long c1 = 99, c2 = 79, c3 = 670;
|
|
long long temp1, temp2, temp3, result;
|
|
|
|
if ((p1 == 0) || (p2 == 0) || (p3 == 0))
|
|
return 32;
|
|
|
|
temp1 = p1 * ((c3 * p1) + (long long)1024) *
|
|
((c2 * (long long)d_out) - (c2 * p3) - (c1 * p2) - (c2 * p2));
|
|
temp2 = p2 * ((c1 * p1) + (c2 * p1) + (c3 * p1) + (long long)1024);
|
|
temp3 = (d_out - p3) * ((c3 * p1) + (c2 * p1) + (long long)1024);
|
|
result = temp1 / ((long long)128 * (temp2 - temp3));
|
|
|
|
dev_info(jackdet->p_s5m3700x->dev,
|
|
"%s called. tp1: %lld, tp2: %lld, tp3: %lld\n",
|
|
__func__, temp1, temp2, temp3);
|
|
|
|
return result;
|
|
}
|
|
|
|
int s5m3700x_usbjack_get_imp(void)
|
|
{
|
|
struct s5m3700x_jack *jackdet = NULL;
|
|
unsigned int imp_adc = 0;
|
|
int cal = 0;
|
|
|
|
if (p_s5m3700x_jack != NULL) {
|
|
jackdet = p_s5m3700x_jack;
|
|
|
|
imp_adc = read_adc(jackdet, ADC_IMP, JACK_IMP_ADC, 0);
|
|
|
|
cal = calculate_imp(jackdet, imp_adc);
|
|
|
|
dev_info(jackdet->p_s5m3700x->dev,
|
|
"%s called. impedance: %d\n", __func__, cal);
|
|
}
|
|
|
|
return cal;
|
|
}
|
|
EXPORT_SYMBOL_GPL(s5m3700x_usbjack_get_imp);
|
|
|
|
int s5m3700x_get_usbimp(void)
|
|
{
|
|
struct s5m3700x_jack *jackdet = NULL;
|
|
struct s5m3700x_priv *s5m3700x = NULL;
|
|
int vimp = 0;
|
|
|
|
if (p_s5m3700x_jack != NULL) {
|
|
jackdet = p_s5m3700x_jack;
|
|
s5m3700x = jackdet->p_s5m3700x;
|
|
vimp = s5m3700x_usbjack_get_imp();
|
|
|
|
dev_info(jackdet->p_s5m3700x->dev,
|
|
"%s called. imp: %d, r1: %d, r2: %d, r3: %d, r4: %d, r5: %d\n",
|
|
__func__, vimp, jackdet->jack_imp_range[0],
|
|
jackdet->jack_imp_range[1], jackdet->jack_imp_range[2],
|
|
jackdet->jack_imp_range[3], jackdet->jack_imp_range[4]);
|
|
|
|
regcache_cache_switch(s5m3700x, false);
|
|
if (vimp < 10)
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_EE_ACTR_ETC1,
|
|
IMP_VAL_MASK, 2 << IMP_VAL_SHIFT);
|
|
else if (vimp >= 10 && vimp < 32)
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_EE_ACTR_ETC1,
|
|
IMP_VAL_MASK, 1 << IMP_VAL_SHIFT);
|
|
else
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_EE_ACTR_ETC1,
|
|
IMP_VAL_MASK, 0 << IMP_VAL_SHIFT);
|
|
regcache_cache_switch(s5m3700x, true);
|
|
|
|
/* Need to calibration */
|
|
if (vimp >= jackdet->jack_imp_range[0]
|
|
&& vimp < jackdet->jack_imp_range[1]) {
|
|
/* 4 ohm */
|
|
return JACK_IMP_VAL_1;
|
|
} else if (vimp >= jackdet->jack_imp_range[1]
|
|
&& vimp < jackdet->jack_imp_range[2]) {
|
|
/* 24 ohm */
|
|
return JACK_IMP_VAL_2;
|
|
} else if (vimp >= jackdet->jack_imp_range[2]
|
|
&& vimp < jackdet->jack_imp_range[3]) {
|
|
/* 40 ohm */
|
|
return JACK_IMP_VAL_3;
|
|
} else if (vimp >= jackdet->jack_imp_range[3]
|
|
&& vimp < jackdet->jack_imp_range[4]) {
|
|
/* 40 ~ 100 ohm */
|
|
return JACK_IMP_VAL_4;
|
|
} else if (vimp >= jackdet->jack_imp_range[4]) {
|
|
/* 100 ohm ~ */
|
|
return JACK_IMP_VAL_5;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
EXPORT_SYMBOL_GPL(s5m3700x_get_usbimp);
|
|
|
|
static char *s5m3700x_return_status_name(unsigned int status)
|
|
{
|
|
switch (status) {
|
|
case USB_OUT:
|
|
return "USB_OUT";
|
|
case USB_INIT:
|
|
return "USB_INIT";
|
|
case USB_JACK_CMP:
|
|
return "USB_JACK_CMP";
|
|
case USB_JACK_IMP:
|
|
return "USB_JACK_IMP";
|
|
case USB_POLE_DEC:
|
|
return "USB_POLE_DEC";
|
|
case USB_3POLE:
|
|
return "USB_3POLE";
|
|
case USB_4POLE:
|
|
return "USB_4POLE";
|
|
case USB_AUX:
|
|
return "USB_AUX";
|
|
case USB_MCHK_DONE:
|
|
return "USB_MCHK_DONE";
|
|
case USB_IN:
|
|
return "USB_IN";
|
|
case USB_INSERTING:
|
|
return "USB_INSERTING";
|
|
default:
|
|
return "No status";
|
|
}
|
|
}
|
|
|
|
static void s5m3700x_print_status_change(struct device *dev, unsigned int prv_jack, unsigned int cur_jack)
|
|
{
|
|
dev_info(dev, "Priv: %s -> Cur: %s\n",
|
|
s5m3700x_return_status_name(prv_jack), s5m3700x_return_status_name(cur_jack));
|
|
}
|
|
|
|
static bool s5m3700x_usbjackstate_register(struct s5m3700x_jack *jackdet)
|
|
{
|
|
struct s5m3700x_priv *s5m3700x = jackdet->p_s5m3700x;
|
|
struct earjack_state *jackstate = &jackdet->jack_state;
|
|
struct device *dev = jackdet->p_s5m3700x->dev;
|
|
unsigned int cur_jack, prv_jack, mic4_on;
|
|
unsigned int value1, value2;
|
|
|
|
cur_jack = jackstate->cur_jack_state;
|
|
prv_jack = jackstate->prv_jack_state;
|
|
|
|
regcache_cache_switch(s5m3700x, false);
|
|
|
|
switch (cur_jack) {
|
|
case USB_OUT:
|
|
s5m3700x_read(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_1E_CHOP1, &mic4_on);
|
|
mic4_on &= MIC4_ON_MASK;
|
|
|
|
if (mic4_on)
|
|
mute_mic(s5m3700x, true);
|
|
|
|
if (prv_jack & (USB_IN | USB_INSERTING)) {
|
|
s5m3700x_print_status_change(dev, prv_jack, cur_jack);
|
|
|
|
jackdet->switch_ic_state = 0;
|
|
jackdet->switch_jack_done = 0;
|
|
jackdet->usbjack_re_read = 0;
|
|
jackstate->prv_btn_state = BUTTON_RELEASE;
|
|
jackstate->cur_btn_state = BUTTON_RELEASE;
|
|
|
|
/* Disabled ANT */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_C0_ACTR_JD1,
|
|
PDB_ANT_MDET_MASK, 0);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_D2_DCTR_TEST2,
|
|
T_PDB_ANT_MDET_MASK, 0);
|
|
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_D6_DCTR_TEST6, 0x01);
|
|
|
|
/* Disabled TEST Mode */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FD_ACTR_GP, 0x0C);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FE_DCTR_GP1,
|
|
T_CNT_ADC_DIV_MASK, 0);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FF_DCTR_GP2,
|
|
T_ADC_STARTTIME_MASK, 0);
|
|
|
|
/* Disabled TEST Mode in IMP CAL */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_EC_DCTR_IMP6,
|
|
T_IMP_SEQ_MASK, 0);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_EE_ACTR_ETC1,
|
|
IMP_VAL_MASK, 0 << IMP_VAL_SHIFT);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_EE_ACTR_ETC1,
|
|
SEL_MDET_JO_SIG_MASK, SEL_MDET_JO_SIG_MASK);
|
|
|
|
/* Reset AP and Pole in machine */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E0_DCTR_FSM1, 0x06);
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E3_DCTR_FSM4, 0x40);
|
|
|
|
/* HP PULLDN */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_ANALOG_ADDR, S5M3700X_13A_POP_HP,
|
|
EN_HP_PDN_MASK, EN_HP_PDN_MASK);
|
|
|
|
micbias_switch_unlock(jackdet, false);
|
|
|
|
/* LDET OUT */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_D5_DCTR_TEST5, 0xC3);
|
|
} else {
|
|
goto err;
|
|
}
|
|
break;
|
|
case USB_INIT:
|
|
if (prv_jack & USB_OUT) {
|
|
s5m3700x_print_status_change(dev, prv_jack, cur_jack);
|
|
|
|
/* Enable MCB2 */
|
|
micbias_switch_unlock(jackdet, true);
|
|
|
|
#if IS_ENABLED(CONFIG_USB_SW_MAX20328B)
|
|
max20328_switch_mode_event(MAX_USBC_SWITCH_ENABLE);
|
|
#endif
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_D5_DCTR_TEST5, 0x82);
|
|
} else {
|
|
goto err;
|
|
}
|
|
break;
|
|
case USB_JACK_CMP:
|
|
if (prv_jack & (USB_INIT | USB_IN)) {
|
|
s5m3700x_print_status_change(dev, prv_jack, cur_jack);
|
|
/* Send the jack out event to the audio framework */
|
|
input_report_switch(jackdet->input, SW_HEADPHONE_INSERT, 0);
|
|
input_report_switch(jackdet->input, SW_MICROPHONE_INSERT, 0);
|
|
input_sync(jackdet->input);
|
|
|
|
/* T_JACK_STATE */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E3_DCTR_FSM4, 0x40);
|
|
|
|
/*
|
|
* Enable MCB2
|
|
* It is need code. Cause Using mdet workqueue function.
|
|
*/
|
|
micbias_switch_unlock(jackdet, true);
|
|
|
|
/* 5-Pin Setting */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E0_DCTR_FSM1, 0x06);
|
|
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_EC_DCTR_IMP6,
|
|
T_IMP_SEQ_MASK, 0);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_EE_ACTR_ETC1,
|
|
IMP_VAL_MASK, 0 << IMP_VAL_SHIFT);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_EE_ACTR_ETC1,
|
|
SEL_MDET_JO_SIG_MASK, SEL_MDET_JO_SIG_MASK);
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FD_ACTR_GP, 0x0C);
|
|
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_D6_DCTR_TEST6, 0x01);
|
|
|
|
/* ANT Enable */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_D2_DCTR_TEST2,
|
|
T_PDB_ANT_MDET_MASK, T_PDB_ANT_MDET_MASK);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_C0_ACTR_JD1,
|
|
PDB_ANT_MDET_MASK, PDB_ANT_MDET_MASK);
|
|
|
|
/* Read mdet status start */
|
|
cancel_delayed_work(&jackdet->mdet_chk_work);
|
|
queue_delayed_work(jackdet->mdet_chk_wq, &jackdet->mdet_chk_work,
|
|
msecs_to_jiffies(jackdet->mdet_chk_delay));
|
|
} else {
|
|
goto err;
|
|
}
|
|
break;
|
|
case USB_MCHK_DONE:
|
|
if (prv_jack & USB_JACK_CMP) {
|
|
s5m3700x_print_status_change(dev, prv_jack, cur_jack);
|
|
|
|
if (jackdet->switch_jack_done == 0) {
|
|
/* Reading Mic adc in manual */
|
|
value1 = read_manual_adc_switch(jackdet);
|
|
|
|
/*
|
|
* Switching MIC-GND
|
|
* LRMG => LRGM
|
|
*/
|
|
#if IS_ENABLED(CONFIG_USB_SW_MAX20328B)
|
|
max20328_switch_mode_event(MAX_MIC_GND_SWAP);
|
|
#endif
|
|
s5m3700x_usleep(5000);
|
|
value2 = read_manual_adc_switch(jackdet);
|
|
|
|
dev_info(dev, "LRMG: %d, LRGM: %d\n", value1, value2);
|
|
|
|
/*
|
|
* Switching GND-MIC
|
|
* LRGM => LRMG
|
|
*/
|
|
if (value1 > value2) {
|
|
#if IS_ENABLED(CONFIG_USB_SW_MAX20328B)
|
|
max20328_switch_mode_event(MAX_GND_MIC_SWAP);
|
|
#endif
|
|
s5m3700x_usleep(5000);
|
|
jackdet->switch_ic_state = 0;
|
|
} else
|
|
jackdet->switch_ic_state = 1;
|
|
}
|
|
|
|
/* T_JACK_STATE */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E3_DCTR_FSM4, 0x00);
|
|
} else {
|
|
goto err;
|
|
}
|
|
break;
|
|
case USB_POLE_DEC:
|
|
if (prv_jack & USB_MCHK_DONE) {
|
|
s5m3700x_print_status_change(dev, prv_jack, cur_jack);
|
|
jackdet->switch_jack_done = 1;
|
|
} else
|
|
goto err;
|
|
break;
|
|
case USB_3POLE:
|
|
if (prv_jack & USB_POLE_DEC) {
|
|
s5m3700x_print_status_change(dev, prv_jack, cur_jack);
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_D6_DCTR_TEST6, 0x81);
|
|
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_EE_ACTR_ETC1,
|
|
SEL_MDET_JO_SIG_MASK, 0);
|
|
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E3_DCTR_FSM4,
|
|
POLE_VALUE_MASK, POLE_VALUE_3POLE << POLE_VALUE_SHIFT);
|
|
} else {
|
|
goto err;
|
|
}
|
|
break;
|
|
case USB_4POLE:
|
|
if (prv_jack & USB_POLE_DEC) {
|
|
s5m3700x_print_status_change(dev, prv_jack, cur_jack);
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_D6_DCTR_TEST6, 0x21);
|
|
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E3_DCTR_FSM4,
|
|
POLE_VALUE_MASK, POLE_VALUE_4POLE << POLE_VALUE_SHIFT);
|
|
} else {
|
|
goto err;
|
|
}
|
|
break;
|
|
}
|
|
|
|
regcache_cache_switch(s5m3700x, true);
|
|
return true;
|
|
|
|
err:
|
|
regcache_cache_switch(s5m3700x, true);
|
|
dev_err(dev, "%s Jack state machine error! prv: 0x%02x, cur: 0x%02x\n",
|
|
__func__, jackstate->prv_jack_state, jackstate->cur_jack_state);
|
|
return false;
|
|
}
|
|
|
|
static void s5m3700x_usbjackstate_set(struct s5m3700x_jack *jackdet,
|
|
unsigned int change_state)
|
|
{
|
|
struct earjack_state *jackstate = &jackdet->jack_state;
|
|
struct device *dev = jackdet->p_s5m3700x->dev;
|
|
bool ret;
|
|
|
|
/* Save privious jack state */
|
|
jackstate->prv_jack_state = jackstate->cur_jack_state;
|
|
|
|
/* Update current jack state */
|
|
jackstate->cur_jack_state = change_state;
|
|
|
|
if (jackstate->prv_jack_state != jackstate->cur_jack_state) {
|
|
dev_info(dev, "%s called, Prv: 0x%02x, Cur: 0x%02x\n", __func__,
|
|
jackstate->prv_jack_state, jackstate->cur_jack_state);
|
|
|
|
/* Set jack register */
|
|
ret = s5m3700x_usbjackstate_register(jackdet);
|
|
dev_info(dev, "Jack register write %s.\n",
|
|
ret ? "complete" : "incomplete");
|
|
} else {
|
|
dev_info(dev, "Prv_jack_state and Cur_jack_state are same.\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* process_button_ev() - Process the button events
|
|
*
|
|
* @jackdet: jack information struct
|
|
* @on: button press is true, button release is false.
|
|
*
|
|
* Desc: It processes button press or release events. It gives information
|
|
* about the button state to framework layer.
|
|
*/
|
|
static void process_button_ev(struct s5m3700x_jack *jackdet, bool on)
|
|
{
|
|
struct earjack_state *jackstate = &jackdet->jack_state;
|
|
struct s5m3700x_priv *s5m3700x = jackdet->p_s5m3700x;
|
|
|
|
dev_info(s5m3700x->dev, "%s: key %d is %s, adc: %d\n",
|
|
__func__, jackstate->button_code,
|
|
on ? "pressed" : "released", jackstate->btn_adc);
|
|
|
|
input_report_key(jackdet->input, jackstate->button_code, on);
|
|
input_sync(jackdet->input);
|
|
mute_mic(s5m3700x, on);
|
|
}
|
|
|
|
/*
|
|
* before_button_error_chk() - Check button state error
|
|
*
|
|
* @jackdet: jack information struct
|
|
*
|
|
* Desc: Error check condition for button irq before current button state check.
|
|
* Return: There is not problem, return true.
|
|
*/
|
|
static bool before_button_error_chk(struct s5m3700x_jack *jackdet)
|
|
{
|
|
struct earjack_state *jackstate = &jackdet->jack_state;
|
|
struct device *dev = jackdet->p_s5m3700x->dev;
|
|
|
|
/* Terminate workqueue Cond1: jack is not detected */
|
|
if (jackstate->cur_jack_state & JACK_OUT || jackstate->cur_jack_state & USB_OUT) {
|
|
dev_err(dev, "Skip button events because jack is not detected.\n");
|
|
if (jackstate->cur_btn_state == BUTTON_PRESS) {
|
|
jackstate->prv_btn_state = BUTTON_RELEASE;
|
|
process_button_ev(jackdet, BUTTON_RELEASE);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Terminate workqueue Cond2: 3 pole earjack */
|
|
if (!((jackstate->cur_jack_state & JACK_4POLE) || (jackstate->cur_jack_state & USB_4POLE))) {
|
|
dev_err(dev, "Skip button events for 3 pole earjack.\n");
|
|
return false;
|
|
}
|
|
|
|
/* Terminate workqueue Cond3: button adc error */
|
|
if (jackstate->btn_adc == -EINVAL) {
|
|
dev_err(dev, "Button ADC Error! BTN ADC: %d\n", jackstate->btn_adc);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* after_button_error_chk() - Check button state error
|
|
*
|
|
* @jackdet: jack information struct
|
|
*
|
|
* Desc: Error check condition for button irq after current button state check.
|
|
* Return: There is not problem, return true.
|
|
*/
|
|
static bool after_button_error_chk(struct s5m3700x_jack *jackdet)
|
|
{
|
|
struct earjack_state *jackstate = &jackdet->jack_state;
|
|
struct device *dev = jackdet->p_s5m3700x->dev;
|
|
|
|
/* Terminate workqueue Cond4: 3 pole earjack with button press */
|
|
if ((jackstate->cur_btn_state == BUTTON_PRESS) &&
|
|
!((jackstate->cur_jack_state & JACK_4POLE) || (jackstate->cur_jack_state & USB_4POLE))) {
|
|
dev_err(dev, "Skip button press event for 3 pole earjack.\n");
|
|
jackstate->prv_btn_state = BUTTON_RELEASE;
|
|
process_button_ev(jackdet, BUTTON_RELEASE);
|
|
return false;
|
|
}
|
|
|
|
/* Terminate workqueue Cond5: button state not changed with button press */
|
|
if ((jackstate->cur_btn_state == BUTTON_PRESS) &&
|
|
(jackstate->prv_btn_state == jackstate->cur_btn_state)) {
|
|
dev_err(dev, "Skip button press event, button status are same.\n");
|
|
jackstate->prv_btn_state = BUTTON_RELEASE;
|
|
process_button_ev(jackdet, BUTTON_RELEASE);
|
|
return false;
|
|
}
|
|
|
|
/* Terminate workqueue Cond6: button state not changed */
|
|
if (jackstate->prv_btn_state == jackstate->cur_btn_state) {
|
|
dev_err(dev, "Button status are same.\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void reset_mic4_boost(struct s5m3700x_jack *jackdet)
|
|
{
|
|
struct s5m3700x_priv *s5m3700x = jackdet->p_s5m3700x;
|
|
|
|
regcache_cache_switch(s5m3700x, false);
|
|
|
|
/* Ear-mic BST Reset */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_AF_BST_CTR,
|
|
EN_ARESETB_BST4_MASK, EN_ARESETB_BST4_MASK);
|
|
s5m3700x_usleep(100);
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_AF_BST_CTR,
|
|
EN_ARESETB_BST4_MASK, 0);
|
|
|
|
regcache_cache_switch(s5m3700x, true);
|
|
}
|
|
|
|
/*
|
|
* s5m3700x_buttons_work() - Process button interrupt
|
|
*
|
|
* @work: delayed workqueue
|
|
*
|
|
* Desc: When button interrupt is occured, generated buttons_work process.
|
|
* Judge whether button press or release using button adc. And notify to
|
|
* framework layer.
|
|
*/
|
|
static void s5m3700x_buttons_work(struct work_struct *work)
|
|
{
|
|
struct s5m3700x_jack *jackdet =
|
|
container_of(work, struct s5m3700x_jack, buttons_work.work);
|
|
struct earjack_state *jackstate = &jackdet->jack_state;
|
|
struct device *dev = jackdet->p_s5m3700x->dev;
|
|
struct jack_buttons_zone *btn_zones = jackdet->jack_buttons_zones;
|
|
int num_buttons_zones = ARRAY_SIZE(jackdet->jack_buttons_zones);
|
|
int i;
|
|
u8 btn_state;
|
|
|
|
/* Read adc value */
|
|
jackstate->btn_adc = read_adc(jackdet, ADC_BTN, JACK_MDET_ADC, 0);
|
|
|
|
/* Check error status */
|
|
if (!before_button_error_chk(jackdet)) {
|
|
jack_wake_unlock(jackdet->jack_wakeup);
|
|
return;
|
|
}
|
|
|
|
jackstate->cur_btn_state =
|
|
jackstate->btn_adc > jackdet->btn_release_value ? BUTTON_RELEASE : BUTTON_PRESS;
|
|
|
|
if (jackstate->cur_btn_state == BUTTON_PRESS) {
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_FA_STATUS11, &btn_state);
|
|
}
|
|
|
|
/* Check button press/release */
|
|
dev_info(dev, "Button %s! adc: %d, btn_release_value: %d\n",
|
|
jackstate->btn_adc > jackdet->btn_release_value ? "Released" : "Pressed",
|
|
jackstate->btn_adc, jackdet->btn_release_value);
|
|
|
|
/* Check error status */
|
|
if (!after_button_error_chk(jackdet)) {
|
|
jack_wake_unlock(jackdet->jack_wakeup);
|
|
return;
|
|
}
|
|
|
|
/* Save button information */
|
|
jackstate->prv_btn_state = jackstate->cur_btn_state;
|
|
|
|
if (jackstate->cur_btn_state == BUTTON_PRESS) {
|
|
/* Determine which button pressed */
|
|
for (i = 0; i < num_buttons_zones; i++) {
|
|
if (jackstate->btn_adc <= btn_zones[i].adc_high) {
|
|
jackstate->button_code = btn_zones[i].code;
|
|
dev_info(dev,
|
|
"%s: adc: %d, btn code: %d, low: %d, high: %d\n",
|
|
__func__, jackstate->btn_adc, jackstate->button_code,
|
|
btn_zones[i].adc_low, btn_zones[i].adc_high);
|
|
process_button_ev(jackdet, BUTTON_PRESS);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
/* Ear-mic BST4 Reset */
|
|
reset_mic4_boost(jackdet);
|
|
|
|
/* Button released */
|
|
process_button_ev(jackdet, BUTTON_RELEASE);
|
|
}
|
|
|
|
/* Terminate workqueue Cond7: Button press and release event duplicated. */
|
|
if (jackdet->btn_state == BUTTON_PRESS_RELEASE) {
|
|
jackdet->btn_state = BUTTON_RELEASE;
|
|
|
|
dev_err(dev, "Button press and release event duplicated.\n");
|
|
|
|
jackstate->prv_btn_state = BUTTON_RELEASE;
|
|
|
|
msleep(50);
|
|
|
|
/* Ear-mic BST4 Reset */
|
|
reset_mic4_boost(jackdet);
|
|
|
|
process_button_ev(jackdet, BUTTON_RELEASE);
|
|
}
|
|
|
|
jack_wake_unlock(jackdet->jack_wakeup);
|
|
}
|
|
|
|
static void s5m3700x_mdet_chk_work(struct work_struct *work)
|
|
{
|
|
struct s5m3700x_jack *jackdet =
|
|
container_of(work, struct s5m3700x_jack, mdet_chk_work.work);
|
|
struct device *dev = jackdet->p_s5m3700x->dev;
|
|
u8 mdet_status = 0;
|
|
|
|
/* lock for jack and button irq */
|
|
jack_wake_lock(jackdet->jack_wakeup);
|
|
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_F0_STATUS1, &mdet_status);
|
|
|
|
dev_info(dev, "%s called. MDET R: %02X\n", __func__, mdet_status);
|
|
if (mdet_status & ANT_MDET_DET_MASK) {
|
|
cancel_delayed_work(&jackdet->mdet_chk_work);
|
|
s5m3700x_usbjackstate_set(jackdet, USB_MCHK_DONE);
|
|
jack_wake_unlock(jackdet->jack_wakeup);
|
|
} else {
|
|
/* lock for jack and button irq */
|
|
jack_wake_unlock(jackdet->jack_wakeup);
|
|
|
|
/* Read mdet status ongoing */
|
|
cancel_delayed_work(&jackdet->mdet_chk_work);
|
|
queue_delayed_work(jackdet->mdet_chk_wq, &jackdet->mdet_chk_work,
|
|
msecs_to_jiffies(jackdet->mdet_chk_delay));
|
|
}
|
|
}
|
|
|
|
static char *s5m3700x_print_jack_type_state(int cur_jack_state)
|
|
{
|
|
if (cur_jack_state & USB_4POLE)
|
|
return "4POLE";
|
|
else
|
|
return "3POLE";
|
|
}
|
|
|
|
static void s5m3700x_usbjack_work(struct work_struct *work)
|
|
{
|
|
struct s5m3700x_jack *jackdet =
|
|
container_of(work, struct s5m3700x_jack, usbjack_work.work);
|
|
struct earjack_state *jackstate = &jackdet->jack_state;
|
|
struct device *dev = jackdet->p_s5m3700x->dev;
|
|
int jack_state = USB_4POLE;
|
|
|
|
mutex_lock(&jackdet->mdet_lock);
|
|
dev_info(dev, "%s called, cur_jack_state: 0x%02x.\n",
|
|
__func__, jackstate->cur_jack_state);
|
|
|
|
/* Pole value decision */
|
|
if (jackstate->cur_jack_state & USB_POLE_DEC) {
|
|
/* Read mdet adc value for detect mic */
|
|
jackstate->mdet_adc = read_adc(jackdet, ADC_POLE, JACK_MDET_ADC, 1);
|
|
|
|
#if IS_ENABLED(CONFIG_USB_SW_MAX20328B)
|
|
if (jackdet->switch_ic_state == 0)
|
|
max20328_switch_mode_event(MAX_USBC_SWITCH_SBU_DIRECT_CONNECT);
|
|
else
|
|
max20328_switch_mode_event(MAX_USBC_SWITCH_SBU_FLIP_CONNECT);
|
|
#endif
|
|
|
|
if (jackstate->mdet_adc > jackdet->mic_adc_range[0])
|
|
jack_state = USB_4POLE;
|
|
else
|
|
jack_state = USB_3POLE;
|
|
|
|
/* Report jack type */
|
|
s5m3700x_usbjackstate_set(jackdet, jack_state);
|
|
if (jack_state == USB_4POLE)
|
|
input_report_switch(jackdet->input, SW_MICROPHONE_INSERT, 1);
|
|
else
|
|
input_report_switch(jackdet->input, SW_HEADPHONE_INSERT, 1);
|
|
input_sync(jackdet->input);
|
|
|
|
dev_info(dev, "%s mic adc: %d, usb type: %s\n", __func__,
|
|
jackstate->mdet_adc,
|
|
s5m3700x_print_jack_type_state(jackstate->cur_jack_state));
|
|
} else {
|
|
dev_info(dev, "%s called, current Jack is USB_OUT\n", __func__);
|
|
|
|
/* Send the jack out event to the audio framework */
|
|
input_report_switch(jackdet->input, SW_HEADPHONE_INSERT, 0);
|
|
input_report_switch(jackdet->input, SW_MICROPHONE_INSERT, 0);
|
|
input_sync(jackdet->input);
|
|
}
|
|
|
|
dev_info(dev, "%s called, USB %s, Mic %s\n", __func__,
|
|
(jackstate->cur_jack_state & USB_IN) ? "inserted" : "removed",
|
|
(jackstate->cur_jack_state & USB_4POLE) ? "inserted" : "removed");
|
|
|
|
mutex_unlock(&jackdet->mdet_lock);
|
|
jack_wake_unlock(jackdet->jack_wakeup);
|
|
}
|
|
|
|
/*
|
|
* s5m3700x_configure_mic_bias() - Configure mic bias voltage
|
|
*
|
|
* @jackdet: jack information struct
|
|
*
|
|
* Desc: Configure the mic1 and mic2 bias voltages with default value
|
|
* or the value received from the device tree.
|
|
* Also configure the internal LDO voltage.
|
|
*/
|
|
static void s5m3700x_configure_mic_bias(struct s5m3700x_jack *jackdet)
|
|
{
|
|
struct s5m3700x_priv *s5m3700x = jackdet->p_s5m3700x;
|
|
|
|
/* Configure Mic1 Bias Voltage */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_C8_ACTR_MCB4,
|
|
CTRV_MCB1_MASK, jackdet->mic_bias1_voltage << CTRV_MCB1_SHIFT);
|
|
|
|
/* Configure Mic2 Bias Voltage */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_C9_ACTR_MCB5,
|
|
CTRV_MCB2_MASK, jackdet->mic_bias2_voltage << CTRV_MCB2_SHIFT);
|
|
|
|
/* Configure Mic3 Bias Voltage */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_C9_ACTR_MCB5,
|
|
CTRV_MCB3_MASK, jackdet->mic_bias3_voltage << CTRV_MCB3_SHIFT);
|
|
}
|
|
|
|
/*
|
|
* s5m3700x_usbjack_parse_dt() - Parsing device tree options about jack
|
|
*
|
|
* @s5m3700x: codec information struct
|
|
*
|
|
* Desc: Initialize jack_det struct variable as parsing device tree options.
|
|
* Customer can tune this options. If not set by customer,
|
|
* it use default value below defined.
|
|
*/
|
|
|
|
static void s5m3700x_usbjack_parse_dt(struct s5m3700x_priv *s5m3700x)
|
|
{
|
|
struct s5m3700x_jack *jackdet = s5m3700x->p_jackdet;
|
|
struct device *dev = s5m3700x->dev;
|
|
struct of_phandle_args args;
|
|
unsigned int tc, delay, mic_range;
|
|
unsigned int bias_v_conf, btn_rel_val;
|
|
int usb_hp_chl, usb_hp_chr;
|
|
int ret, i;
|
|
|
|
jackdet->irqb_gpio = of_get_named_gpio(dev->of_node, "s5m3700x-codec-int", 0);
|
|
if (jackdet->irqb_gpio < 0)
|
|
dev_err(dev, "%s cannot find irqb gpio in the dt\n", __func__);
|
|
else
|
|
dev_info(dev, "%s: irqb gpio = %d\n",
|
|
__func__, jackdet->irqb_gpio);
|
|
|
|
/*
|
|
* Setting ADC measurement value in test mode (TC, Delay).
|
|
*/
|
|
ret = of_property_read_u32(dev->of_node, "t-pole-cnt", &tc);
|
|
if (!ret)
|
|
jackdet->t_pole_cnt = tc;
|
|
else
|
|
jackdet->t_pole_cnt = 5;
|
|
|
|
ret = of_property_read_u32(dev->of_node, "t-btn-cnt", &tc);
|
|
if (!ret)
|
|
jackdet->t_btn_cnt = tc;
|
|
else
|
|
jackdet->t_btn_cnt = 4;
|
|
|
|
ret = of_property_read_u32(dev->of_node, "t-pole-delay", &delay);
|
|
if (!ret)
|
|
jackdet->t_pole_delay = delay;
|
|
else
|
|
jackdet->t_pole_delay = 10;
|
|
|
|
ret = of_property_read_u32(dev->of_node, "t-btn-delay", &delay);
|
|
if (!ret)
|
|
jackdet->t_btn_delay = delay;
|
|
else
|
|
jackdet->t_btn_delay = 4;
|
|
|
|
dev_info(dev, "TM ADC cnt: %d %d, delay: %d %d\n",
|
|
jackdet->t_pole_cnt, jackdet->t_btn_cnt,
|
|
jackdet->t_pole_delay, jackdet->t_btn_delay);
|
|
|
|
/*
|
|
* Setting ADC detecting range.
|
|
*/
|
|
/* Mic det adc range */
|
|
ret = of_parse_phandle_with_args(dev->of_node, "mic-adc-range", "#list-imp-cells", 0, &args);
|
|
if (!ret) {
|
|
for (i = 0; i < 2; i++)
|
|
jackdet->mic_adc_range[i] = args.args[i];
|
|
} else {
|
|
jackdet->mic_adc_range[0] = S5M3700X_MIC_ADC_RANGE_0;
|
|
jackdet->mic_adc_range[1] = S5M3700X_MIC_ADC_RANGE_1;
|
|
}
|
|
|
|
/* Mdet check delay */
|
|
ret = of_property_read_u32(dev->of_node, "mdet-chk-delay", &mic_range);
|
|
if (!ret)
|
|
jackdet->mdet_chk_delay = mic_range;
|
|
else
|
|
jackdet->mdet_chk_delay = S5M3700X_MDET_CHK_DEFAULT;
|
|
|
|
/* OMTP det adc range */
|
|
ret = of_property_read_u32(dev->of_node, "omtp-adc-range", &mic_range);
|
|
if (!ret)
|
|
jackdet->omtp_range = mic_range;
|
|
else
|
|
jackdet->omtp_range = S5M3700X_OMTP_ADC_DEFAULT;
|
|
|
|
/* USB Switch adc range */
|
|
ret = of_property_read_u32(dev->of_node, "usb-switch-range", &mic_range);
|
|
if (!ret)
|
|
jackdet->usb_switch_range = mic_range;
|
|
else
|
|
jackdet->usb_switch_range = S5M3700X_USB_ADC_DEFAULT;
|
|
|
|
dev_info(dev, "mic_adc_range[0]: %d, mic_adc_range[0]: %d, omtp: %d, usb: %d, mdet chk: %d\n",
|
|
jackdet->mic_adc_range[0], jackdet->mic_adc_range[1], jackdet->omtp_range,
|
|
jackdet->usb_switch_range, jackdet->mdet_chk_delay);
|
|
|
|
/* Jack Imp adc range */
|
|
ret = of_parse_phandle_with_args(dev->of_node, "jack-imp-range", "#list-imp-cells", 0, &args);
|
|
if (!ret) {
|
|
for (i = 0; i < 5; i++)
|
|
jackdet->jack_imp_range[i] = args.args[i];
|
|
} else {
|
|
jackdet->jack_imp_range[0] = S5M3700X_JACK_IMP_RANGE_0;
|
|
jackdet->jack_imp_range[1] = S5M3700X_JACK_IMP_RANGE_1;
|
|
jackdet->jack_imp_range[2] = S5M3700X_JACK_IMP_RANGE_2;
|
|
jackdet->jack_imp_range[3] = S5M3700X_JACK_IMP_RANGE_3;
|
|
jackdet->jack_imp_range[4] = S5M3700X_JACK_IMP_RANGE_4;
|
|
}
|
|
|
|
dev_info(dev, "jack imp range: %d %d %d %d %d.\n",
|
|
jackdet->jack_imp_range[0],
|
|
jackdet->jack_imp_range[1],
|
|
jackdet->jack_imp_range[2],
|
|
jackdet->jack_imp_range[3],
|
|
jackdet->jack_imp_range[4]);
|
|
|
|
/*
|
|
* Set mic bias 1/2/3 voltages
|
|
*/
|
|
/* Mic Bias 1 */
|
|
ret = of_property_read_u32(dev->of_node, "mic-bias1-voltage", &bias_v_conf);
|
|
if (!ret &&
|
|
((bias_v_conf >= MICBIAS_VO_1_5V) &&
|
|
(bias_v_conf <= MICBIAS_VO_2_8V))) {
|
|
jackdet->mic_bias1_voltage = bias_v_conf;
|
|
} else {
|
|
jackdet->mic_bias1_voltage = MICBIAS_VO_2_8V;
|
|
dev_warn(dev, "Property 'mic-bias1-voltage' %s",
|
|
ret ? "not found, default set 2.8V" : "used invalid value");
|
|
}
|
|
|
|
/* Mic Bias 2 */
|
|
ret = of_property_read_u32(dev->of_node, "mic-bias2-voltage", &bias_v_conf);
|
|
if (!ret &&
|
|
((bias_v_conf >= MICBIAS_VO_1_5V) &&
|
|
(bias_v_conf <= MICBIAS_VO_2_8V))) {
|
|
jackdet->mic_bias2_voltage = bias_v_conf;
|
|
} else {
|
|
jackdet->mic_bias2_voltage = MICBIAS_VO_2_8V;
|
|
dev_warn(dev, "Property 'mic-bias2-voltage' %s",
|
|
ret ? "not found, default set 2.8V" : "used invalid value");
|
|
}
|
|
|
|
/* Mic Bias 3 */
|
|
ret = of_property_read_u32(dev->of_node, "mic-bias3-voltage", &bias_v_conf);
|
|
if (!ret &&
|
|
((bias_v_conf >= MICBIAS_VO_1_5V) &&
|
|
(bias_v_conf <= MICBIAS_VO_2_8V))) {
|
|
jackdet->mic_bias3_voltage = bias_v_conf;
|
|
} else {
|
|
jackdet->mic_bias3_voltage = MICBIAS_VO_2_8V;
|
|
dev_warn(dev, "Property 'mic-bias3-voltage' %s",
|
|
ret ? "not found, default set 2.8V" : "used invalid value");
|
|
}
|
|
|
|
dev_info(dev, "Bias voltage values: bias1=%d, bias2=%d, bias3=%d\n",
|
|
jackdet->mic_bias1_voltage,
|
|
jackdet->mic_bias2_voltage,
|
|
jackdet->mic_bias3_voltage);
|
|
|
|
/*
|
|
* Setting delay time for adc check
|
|
*/
|
|
/* Mic bias on delay time */
|
|
ret = of_property_read_u32(dev->of_node, "mic-det-delay", &delay);
|
|
if (!ret)
|
|
jackdet->mdet_delay = delay;
|
|
else
|
|
jackdet->mdet_delay = S5M3700X_MDET_DELAY;
|
|
|
|
/* Button adc check delay time */
|
|
ret = of_property_read_u32(dev->of_node, "btn-adc-delay", &delay);
|
|
if (!ret)
|
|
jackdet->btn_adc_delay = delay;
|
|
else
|
|
jackdet->btn_adc_delay = S5M3700X_BTN_ADC_DELAY;
|
|
|
|
dev_info(dev, "ADC check delay, mic: %d, btn: %d\n",
|
|
jackdet->mdet_delay, jackdet->btn_adc_delay);
|
|
|
|
/*
|
|
* Set button adc tuning values
|
|
*/
|
|
/* Button release adc value */
|
|
ret = of_property_read_u32(dev->of_node, "btn-release-value", &btn_rel_val);
|
|
if (!ret)
|
|
jackdet->btn_release_value = btn_rel_val;
|
|
else
|
|
jackdet->btn_release_value = S5M3700X_BTN_REL_DEFAULT;
|
|
|
|
/* Button press adc value, a maximum of 4 buttons are supported */
|
|
for (i = 0; i < 4; i++) {
|
|
if (of_parse_phandle_with_args(dev->of_node,
|
|
"but-zones-list", "#list-but-cells", i, &args))
|
|
break;
|
|
jackdet->jack_buttons_zones[i].code = args.args[0];
|
|
jackdet->jack_buttons_zones[i].adc_low = args.args[1];
|
|
jackdet->jack_buttons_zones[i].adc_high = args.args[2];
|
|
}
|
|
|
|
for (i = 0; i < 4; i++)
|
|
dev_info(dev, "Button Press: code(%d), low(%d), high(%d)\n",
|
|
jackdet->jack_buttons_zones[i].code,
|
|
jackdet->jack_buttons_zones[i].adc_low,
|
|
jackdet->jack_buttons_zones[i].adc_high);
|
|
dev_info(dev, "Button Release: %d\n", jackdet->btn_release_value);
|
|
|
|
/* Button debounce value */
|
|
ret = of_property_read_u32(dev->of_node, "btn-dbnc-value", &btn_rel_val);
|
|
if (!ret)
|
|
jackdet->btn_dbnc_value = btn_rel_val;
|
|
else
|
|
jackdet->btn_dbnc_value = S5M3700X_BTN_DBNC_DEFAULT;
|
|
|
|
/* USB HP Offset Cal */
|
|
ret = of_property_read_u32(dev->of_node, "usb-hp-chl", &usb_hp_chl);
|
|
if (!ret)
|
|
jackdet->usb_hp_chl = usb_hp_chl;
|
|
else
|
|
jackdet->usb_hp_chl = S5M3700X_USB_HP_CHL_OFFSET;
|
|
|
|
ret = of_property_read_u32(dev->of_node, "usb-hp-chr", &usb_hp_chr);
|
|
if (!ret)
|
|
jackdet->usb_hp_chr = usb_hp_chr;
|
|
else
|
|
jackdet->usb_hp_chr = S5M3700X_USB_HP_CHR_OFFSET;
|
|
|
|
dev_info(dev, "USB HP Offset Ch-L: %d, Ch-R: %d\n",
|
|
jackdet->usb_hp_chl, jackdet->usb_hp_chr);
|
|
}
|
|
|
|
static void s5m3700_usb_hp_trim(struct s5m3700x_priv *s5m3700x)
|
|
{
|
|
struct s5m3700x_jack *jackdet = s5m3700x->p_jackdet;
|
|
u8 t_cond[8], trim_val;
|
|
u32 cond[2];
|
|
unsigned int i = 0;
|
|
int sft_table[25] = {90, 81, 72, 64, 57, 51, 46, 41, 36, 33, 29,
|
|
26, 47, 42, 37, 34, 30, 27, 24, 22, 20, 18, 16, 14, 13};
|
|
unsigned int cal_table[25], tune_val = 0;
|
|
bool cond_flag;
|
|
|
|
dev_info(s5m3700x->dev, "%s called, setting usb hp trim values.\n", __func__);
|
|
|
|
s5m3700x_i2c_read_reg(S5M3700X_OTP_ADDR, 0x5F, &t_cond[0]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_OTP_ADDR, 0x5E, &t_cond[1]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_OTP_ADDR, 0x5D, &t_cond[2]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_OTP_ADDR, 0x5C, &t_cond[3]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_OTP_ADDR, 0x63, &t_cond[4]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_OTP_ADDR, 0x62, &t_cond[5]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_OTP_ADDR, 0x61, &t_cond[6]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_OTP_ADDR, 0x60, &t_cond[7]);
|
|
|
|
cond[0] = t_cond[0] + (t_cond[1] << 8) + (t_cond[2] << 16) + (t_cond[3] << 24);
|
|
cond[1] = t_cond[4] + (t_cond[5] << 8) + (t_cond[6] << 16) + (t_cond[7] << 24);
|
|
|
|
dev_info(s5m3700x->dev, "%s called, 0: 0x%08x 1: 0x%08x\n",
|
|
__func__, cond[0], cond[1]);
|
|
|
|
/* L Ch */
|
|
for (i = 0; i < 25; i++)
|
|
cal_table[i] = jackdet->usb_hp_chl / sft_table[i];
|
|
|
|
for (i = 0; i < 25; i++) {
|
|
s5m3700x_i2c_read_reg(S5M3700X_OTP_ADDR, 0x00 + i, &trim_val);
|
|
cond_flag = cond[0] & BIT(i);
|
|
if (cond_flag) {
|
|
tune_val = trim_val + cal_table[i];
|
|
if (tune_val >= 0xff)
|
|
tune_val = 0xff;
|
|
} else {
|
|
if (trim_val > cal_table[i]) {
|
|
tune_val = trim_val - cal_table[i];
|
|
} else {
|
|
tune_val = cal_table[i] - trim_val;
|
|
cond[0] = cond[0] | BIT(i);
|
|
}
|
|
}
|
|
s5m3700x_write(s5m3700x, S5M3700X_OTP_ADDR, 0x00 + i, tune_val);
|
|
}
|
|
|
|
/* R Ch */
|
|
for (i = 0; i < 25; i++)
|
|
cal_table[i] = jackdet->usb_hp_chr / sft_table[i];
|
|
|
|
for (i = 0; i < 25; i++) {
|
|
s5m3700x_i2c_read_reg(S5M3700X_OTP_ADDR, 0x19 + i, &trim_val);
|
|
cond_flag = cond[1] & BIT(i);
|
|
if (cond_flag) {
|
|
tune_val = trim_val + cal_table[i];
|
|
if (tune_val >= 0xff)
|
|
tune_val = 0xff;
|
|
} else {
|
|
if (trim_val > cal_table[i]) {
|
|
tune_val = trim_val - cal_table[i];
|
|
} else {
|
|
tune_val = cal_table[i] - trim_val;
|
|
cond[1] = cond[1] | BIT(i);
|
|
}
|
|
}
|
|
s5m3700x_write(s5m3700x, S5M3700X_OTP_ADDR, 0x19 + i, tune_val);
|
|
}
|
|
dev_info(s5m3700x->dev, "%s called, 0: 0x%08x 1: 0x%08x\n",
|
|
__func__, cond[0], cond[1]);
|
|
|
|
s5m3700x_write(s5m3700x, S5M3700X_OTP_ADDR, 0x5F, (cond[0] & MASK(8, 0)));
|
|
s5m3700x_write(s5m3700x, S5M3700X_OTP_ADDR, 0x5E, (cond[0] & MASK(8, 8)) >> 8);
|
|
s5m3700x_write(s5m3700x, S5M3700X_OTP_ADDR, 0x5D, (cond[0] & MASK(8, 16)) >> 16);
|
|
s5m3700x_write(s5m3700x, S5M3700X_OTP_ADDR, 0x5C, (cond[0] & MASK(8, 24)) >> 24);
|
|
s5m3700x_write(s5m3700x, S5M3700X_OTP_ADDR, 0x63, (cond[1] & MASK(8, 0)));
|
|
s5m3700x_write(s5m3700x, S5M3700X_OTP_ADDR, 0x62, (cond[1] & MASK(8, 8)) >> 8);
|
|
s5m3700x_write(s5m3700x, S5M3700X_OTP_ADDR, 0x61, (cond[1] & MASK(8, 16)) >> 16);
|
|
s5m3700x_write(s5m3700x, S5M3700X_OTP_ADDR, 0x60, (cond[1] & MASK(8, 24)) >> 24);
|
|
}
|
|
|
|
static void s5m3700x_hp_trim_initialize(struct s5m3700x_priv *s5m3700x)
|
|
{
|
|
u8 temp[50];
|
|
unsigned int i = 0;
|
|
|
|
dev_info(s5m3700x->dev, "%s called, setting hp trim values.\n", __func__);
|
|
|
|
for (i = 0; i < 50; i++) {
|
|
/* Reading HP Normal Trim */
|
|
s5m3700x_i2c_read_reg(S5M3700X_OTP_ADDR, i, &temp[i]);
|
|
s5m3700x->hp_normal_trim[i] = (unsigned int)temp[i];
|
|
|
|
/* Setting HP UHQA Trim */
|
|
s5m3700x->hp_uhqa_trim[i] = ((s5m3700x->hp_normal_trim[i] * 777) + 500) / 1000;
|
|
}
|
|
}
|
|
|
|
static void s5m3700x_usbjack_register_initialize(struct s5m3700x_priv *s5m3700x)
|
|
{
|
|
struct s5m3700x_jack *jackdet = s5m3700x->p_jackdet;
|
|
|
|
dev_info(s5m3700x->dev, "%s called, setting defaults\n", __func__);
|
|
|
|
/* PDB JD CLK Enable */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_D0_DCTR_CM, 0x01);
|
|
|
|
/* ANT LDET VTH Setting */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_C3_ACTR_JD4,
|
|
CTRV_ANT_LDET_VTH_MASK, ANT_LDET_VTH_3 << CTRV_ANT_LDET_VTH_SHIFT);
|
|
|
|
/* PDB JD Comparator On */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_C0_ACTR_JD1, 0x03);
|
|
|
|
/* T_PDB_BTN_DET */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_D5_DCTR_TEST5, 0xC3);
|
|
|
|
/* T_D2D_ANT_MDET_DBNC */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_D6_DCTR_TEST6, 0x01);
|
|
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_D7_DCTR_TEST7, 0x04);
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_DB_DCTR_DBNC4, 0x00);
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_DC_DCTR_DBNC5, 0x00);
|
|
|
|
/* IMP RAMP */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E9_DCTR_IMP3, 0x81);
|
|
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_FD_ACTR_GP, 0x0C);
|
|
|
|
/* T_JACK_STATE */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E3_DCTR_FSM4, 0x40);
|
|
|
|
/* Auto Btn Enable */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E5_DCTR_FSM6, 0x04);
|
|
|
|
/* IMP Skip */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_EA_DCTR_IMP4,
|
|
IMP_SKIP_OPT_MASK, IMP_SKIP_OPT_MASK);
|
|
|
|
/* MDET Trigger Setting */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_EE_ACTR_ETC1,
|
|
SEL_MDET_JO_SIG_MASK, SEL_MDET_JO_SIG_MASK);
|
|
|
|
/* ADC Delay */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_DF_DCTR_DLY2, 0x00);
|
|
|
|
/* LDET ADC Range Setting */
|
|
s5m3700x_write(s5m3700x, S5M3700X_OTP_ADDR, S5M3700X_2E0_JACK_OTP10, 0x4D);
|
|
s5m3700x_write(s5m3700x, S5M3700X_OTP_ADDR, S5M3700X_2E1_JACK_OTP11, 0x60);
|
|
s5m3700x_write(s5m3700x, S5M3700X_OTP_ADDR, S5M3700X_2E2_JACK_OTP12, 0x77);
|
|
|
|
/* Setting the MDET Trim */
|
|
s5m3700x_write(s5m3700x, S5M3700X_OTP_ADDR, 0xDD, 0x66);
|
|
|
|
/* IRQ Un-masking */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_08_IRQ1M, 0x3F);
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_09_IRQ2M, 0x7F);
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_0A_IRQ3M, 0xEE);
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_0B_IRQ4M, 0xEE);
|
|
|
|
/* 5-Pin Setting */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_E0_DCTR_FSM1, 0x06);
|
|
|
|
/* Tuning BTN DBNC Value */
|
|
s5m3700x_update_bits(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_DD_DCTR_DBNC6,
|
|
CTMD_BTN_DBNC_MASK, jackdet->btn_dbnc_value);
|
|
|
|
/* USB HP trim */
|
|
s5m3700_usb_hp_trim(s5m3700x);
|
|
}
|
|
|
|
static void s5m3700x_usbjack_register_exit(struct snd_soc_component *codec)
|
|
{
|
|
struct s5m3700x_priv *s5m3700x = snd_soc_component_get_drvdata(codec);
|
|
|
|
/* IRQ Masking */
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_08_IRQ1M, 0xFF);
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_09_IRQ2M, 0xFF);
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_0A_IRQ3M, 0xFF);
|
|
s5m3700x_write(s5m3700x, S5M3700X_DIGITAL_ADDR, S5M3700X_0B_IRQ4M, 0xFF);
|
|
}
|
|
|
|
static int return_irq_type_usb(struct s5m3700x_jack *jackdet, int insert)
|
|
{
|
|
unsigned int pend1, pend2, pend3, pend4, pend5, pend6, stat1, stat2, stat3;
|
|
struct s5m3700x_priv *s5m3700x = jackdet->p_s5m3700x;
|
|
|
|
/* Read the IRQ */
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_01_IRQ1, &jackdet->irq_val[0]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_02_IRQ2, &jackdet->irq_val[1]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_03_IRQ3, &jackdet->irq_val[2]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_04_IRQ4, &jackdet->irq_val[3]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_05_IRQ5, &jackdet->irq_val[4]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_06_IRQ6, &jackdet->irq_val[5]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_F0_STATUS1, &jackdet->irq_val[6]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_F1_STATUS2, &jackdet->irq_val[7]);
|
|
s5m3700x_i2c_read_reg(S5M3700X_DIGITAL_ADDR, S5M3700X_FA_STATUS11, &jackdet->irq_val[8]);
|
|
|
|
pend1 = jackdet->irq_val[0];
|
|
pend2 = jackdet->irq_val[1];
|
|
pend3 = jackdet->irq_val[2];
|
|
pend4 = jackdet->irq_val[3];
|
|
pend5 = jackdet->irq_val[4];
|
|
pend6 = jackdet->irq_val[5];
|
|
stat1 = jackdet->irq_val[6];
|
|
stat2 = jackdet->irq_val[7];
|
|
stat3 = jackdet->irq_val[8];
|
|
|
|
dev_info(s5m3700x->dev,
|
|
"[IRQ] %s(%d) 0x1:%02x 0x2:%02x 0x3:%02x 0x4:%02x 0x5:%02x 0x6:%02x st1:%02x st2:%02x st3:%02x insert: %d\n",
|
|
__func__, __LINE__, jackdet->irq_val[0], jackdet->irq_val[1],
|
|
jackdet->irq_val[2], jackdet->irq_val[3], jackdet->irq_val[4],
|
|
jackdet->irq_val[5], jackdet->irq_val[6], jackdet->irq_val[7], jackdet->irq_val[8], insert);
|
|
|
|
if (insert == IRQ_ST_JACKOUT)
|
|
return IRQ_ST_JACKOUT;
|
|
else if (insert == IRQ_ST_JACKDET)
|
|
return IRQ_ST_JACKDET;
|
|
else if (insert == 0) {
|
|
if (pend1 & ST_JO_R)
|
|
return IRQ_ST_JACKOUT;
|
|
else if (pend1 & ST_C_JI_R)
|
|
return IRQ_ST_CMP_JACK_IN;
|
|
else if (pend2 & ST_DEC_POLE_R)
|
|
return IRQ_ST_USB_JACKDET;
|
|
else if ((pend3 & BTN_DET_R) &&
|
|
(pend4 & BTN_DET_F) && (!(stat3 & BTN_STATE_MASK)))
|
|
return IRQ_ST_BTN_DET_R_F;
|
|
else if (pend3 & BTN_DET_R)
|
|
return IRQ_ST_BTN_DET_R;
|
|
else if (pend4 & BTN_DET_F)
|
|
return IRQ_ST_BTN_DET_F;
|
|
else
|
|
return IRQ_ST_ERR;
|
|
} else
|
|
return IRQ_ST_ERR;
|
|
}
|
|
|
|
static int s5m3700x_usbjack_handler(struct s5m3700x_priv *s5m3700x, unsigned int insert)
|
|
{
|
|
struct device *dev = s5m3700x->dev;
|
|
struct s5m3700x_jack *jackdet = s5m3700x->p_jackdet;
|
|
|
|
mutex_lock(&jackdet->key_lock);
|
|
|
|
switch (return_irq_type_usb(jackdet, insert)) {
|
|
case IRQ_ST_JACKOUT:
|
|
dev_info(dev, "[USB IRQ] %s USB Jack out interrupt, line: %d\n",
|
|
__func__, __LINE__);
|
|
#if IS_ENABLED(CONFIG_USB_SW_MAX20328B)
|
|
max20328_switch_mode_event(MAX_USBC_SWITCH_DISABLE);
|
|
#endif
|
|
s5m3700x_usbjackstate_set(jackdet, USB_OUT);
|
|
|
|
cancel_delayed_work(&jackdet->mdet_chk_work);
|
|
cancel_delayed_work(&jackdet->usbjack_work);
|
|
queue_delayed_work(jackdet->usbjack_wq, &jackdet->usbjack_work,
|
|
msecs_to_jiffies(0));
|
|
break;
|
|
case IRQ_ST_JACKDET:
|
|
dev_info(dev, "[USB IRQ] %s CCIC det interrupt, line: %d\n",
|
|
__func__, __LINE__);
|
|
|
|
s5m3700x_usbjackstate_set(jackdet, USB_INIT);
|
|
break;
|
|
case IRQ_ST_CMP_JACK_IN:
|
|
dev_info(dev, "[IRQ] %s Completely Jack in interrupt, line: %d\n",
|
|
__func__, __LINE__);
|
|
s5m3700x_usbjackstate_set(jackdet, USB_JACK_CMP);
|
|
break;
|
|
case IRQ_ST_USB_JACKDET:
|
|
dev_info(dev, "[IRQ] %s Jack det interrupt, line: %d\n",
|
|
__func__, __LINE__);
|
|
if (jackdet->usbjack_re_read == 1) {
|
|
jackdet->usbjack_re_read = 0;
|
|
break;
|
|
}
|
|
|
|
s5m3700x_usbjackstate_set(jackdet, USB_POLE_DEC);
|
|
|
|
/* lock for jack and button irq */
|
|
jack_wake_lock(jackdet->jack_wakeup);
|
|
|
|
cancel_delayed_work(&jackdet->usbjack_work);
|
|
queue_delayed_work(jackdet->usbjack_wq, &jackdet->usbjack_work,
|
|
msecs_to_jiffies(jackdet->mdet_delay));
|
|
break;
|
|
case IRQ_ST_BTN_DET_R_F:
|
|
dev_info(dev, "[USB IRQ] %s Button interrupt, line: %d\n",
|
|
__func__, __LINE__);
|
|
|
|
/* lock for jack and button irq */
|
|
jack_wake_lock(jackdet->jack_wakeup);
|
|
jackdet->btn_state = BUTTON_PRESS_RELEASE;
|
|
|
|
queue_delayed_work(jackdet->buttons_press_wq, &jackdet->buttons_work,
|
|
msecs_to_jiffies(jackdet->btn_adc_delay));
|
|
break;
|
|
case IRQ_ST_BTN_DET_R:
|
|
dev_info(dev, "[USB IRQ] %s Button interrupt, line: %d\n",
|
|
__func__, __LINE__);
|
|
|
|
/* lock for jack and button irq */
|
|
jack_wake_lock(jackdet->jack_wakeup);
|
|
|
|
queue_delayed_work(jackdet->buttons_press_wq, &jackdet->buttons_work,
|
|
msecs_to_jiffies(jackdet->btn_adc_delay));
|
|
break;
|
|
case IRQ_ST_BTN_DET_F:
|
|
dev_info(dev, "[USB IRQ] %s Button interrupt, line: %d\n",
|
|
__func__, __LINE__);
|
|
|
|
/* lock for jack and button irq */
|
|
jack_wake_lock(jackdet->jack_wakeup);
|
|
|
|
/* Ear-mic BST4 Reset */
|
|
reset_mic4_boost(jackdet);
|
|
|
|
queue_delayed_work(jackdet->buttons_release_wq, &jackdet->buttons_work,
|
|
msecs_to_jiffies(jackdet->btn_adc_delay));
|
|
break;
|
|
default:
|
|
dev_info(dev, "[USB IRQ] %s IRQ return type skip, line %d\n",
|
|
__func__, __LINE__);
|
|
break;
|
|
}
|
|
|
|
mutex_unlock(&jackdet->key_lock);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/*
|
|
* s5m3700x_usbjack_notifier_handler() - Codec IRQ Handler
|
|
*
|
|
* Desc: Set codec register according to codec IRQ.
|
|
*/
|
|
static int s5m3700x_usbjack_notifier_handler(struct notifier_block *nb,
|
|
unsigned long insert, void *data)
|
|
{
|
|
struct s5m3700x_usbjack_notifier_struct *ns = data;
|
|
struct s5m3700x_priv *s5m3700x = ns->s5m3700x;
|
|
|
|
dev_err(s5m3700x->dev, "%s called. insert: %d\n", __func__, insert);
|
|
|
|
s5m3700x_usbjack_handler(s5m3700x, (int)insert);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
void s5m3700x_usbjack_call_notifier(unsigned int event)
|
|
{
|
|
struct s5m3700x_jack *jackdet = NULL;
|
|
struct s5m3700x_priv *s5m3700x = NULL;
|
|
|
|
if (p_s5m3700x_jack != NULL) {
|
|
jackdet = p_s5m3700x_jack;
|
|
s5m3700x = jackdet->p_s5m3700x;
|
|
|
|
dev_info(s5m3700x->dev, "%s called, event: %d\n", __func__, event);
|
|
blocking_notifier_call_chain(&s5m3700x_usbjack_notifier, event, &s5m3700x_usbjack_notifier_t);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(s5m3700x_usbjack_call_notifier);
|
|
|
|
static irqreturn_t s5m3700x_irq_thread(int irq, void *irq_data)
|
|
{
|
|
blocking_notifier_call_chain(&s5m3700x_usbjack_notifier, 0, &s5m3700x_usbjack_notifier_t);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/*
|
|
* s5m3700x_register_inputdev() - Register button input device
|
|
*
|
|
* @jackdet: jack information struct
|
|
*
|
|
* Desc: Register button input device
|
|
*/
|
|
static int s5m3700x_register_inputdev(struct s5m3700x_jack *jackdet)
|
|
{
|
|
struct device *dev = jackdet->p_s5m3700x->dev;
|
|
int i, ret;
|
|
|
|
/* Register Headset Device */
|
|
jackdet->input = devm_input_allocate_device(dev);
|
|
if (!jackdet->input) {
|
|
dev_err(dev, "Failed to allocate switch input device\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
jackdet->input->name = "S5M3700X Headset Input";
|
|
jackdet->input->phys = dev_name(dev);
|
|
jackdet->input->id.bustype = BUS_I2C;
|
|
|
|
ret = input_register_device(jackdet->input);
|
|
if (ret != 0) {
|
|
jackdet->input = NULL;
|
|
dev_err(dev, "Failed to register switch input device\n");
|
|
}
|
|
|
|
/*
|
|
* input_set_capability (dev, type, code)
|
|
* @dev : input device
|
|
* @type : event type (EV_KEY, EV_SW, etc...)
|
|
* @code : event code (4POLE, 3POLE, LINE...)
|
|
*/
|
|
/* 3-Pole event */
|
|
input_set_capability(jackdet->input, EV_SW, SW_HEADPHONE_INSERT);
|
|
/* 4-Pole event */
|
|
input_set_capability(jackdet->input, EV_SW, SW_MICROPHONE_INSERT);
|
|
/* Button event */
|
|
for (i = 0; i < 4; i++)
|
|
input_set_capability(jackdet->input, EV_KEY,
|
|
jackdet->jack_buttons_zones[i].code);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* s5m3700x_usbjack_probe() - Initialize variable related jack
|
|
*
|
|
* @codec: SoC audio codec device
|
|
*
|
|
* Desc: This function is called by s5m3700x_component_probe. For separate codec
|
|
* and jack code, this function called from codec driver. This function initialize
|
|
* jack variable, workqueue, mutex, and wakelock.
|
|
*/
|
|
|
|
int s5m3700x_usbjack_probe(struct s5m3700x_priv *s5m3700x)
|
|
{
|
|
struct s5m3700x_jack *jackdet;
|
|
struct earjack_state *jackstate;
|
|
int ret;
|
|
|
|
dev_info(s5m3700x->dev, "Codec Jack Probe: (%s)\n", __func__);
|
|
|
|
jackdet = kzalloc(sizeof(struct s5m3700x_jack), GFP_KERNEL);
|
|
if (jackdet == NULL)
|
|
return -ENOMEM;
|
|
|
|
jackdet->codec = s5m3700x->codec;
|
|
jackdet->p_s5m3700x = s5m3700x;
|
|
p_s5m3700x_jack = jackdet;
|
|
s5m3700x->p_jackdet = jackdet;
|
|
jackstate = &jackdet->jack_state;
|
|
|
|
/* Initialize workqueue */
|
|
/* Initialize workqueue for button handling */
|
|
INIT_DELAYED_WORK(&jackdet->buttons_work, s5m3700x_buttons_work);
|
|
jackdet->buttons_press_wq = create_singlethread_workqueue("buttons_press_wq");
|
|
if (jackdet->buttons_press_wq == NULL) {
|
|
dev_err(s5m3700x->dev, "Failed to create buttons_press_wq\n");
|
|
return -ENOMEM;
|
|
}
|
|
jackdet->buttons_release_wq = create_singlethread_workqueue("buttons_release_wq");
|
|
if (jackdet->buttons_release_wq == NULL) {
|
|
dev_err(s5m3700x->dev, "Failed to create buttons_release_wq\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Initialize workqueue for usb detect handling */
|
|
INIT_DELAYED_WORK(&jackdet->usbjack_work, s5m3700x_usbjack_work);
|
|
jackdet->usbjack_wq = create_singlethread_workqueue("usbjack_wq");
|
|
if (jackdet->usbjack_wq == NULL) {
|
|
dev_err(s5m3700x->dev, "Failed to create usbjack_wq\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Initialize workqueue for mdet detect handling */
|
|
INIT_DELAYED_WORK(&jackdet->mdet_chk_work, s5m3700x_mdet_chk_work);
|
|
jackdet->mdet_chk_wq = create_singlethread_workqueue("mdet_chk_wq");
|
|
if (jackdet->mdet_chk_wq == NULL) {
|
|
dev_err(s5m3700x->dev, "Failed to create mdet_chk_wq\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Initialize mutex lock */
|
|
mutex_init(&jackdet->key_lock);
|
|
mutex_init(&jackdet->mdet_lock);
|
|
mutex_init(&jackdet->adc_lock);
|
|
mutex_init(&jackdet->usb_mb_lock);
|
|
if (jack_set_wake_lock(jackdet) < 0) {
|
|
pr_err("%s: jack_set_wake_lock fail\n", __func__);
|
|
goto err_wake_lock;
|
|
}
|
|
|
|
/* Device Tree for jack */
|
|
s5m3700x_usbjack_parse_dt(s5m3700x);
|
|
|
|
/* Register input device */
|
|
s5m3700x_register_inputdev(jackdet);
|
|
|
|
/* Jack Notifier initialize */
|
|
s5m3700x_usbjack_notifier_block.notifier_call = s5m3700x_usbjack_notifier_handler,
|
|
s5m3700x_usbjack_register_notifier(&s5m3700x_usbjack_notifier_block, s5m3700x);
|
|
|
|
/* Request Jack IRQ */
|
|
if (jackdet->irqb_gpio > 0) {
|
|
jackdet->codec_irq = gpio_to_irq(jackdet->irqb_gpio);
|
|
dev_info(s5m3700x->dev, "%s: codec_irq = %d\n", __func__, jackdet->codec_irq);
|
|
if (jackdet->codec_irq > 0) {
|
|
ret = request_threaded_irq(jackdet->codec_irq,
|
|
NULL, s5m3700x_irq_thread,
|
|
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
|
"s5m3700x-irq", jackdet);
|
|
if (ret)
|
|
dev_err(s5m3700x->dev, "%s Failed to Request IRQ. ret: %d\n", __func__, ret);
|
|
else {
|
|
dev_info(s5m3700x->dev, "%s Request IRQ. ret: %d\n", __func__, ret);
|
|
|
|
ret = enable_irq_wake(jackdet->codec_irq);
|
|
if (ret < 0) {
|
|
dev_err(s5m3700x->dev, "%s Failed to Enable Wakeup Source(%d)\n", __func__, ret);
|
|
goto err_wake_irq;
|
|
}
|
|
}
|
|
} else
|
|
dev_err(s5m3700x->dev, "%s Failed gpio_to_irq. ret: %d\n", __func__, jackdet->codec_irq);
|
|
}
|
|
|
|
/* Initialize jack state variable */
|
|
jackdet->switch_ic_state = 0;
|
|
jackdet->switch_jack_done = 0;
|
|
jackdet->usbjack_re_read = 0;
|
|
jackdet->earjack_re_read = 0;
|
|
jackdet->btn_state = 0;
|
|
jackstate->ldet_adc = -EINVAL;
|
|
jackstate->mdet_adc = -EINVAL;
|
|
jackstate->prv_btn_state = BUTTON_RELEASE;
|
|
jackstate->cur_btn_state = BUTTON_RELEASE;
|
|
jackstate->button_code = -EINVAL;
|
|
jackstate->btn_adc = -EINVAL;
|
|
|
|
#if IS_ENABLED(CONFIG_PM)
|
|
pm_runtime_get_sync(s5m3700x->dev);
|
|
#else
|
|
s5m3700x_enable(s5m3700x->dev);
|
|
#endif
|
|
|
|
/* register value init for jack */
|
|
jackstate->prv_jack_state = USB_OUT;
|
|
jackstate->cur_jack_state = USB_OUT;
|
|
|
|
s5m3700x_usbjack_register_initialize(s5m3700x);
|
|
|
|
if (s5m3700x->codec_ver >= REV_0_3)
|
|
s5m3700x_hp_trim_initialize(s5m3700x);
|
|
|
|
/* Configure mic bias voltage */
|
|
s5m3700x_configure_mic_bias(jackdet);
|
|
|
|
#if IS_ENABLED(CONFIG_PM)
|
|
pm_runtime_put_sync(s5m3700x->dev);
|
|
#else
|
|
s5m3700x_disable(s5m3700x->dev);
|
|
#endif
|
|
|
|
/* Jack OTP Read */
|
|
s5m3700x_read_imp_otp(jackdet);
|
|
|
|
dev_info(s5m3700x->dev, "%s : Jack probe done.\n", __func__);
|
|
|
|
return true;
|
|
err_wake_irq:
|
|
disable_irq_wake(jackdet->codec_irq);
|
|
err_wake_lock:
|
|
wakeup_source_unregister(jackdet->jack_wakeup);
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* s5m3700x_usbjack_remove() - Remove variable related jack
|
|
*
|
|
* @codec: SoC audio codec device
|
|
*
|
|
* Desc: This function is called by s5m3700x_component_remove. For separate codec
|
|
* and jack code, this function called from codec driver. This function destroy
|
|
* workqueue.
|
|
*/
|
|
int s5m3700x_usbjack_remove(struct snd_soc_component *codec)
|
|
{
|
|
struct s5m3700x_priv *s5m3700x = snd_soc_component_get_drvdata(codec);
|
|
struct s5m3700x_jack *jackdet = s5m3700x->p_jackdet;
|
|
|
|
dev_info(codec->dev, "(*) %s called\n", __func__);
|
|
|
|
destroy_workqueue(jackdet->jack_det_wq);
|
|
destroy_workqueue(jackdet->buttons_press_wq);
|
|
destroy_workqueue(jackdet->buttons_release_wq);
|
|
destroy_workqueue(jackdet->usbjack_wq);
|
|
destroy_workqueue(jackdet->mdet_chk_wq);
|
|
|
|
s5m3700x_usbjack_register_exit(codec);
|
|
|
|
/* Unregister ADC pin */
|
|
kfree(jackdet);
|
|
|
|
return true;
|
|
}
|