summaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/rt5640.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/rt5640.c')
-rw-r--r--sound/soc/codecs/rt5640.c553
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,