diff options
Diffstat (limited to 'sound/soc/codecs/rt5640.c')
-rw-r--r-- | sound/soc/codecs/rt5640.c | 553 |
1 files changed, 484 insertions, 69 deletions
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 05567426f211..8bf8d360c25f 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -24,6 +24,7 @@ #include <linux/spi/spi.h> #include <linux/acpi.h> #include <sound/core.h> +#include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -476,20 +477,6 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w, return idx; } -static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink) -{ - struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); - unsigned int val; - - val = snd_soc_component_read32(component, RT5640_GLB_CLK); - val &= RT5640_SCLK_SRC_MASK; - if (val == RT5640_SCLK_SRC_PLL1) - return 1; - else - return 0; -} - static int is_using_asrc(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink) { @@ -1071,9 +1058,6 @@ static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w, } static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { - SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2, - RT5640_PWR_PLL_BIT, 0, NULL, 0), - /* ASRC */ SND_SOC_DAPM_SUPPLY_S("Stereo Filter ASRC", 1, RT5640_ASRC_1, 15, 0, NULL, 0), @@ -1427,22 +1411,18 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { {"Stereo ADC MIXL", "ADC1 Switch", "Stereo ADC L1 Mux"}, {"Stereo ADC MIXL", "ADC2 Switch", "Stereo ADC L2 Mux"}, {"Stereo ADC MIXL", NULL, "Stereo Filter"}, - {"Stereo Filter", NULL, "PLL1", is_sys_clk_from_pll}, {"Stereo ADC MIXR", "ADC1 Switch", "Stereo ADC R1 Mux"}, {"Stereo ADC MIXR", "ADC2 Switch", "Stereo ADC R2 Mux"}, {"Stereo ADC MIXR", NULL, "Stereo Filter"}, - {"Stereo Filter", NULL, "PLL1", is_sys_clk_from_pll}, {"Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux"}, {"Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux"}, {"Mono ADC MIXL", NULL, "Mono Left Filter"}, - {"Mono Left Filter", NULL, "PLL1", is_sys_clk_from_pll}, {"Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux"}, {"Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux"}, {"Mono ADC MIXR", NULL, "Mono Right Filter"}, - {"Mono Right Filter", NULL, "PLL1", is_sys_clk_from_pll}, {"IF2 ADC L", NULL, "Mono ADC MIXL"}, {"IF2 ADC R", NULL, "Mono ADC MIXR"}, @@ -1512,10 +1492,8 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { {"DIG MIXR", "DAC R1 Switch", "DAC MIXR"}, {"DAC L1", NULL, "Stereo DAC MIXL"}, - {"DAC L1", NULL, "PLL1", is_sys_clk_from_pll}, {"DAC L1", NULL, "DAC L1 Power"}, {"DAC R1", NULL, "Stereo DAC MIXR"}, - {"DAC R1", NULL, "PLL1", is_sys_clk_from_pll}, {"DAC R1", NULL, "DAC R1 Power"}, {"SPK MIXL", "REC MIXL Switch", "RECMIXL"}, @@ -1622,10 +1600,8 @@ static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = { {"DIG MIXL", "DAC L2 Switch", "DAC L2 Mux"}, {"DAC L2", NULL, "Mono DAC MIXL"}, - {"DAC L2", NULL, "PLL1", is_sys_clk_from_pll}, {"DAC L2", NULL, "DAC L2 Power"}, {"DAC R2", NULL, "Mono DAC MIXR"}, - {"DAC R2", NULL, "PLL1", is_sys_clk_from_pll}, {"DAC R2", NULL, "DAC R2 Power"}, {"SPK MIXL", "DAC L2 Switch", "DAC L2"}, @@ -1861,6 +1837,7 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai, struct snd_soc_component *component = dai->component; struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); unsigned int reg_val = 0; + unsigned int pll_bit = 0; if (freq == rt5640->sysclk && clk_id == rt5640->sysclk_src) return 0; @@ -1871,6 +1848,7 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai, break; case RT5640_SCLK_S_PLL1: reg_val |= RT5640_SCLK_SRC_PLL1; + pll_bit |= RT5640_PWR_PLL; break; case RT5640_SCLK_S_RCCLK: reg_val |= RT5640_SCLK_SRC_RCCLK; @@ -1879,6 +1857,8 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai, dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); return -EINVAL; } + snd_soc_component_update_bits(component, RT5640_PWR_ANLG2, + RT5640_PWR_PLL, pll_bit); snd_soc_component_update_bits(component, RT5640_GLB_CLK, RT5640_SCLK_SRC_MASK, reg_val); rt5640->sysclk = freq; @@ -2114,10 +2094,376 @@ int rt5640_sel_asrc_clk_src(struct snd_soc_component *component, } EXPORT_SYMBOL_GPL(rt5640_sel_asrc_clk_src); +static void rt5640_enable_micbias1_for_ovcd(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_mutex_lock(dapm); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "LDO2"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS1"); + /* OVCD is unreliable when used with RCCLK as sysclk-source */ + snd_soc_dapm_force_enable_pin_unlocked(dapm, "Platform Clock"); + snd_soc_dapm_sync_unlocked(dapm); + snd_soc_dapm_mutex_unlock(dapm); +} + +static void rt5640_disable_micbias1_for_ovcd(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_mutex_lock(dapm); + snd_soc_dapm_disable_pin_unlocked(dapm, "Platform Clock"); + snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS1"); + snd_soc_dapm_disable_pin_unlocked(dapm, "LDO2"); + snd_soc_dapm_sync_unlocked(dapm); + snd_soc_dapm_mutex_unlock(dapm); +} + +static void rt5640_enable_micbias1_ovcd_irq(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2, + RT5640_IRQ_MB1_OC_MASK, RT5640_IRQ_MB1_OC_NOR); + rt5640->ovcd_irq_enabled = true; +} + +static void rt5640_disable_micbias1_ovcd_irq(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2, + RT5640_IRQ_MB1_OC_MASK, RT5640_IRQ_MB1_OC_BP); + rt5640->ovcd_irq_enabled = false; +} + +static void rt5640_clear_micbias1_ovcd(struct snd_soc_component *component) +{ + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2, + RT5640_MB1_OC_STATUS, 0); +} + +static bool rt5640_micbias1_ovcd(struct snd_soc_component *component) +{ + int val; + + val = snd_soc_component_read32(component, RT5640_IRQ_CTRL2); + dev_dbg(component->dev, "irq ctrl2 %#04x\n", val); + + return (val & RT5640_MB1_OC_STATUS); +} + +static bool rt5640_jack_inserted(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + int val; + + val = snd_soc_component_read32(component, RT5640_INT_IRQ_ST); + dev_dbg(component->dev, "irq status %#04x\n", val); + + if (rt5640->jd_inverted) + return !(val & RT5640_JD_STATUS); + else + return (val & RT5640_JD_STATUS); +} + +/* Jack detect and button-press timings */ +#define JACK_SETTLE_TIME 100 /* milli seconds */ +#define JACK_DETECT_COUNT 5 +#define JACK_DETECT_MAXCOUNT 20 /* Aprox. 2 seconds worth of tries */ +#define JACK_UNPLUG_TIME 80 /* milli seconds */ +#define BP_POLL_TIME 10 /* milli seconds */ +#define BP_POLL_MAXCOUNT 200 /* assume something is wrong after this */ +#define BP_THRESHOLD 3 + +static void rt5640_start_button_press_work(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + rt5640->poll_count = 0; + rt5640->press_count = 0; + rt5640->release_count = 0; + rt5640->pressed = false; + rt5640->press_reported = false; + rt5640_clear_micbias1_ovcd(component); + schedule_delayed_work(&rt5640->bp_work, msecs_to_jiffies(BP_POLL_TIME)); +} + +static void rt5640_button_press_work(struct work_struct *work) +{ + struct rt5640_priv *rt5640 = + container_of(work, struct rt5640_priv, bp_work.work); + struct snd_soc_component *component = rt5640->component; + + /* Check the jack was not removed underneath us */ + if (!rt5640_jack_inserted(component)) + return; + + if (rt5640_micbias1_ovcd(component)) { + rt5640->release_count = 0; + rt5640->press_count++; + /* Remember till after JACK_UNPLUG_TIME wait */ + if (rt5640->press_count >= BP_THRESHOLD) + rt5640->pressed = true; + rt5640_clear_micbias1_ovcd(component); + } else { + rt5640->press_count = 0; + rt5640->release_count++; + } + + /* + * The pins get temporarily shorted on jack unplug, so we poll for + * at least JACK_UNPLUG_TIME milli-seconds before reporting a press. + */ + rt5640->poll_count++; + if (rt5640->poll_count < (JACK_UNPLUG_TIME / BP_POLL_TIME)) { + schedule_delayed_work(&rt5640->bp_work, + msecs_to_jiffies(BP_POLL_TIME)); + return; + } + + if (rt5640->pressed && !rt5640->press_reported) { + dev_dbg(component->dev, "headset button press\n"); + snd_soc_jack_report(rt5640->jack, SND_JACK_BTN_0, + SND_JACK_BTN_0); + rt5640->press_reported = true; + } + + if (rt5640->release_count >= BP_THRESHOLD) { + if (rt5640->press_reported) { + dev_dbg(component->dev, "headset button release\n"); + snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0); + } + /* Re-enable OVCD IRQ to detect next press */ + rt5640_enable_micbias1_ovcd_irq(component); + return; /* Stop polling */ + } + + schedule_delayed_work(&rt5640->bp_work, msecs_to_jiffies(BP_POLL_TIME)); +} + +static int rt5640_detect_headset(struct snd_soc_component *component) +{ + int i, headset_count = 0, headphone_count = 0; + + /* + * We get the insertion event before the jack is fully inserted at which + * point the second ring on a TRRS connector may short the 2nd ring and + * sleeve contacts, also the overcurrent detection is not entirely + * reliable. So we try several times with a wait in between until we + * detect the same type JACK_DETECT_COUNT times in a row. + */ + for (i = 0; i < JACK_DETECT_MAXCOUNT; i++) { + /* Clear any previous over-current status flag */ + rt5640_clear_micbias1_ovcd(component); + + msleep(JACK_SETTLE_TIME); + + /* Check the jack is still connected before checking ovcd */ + if (!rt5640_jack_inserted(component)) + return 0; + + if (rt5640_micbias1_ovcd(component)) { + /* + * Over current detected, there is a short between the + * 2nd ring contact and the ground, so a TRS connector + * without a mic contact and thus plain headphones. + */ + dev_dbg(component->dev, "jack mic-gnd shorted\n"); + headset_count = 0; + headphone_count++; + if (headphone_count == JACK_DETECT_COUNT) + return SND_JACK_HEADPHONE; + } else { + dev_dbg(component->dev, "jack mic-gnd open\n"); + headphone_count = 0; + headset_count++; + if (headset_count == JACK_DETECT_COUNT) + return SND_JACK_HEADSET; + } + } + + dev_err(component->dev, "Error detecting headset vs headphones, bad contact?, assuming headphones\n"); + return SND_JACK_HEADPHONE; +} + +static void rt5640_jack_work(struct work_struct *work) +{ + struct rt5640_priv *rt5640 = + container_of(work, struct rt5640_priv, jack_work); + struct snd_soc_component *component = rt5640->component; + int status; + + if (!rt5640_jack_inserted(component)) { + /* Jack removed, or spurious IRQ? */ + if (rt5640->jack->status & SND_JACK_HEADPHONE) { + if (rt5640->jack->status & SND_JACK_MICROPHONE) { + cancel_delayed_work_sync(&rt5640->bp_work); + rt5640_disable_micbias1_ovcd_irq(component); + rt5640_disable_micbias1_for_ovcd(component); + } + snd_soc_jack_report(rt5640->jack, 0, + SND_JACK_HEADSET | SND_JACK_BTN_0); + dev_dbg(component->dev, "jack unplugged\n"); + } + } else if (!(rt5640->jack->status & SND_JACK_HEADPHONE)) { + /* Jack inserted */ + WARN_ON(rt5640->ovcd_irq_enabled); + rt5640_enable_micbias1_for_ovcd(component); + status = rt5640_detect_headset(component); + if (status == SND_JACK_HEADSET) { + /* Enable ovcd IRQ for button press detect. */ + rt5640_enable_micbias1_ovcd_irq(component); + } else { + /* No more need for overcurrent detect. */ + rt5640_disable_micbias1_for_ovcd(component); + } + dev_dbg(component->dev, "detect status %#02x\n", status); + snd_soc_jack_report(rt5640->jack, status, SND_JACK_HEADSET); + } else if (rt5640->ovcd_irq_enabled && rt5640_micbias1_ovcd(component)) { + dev_dbg(component->dev, "OVCD IRQ\n"); + + /* + * The ovcd IRQ keeps firing while the button is pressed, so + * we disable it and start polling the button until released. + * + * The disable will make the IRQ pin 0 again and since we get + * IRQs on both edges (so as to detect both jack plugin and + * unplug) this means we will immediately get another IRQ. + * The ovcd_irq_enabled check above makes the 2ND IRQ a NOP. + */ + rt5640_disable_micbias1_ovcd_irq(component); + rt5640_start_button_press_work(component); + + /* + * If the jack-detect IRQ flag goes high (unplug) after our + * above rt5640_jack_inserted() check and before we have + * disabled the OVCD IRQ, the IRQ pin will stay high and as + * we react to edges, we miss the unplug event -> recheck. + */ + queue_work(system_long_wq, &rt5640->jack_work); + } +} + +static irqreturn_t rt5640_irq(int irq, void *data) +{ + struct rt5640_priv *rt5640 = data; + + if (rt5640->jack) + queue_work(system_long_wq, &rt5640->jack_work); + + return IRQ_HANDLED; +} + +static void rt5640_cancel_work(void *data) +{ + struct rt5640_priv *rt5640 = data; + + cancel_work_sync(&rt5640->jack_work); + cancel_delayed_work_sync(&rt5640->bp_work); +} + +static void rt5640_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + /* Select JD-source */ + snd_soc_component_update_bits(component, RT5640_JD_CTRL, + RT5640_JD_MASK, rt5640->jd_src); + + /* Selecting GPIO01 as an interrupt */ + snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1, + RT5640_GP1_PIN_MASK, RT5640_GP1_PIN_IRQ); + + /* Set GPIO1 output */ + snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3, + RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT); + + /* Enabling jd2 in general control 1 */ + snd_soc_component_write(component, RT5640_DUMMY1, 0x3f41); + + /* Enabling jd2 in general control 2 */ + snd_soc_component_write(component, RT5640_DUMMY2, 0x4001); + + snd_soc_component_write(component, RT5640_PR_BASE + RT5640_BIAS_CUR4, + 0xa800 | rt5640->ovcd_sf); + + snd_soc_component_update_bits(component, RT5640_MICBIAS, + RT5640_MIC1_OVTH_MASK | RT5640_MIC1_OVCD_MASK, + rt5640->ovcd_th | RT5640_MIC1_OVCD_EN); + + /* + * The over-current-detect is only reliable in detecting the absence + * of over-current, when the mic-contact in the jack is short-circuited, + * the hardware periodically retries if it can apply the bias-current + * leading to the ovcd status flip-flopping 1-0-1 with it being 0 about + * 10% of the time, as we poll the ovcd status bit we might hit that + * 10%, so we enable sticky mode and when checking OVCD we clear the + * status, msleep() a bit and then check to get a reliable reading. + */ + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2, + RT5640_MB1_OC_STKY_MASK, RT5640_MB1_OC_STKY_EN); + + /* + * All IRQs get or-ed together, so we need the jack IRQ to report 0 + * when a jack is inserted so that the OVCD IRQ then toggles the IRQ + * pin 0/1 instead of it being stuck to 1. So we invert the JD polarity + * on systems where the hardware does not already do this. + */ + if (rt5640->jd_inverted) + snd_soc_component_write(component, RT5640_IRQ_CTRL1, + RT5640_IRQ_JD_NOR); + else + snd_soc_component_write(component, RT5640_IRQ_CTRL1, + RT5640_IRQ_JD_NOR | RT5640_JD_P_INV); + + rt5640->jack = jack; + if (rt5640->jack->status & SND_JACK_MICROPHONE) { + rt5640_enable_micbias1_for_ovcd(component); + rt5640_enable_micbias1_ovcd_irq(component); + } + + enable_irq(rt5640->irq); + /* sync initial jack state */ + queue_work(system_long_wq, &rt5640->jack_work); +} + +static void rt5640_disable_jack_detect(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + disable_irq(rt5640->irq); + rt5640_cancel_work(rt5640); + + if (rt5640->jack->status & SND_JACK_MICROPHONE) { + rt5640_disable_micbias1_ovcd_irq(component); + rt5640_disable_micbias1_for_ovcd(component); + snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0); + } + + rt5640->jack = NULL; +} + +static int rt5640_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + if (jack) + rt5640_enable_jack_detect(component, jack); + else + rt5640_disable_jack_detect(component); + + return 0; +} + static int rt5640_probe(struct snd_soc_component *component) { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + u32 dmic1_data_pin = 0; + u32 dmic2_data_pin = 0; + bool dmic_en = false; + u32 val; /* Check if MCLK provided */ rt5640->mclk = devm_clk_get(component->dev, "mclk"); @@ -2159,9 +2505,86 @@ static int rt5640_probe(struct snd_soc_component *component) return -ENODEV; } - if (rt5640->pdata.dmic_en) - rt5640_dmic_enable(component, rt5640->pdata.dmic1_data_pin, - rt5640->pdata.dmic2_data_pin); + /* + * Note on some platforms the platform code may need to add device-props + * rather then relying only on properties set by the firmware. + * Therefor the property parsing MUST be done here, rather then from + * rt5640_i2c_probe(), so that the platform-code can attach extra + * properties before calling snd_soc_register_card(). + */ + if (device_property_read_bool(component->dev, "realtek,in1-differential")) + snd_soc_component_update_bits(component, RT5640_IN1_IN2, + RT5640_IN_DF1, RT5640_IN_DF1); + + if (device_property_read_bool(component->dev, "realtek,in2-differential")) + snd_soc_component_update_bits(component, RT5640_IN3_IN4, + RT5640_IN_DF2, RT5640_IN_DF2); + + if (device_property_read_bool(component->dev, "realtek,in3-differential")) + snd_soc_component_update_bits(component, RT5640_IN1_IN2, + RT5640_IN_DF2, RT5640_IN_DF2); + + if (device_property_read_u32(component->dev, "realtek,dmic1-data-pin", + &val) == 0 && val) { + dmic1_data_pin = val - 1; + dmic_en = true; + } + + if (device_property_read_u32(component->dev, "realtek,dmic2-data-pin", + &val) == 0 && val) { + dmic2_data_pin = val - 1; + dmic_en = true; + } + + if (dmic_en) + rt5640_dmic_enable(component, dmic1_data_pin, dmic2_data_pin); + + if (device_property_read_u32(component->dev, + "realtek,jack-detect-source", &val) == 0) { + if (val <= RT5640_JD_SRC_GPIO4) + rt5640->jd_src = val << RT5640_JD_SFT; + else + dev_warn(component->dev, "Warning: Invalid jack-detect-source value: %d, leaving jack-detect disabled\n", + val); + } + + if (!device_property_read_bool(component->dev, "realtek,jack-detect-not-inverted")) + rt5640->jd_inverted = true; + + /* + * Testing on various boards has shown that good defaults for the OVCD + * threshold and scale-factor are 2000µA and 0.75. For an effective + * limit of 1500µA, this seems to be more reliable then 1500µA and 1.0. + */ + rt5640->ovcd_th = RT5640_MIC1_OVTH_2000UA; + rt5640->ovcd_sf = RT5640_MIC_OVCD_SF_0P75; + + if (device_property_read_u32(component->dev, + "realtek,over-current-threshold-microamp", &val) == 0) { + switch (val) { + case 600: + rt5640->ovcd_th = RT5640_MIC1_OVTH_600UA; + break; + case 1500: + rt5640->ovcd_th = RT5640_MIC1_OVTH_1500UA; + break; + case 2000: + rt5640->ovcd_th = RT5640_MIC1_OVTH_2000UA; + break; + default: + dev_warn(component->dev, "Warning: Invalid over-current-threshold-microamp value: %d, defaulting to 2000uA\n", + val); + } + } + + if (device_property_read_u32(component->dev, + "realtek,over-current-scale-factor", &val) == 0) { + if (val <= RT5640_OVCD_SF_1P5) + rt5640->ovcd_sf = val << RT5640_MIC_OVCD_SF_SFT; + else + dev_warn(component->dev, "Warning: Invalid over-current-scale-factor value: %d, defaulting to 0.75\n", + val); + } return 0; } @@ -2180,8 +2603,8 @@ static int rt5640_suspend(struct snd_soc_component *component) rt5640_reset(component); regcache_cache_only(rt5640->regmap, true); regcache_mark_dirty(rt5640->regmap); - if (gpio_is_valid(rt5640->pdata.ldo1_en)) - gpio_set_value_cansleep(rt5640->pdata.ldo1_en, 0); + if (gpio_is_valid(rt5640->ldo1_en)) + gpio_set_value_cansleep(rt5640->ldo1_en, 0); return 0; } @@ -2190,8 +2613,8 @@ static int rt5640_resume(struct snd_soc_component *component) { struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); - if (gpio_is_valid(rt5640->pdata.ldo1_en)) { - gpio_set_value_cansleep(rt5640->pdata.ldo1_en, 1); + if (gpio_is_valid(rt5640->ldo1_en)) { + gpio_set_value_cansleep(rt5640->ldo1_en, 1); msleep(400); } @@ -2263,6 +2686,7 @@ static const struct snd_soc_component_driver soc_component_dev_rt5640 = { .suspend = rt5640_suspend, .resume = rt5640_resume, .set_bias_level = rt5640_set_bias_level, + .set_jack = rt5640_set_jack, .controls = rt5640_snd_controls, .num_controls = ARRAY_SIZE(rt5640_snd_controls), .dapm_widgets = rt5640_dapm_widgets, @@ -2323,22 +2747,16 @@ MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match); static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np) { - rt5640->pdata.in1_diff = of_property_read_bool(np, - "realtek,in1-differential"); - rt5640->pdata.in2_diff = of_property_read_bool(np, - "realtek,in2-differential"); - - rt5640->pdata.ldo1_en = of_get_named_gpio(np, - "realtek,ldo1-en-gpios", 0); + rt5640->ldo1_en = of_get_named_gpio(np, "realtek,ldo1-en-gpios", 0); /* * LDO1_EN is optional (it may be statically tied on the board). * -ENOENT means that the property doesn't exist, i.e. there is no * GPIO, so is not an error. Any other error code means the property * exists, but could not be parsed. */ - if (!gpio_is_valid(rt5640->pdata.ldo1_en) && - (rt5640->pdata.ldo1_en != -ENOENT)) - return rt5640->pdata.ldo1_en; + if (!gpio_is_valid(rt5640->ldo1_en) && + (rt5640->ldo1_en != -ENOENT)) + return rt5640->ldo1_en; return 0; } @@ -2346,7 +2764,6 @@ static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np) static int rt5640_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { - struct rt5640_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt5640_priv *rt5640; int ret; unsigned int val; @@ -2358,22 +2775,12 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, return -ENOMEM; i2c_set_clientdata(i2c, rt5640); - if (pdata) { - rt5640->pdata = *pdata; - /* - * Translate zero'd out (default) pdata value to an invalid - * GPIO ID. This makes the pdata and DT paths consistent in - * terms of the value left in this field when no GPIO is - * specified, but means we can't actually use GPIO 0. - */ - if (!rt5640->pdata.ldo1_en) - rt5640->pdata.ldo1_en = -EINVAL; - } else if (i2c->dev.of_node) { + if (i2c->dev.of_node) { ret = rt5640_parse_dt(rt5640, i2c->dev.of_node); if (ret) return ret; } else - rt5640->pdata.ldo1_en = -EINVAL; + rt5640->ldo1_en = -EINVAL; rt5640->regmap = devm_regmap_init_i2c(i2c, &rt5640_regmap); if (IS_ERR(rt5640->regmap)) { @@ -2383,13 +2790,13 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, return ret; } - if (gpio_is_valid(rt5640->pdata.ldo1_en)) { - ret = devm_gpio_request_one(&i2c->dev, rt5640->pdata.ldo1_en, + if (gpio_is_valid(rt5640->ldo1_en)) { + ret = devm_gpio_request_one(&i2c->dev, rt5640->ldo1_en, GPIOF_OUT_INIT_HIGH, "RT5640 LDO1_EN"); if (ret < 0) { dev_err(&i2c->dev, "Failed to request LDO1_EN %d: %d\n", - rt5640->pdata.ldo1_en, ret); + rt5640->ldo1_en, ret); return ret; } msleep(400); @@ -2412,19 +2819,27 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5640->regmap, RT5640_DUMMY1, RT5640_MCLK_DET, RT5640_MCLK_DET); - if (rt5640->pdata.in1_diff) - regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2, - RT5640_IN_DF1, RT5640_IN_DF1); - - if (rt5640->pdata.in2_diff) - regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4, - RT5640_IN_DF2, RT5640_IN_DF2); + rt5640->hp_mute = 1; + rt5640->irq = i2c->irq; + INIT_DELAYED_WORK(&rt5640->bp_work, rt5640_button_press_work); + INIT_WORK(&rt5640->jack_work, rt5640_jack_work); - if (rt5640->pdata.in3_diff) - regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2, - RT5640_IN_DF2, RT5640_IN_DF2); + /* Make sure work is stopped on probe-error / remove */ + ret = devm_add_action_or_reset(&i2c->dev, rt5640_cancel_work, rt5640); + if (ret) + return ret; - rt5640->hp_mute = 1; + ret = devm_request_irq(&i2c->dev, rt5640->irq, rt5640_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5640", rt5640); + if (ret == 0) { + /* Gets re-enabled by rt5640_set_jack() */ + disable_irq(rt5640->irq); + } else { + dev_warn(&i2c->dev, "Failed to reguest IRQ %d: %d\n", + rt5640->irq, ret); + rt5640->irq = -ENXIO; + } return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5640, |