aboutsummaryrefslogtreecommitdiffstats
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.c266
1 files changed, 227 insertions, 39 deletions
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index d01fe73ab9c8..0f8e6dd214b0 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -195,6 +195,7 @@ static bool rt5640_volatile_register(struct device *dev, unsigned int reg)
case RT5640_PRIV_DATA:
case RT5640_PGM_REG_ARR1:
case RT5640_PGM_REG_ARR3:
+ case RT5640_DUMMY2:
case RT5640_VENDOR_ID:
case RT5640_VENDOR_ID1:
case RT5640_VENDOR_ID2:
@@ -1838,9 +1839,6 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
unsigned int reg_val = 0;
unsigned int pll_bit = 0;
- if (freq == rt5640->sysclk && clk_id == rt5640->sysclk_src)
- return 0;
-
switch (clk_id) {
case RT5640_SCLK_S_MCLK:
reg_val |= RT5640_SCLK_SRC_MCLK;
@@ -1972,7 +1970,7 @@ static int rt5640_set_bias_level(struct snd_soc_component *component,
RT5640_PWR_FV1 | RT5640_PWR_FV2,
RT5640_PWR_FV1 | RT5640_PWR_FV2);
snd_soc_component_update_bits(component, RT5640_DUMMY1,
- 0x0301, 0x0301);
+ 0x1, 0x1);
snd_soc_component_update_bits(component, RT5640_MICBIAS,
0x0030, 0x0030);
}
@@ -1986,7 +1984,12 @@ static int rt5640_set_bias_level(struct snd_soc_component *component,
snd_soc_component_write(component, RT5640_PWR_DIG2, 0x0000);
snd_soc_component_write(component, RT5640_PWR_VOL, 0x0000);
snd_soc_component_write(component, RT5640_PWR_MIXER, 0x0000);
- snd_soc_component_write(component, RT5640_PWR_ANLG1, 0x0000);
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER)
+ snd_soc_component_write(component, RT5640_PWR_ANLG1,
+ 0x2818);
+ else
+ snd_soc_component_write(component, RT5640_PWR_ANLG1,
+ 0x0000);
snd_soc_component_write(component, RT5640_PWR_ANLG2, 0x0000);
break;
@@ -2096,12 +2099,14 @@ EXPORT_SYMBOL_GPL(rt5640_sel_asrc_clk_src);
void rt5640_enable_micbias1_for_ovcd(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);
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");
+ if (rt5640->use_platform_clock)
+ snd_soc_dapm_force_enable_pin_unlocked(dapm, "Platform Clock");
snd_soc_dapm_sync_unlocked(dapm);
snd_soc_dapm_mutex_unlock(dapm);
}
@@ -2110,9 +2115,11 @@ EXPORT_SYMBOL_GPL(rt5640_enable_micbias1_for_ovcd);
void rt5640_disable_micbias1_for_ovcd(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);
snd_soc_dapm_mutex_lock(dapm);
- snd_soc_dapm_disable_pin_unlocked(dapm, "Platform Clock");
+ if (rt5640->use_platform_clock)
+ 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);
@@ -2159,7 +2166,11 @@ 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_read(component, RT5640_INT_IRQ_ST);
+ if (rt5640->jd_gpio)
+ val = gpiod_get_value(rt5640->jd_gpio) ? RT5640_JD_STATUS : 0;
+ else
+ val = snd_soc_component_read(component, RT5640_INT_IRQ_ST);
+
dev_dbg(component->dev, "irq status %#04x\n", val);
if (rt5640->jd_inverted)
@@ -2297,10 +2308,42 @@ EXPORT_SYMBOL_GPL(rt5640_detect_headset);
static void rt5640_jack_work(struct work_struct *work)
{
struct rt5640_priv *rt5640 =
- container_of(work, struct rt5640_priv, jack_work);
+ container_of(work, struct rt5640_priv, jack_work.work);
struct snd_soc_component *component = rt5640->component;
int status;
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) {
+ int val, jack_type = 0, hda_mic_plugged, hda_hp_plugged;
+
+ /* mic jack */
+ val = snd_soc_component_read(component, RT5640_INT_IRQ_ST);
+ hda_mic_plugged = !(val & RT5640_JD_STATUS);
+ dev_dbg(component->dev, "mic jack status %d\n",
+ hda_mic_plugged);
+
+ snd_soc_component_update_bits(component, RT5640_IRQ_CTRL1,
+ RT5640_JD_P_MASK, !hda_mic_plugged << RT5640_JD_P_SFT);
+
+ if (hda_mic_plugged)
+ jack_type |= SND_JACK_MICROPHONE;
+
+ /* headphone jack */
+ val = snd_soc_component_read(component, RT5640_DUMMY2);
+ hda_hp_plugged = !(val & (0x1 << 11));
+ dev_dbg(component->dev, "headphone jack status %d\n",
+ hda_hp_plugged);
+
+ snd_soc_component_update_bits(component, RT5640_DUMMY2,
+ (0x1 << 10), !hda_hp_plugged << 10);
+
+ if (hda_hp_plugged)
+ jack_type |= SND_JACK_HEADPHONE;
+
+ snd_soc_jack_report(rt5640->jack, jack_type, SND_JACK_HEADSET);
+
+ return;
+ }
+
if (!rt5640_jack_inserted(component)) {
/* Jack removed, or spurious IRQ? */
if (rt5640->jack->status & SND_JACK_HEADPHONE) {
@@ -2348,16 +2391,32 @@ static void rt5640_jack_work(struct work_struct *work)
* 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);
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
}
}
static irqreturn_t rt5640_irq(int irq, void *data)
{
struct rt5640_priv *rt5640 = data;
+ int delay = 0;
+
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) {
+ cancel_delayed_work_sync(&rt5640->jack_work);
+ delay = 100;
+ }
if (rt5640->jack)
- queue_work(system_long_wq, &rt5640->jack_work);
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, delay);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rt5640_jd_gpio_irq(int irq, void *data)
+{
+ struct rt5640_priv *rt5640 = data;
+
+ queue_delayed_work(system_long_wq, &rt5640->jack_work,
+ msecs_to_jiffies(JACK_SETTLE_TIME));
return IRQ_HANDLED;
}
@@ -2366,7 +2425,7 @@ static void rt5640_cancel_work(void *data)
{
struct rt5640_priv *rt5640 = data;
- cancel_work_sync(&rt5640->jack_work);
+ cancel_delayed_work_sync(&rt5640->jack_work);
cancel_delayed_work_sync(&rt5640->bp_work);
}
@@ -2406,7 +2465,12 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component)
if (!rt5640->jack)
return;
- free_irq(rt5640->irq, rt5640);
+ if (rt5640->jd_gpio_irq_requested)
+ free_irq(rt5640->jd_gpio_irq, rt5640);
+
+ if (rt5640->irq_requested)
+ free_irq(rt5640->irq, rt5640);
+
rt5640_cancel_work(rt5640);
if (rt5640->jack->status & SND_JACK_MICROPHONE) {
@@ -2415,18 +2479,22 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component)
snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0);
}
+ rt5640->jd_gpio_irq_requested = false;
+ rt5640->irq_requested = false;
+ rt5640->jd_gpio = NULL;
rt5640->jack = NULL;
}
static void rt5640_enable_jack_detect(struct snd_soc_component *component,
- struct snd_soc_jack *jack)
+ struct snd_soc_jack *jack,
+ struct rt5640_set_jack_data *jack_data)
{
struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
int ret;
/* Select JD-source */
snd_soc_component_update_bits(component, RT5640_JD_CTRL,
- RT5640_JD_MASK, rt5640->jd_src);
+ RT5640_JD_MASK, rt5640->jd_src << RT5640_JD_SFT);
/* Selecting GPIO01 as an interrupt */
snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1,
@@ -2436,12 +2504,8 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
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);
-
rt5640_set_ovcd_params(component);
/*
@@ -2450,12 +2514,25 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
* 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);
+ if (rt5640->jd_inverted) {
+ if (rt5640->jd_src == RT5640_JD_SRC_JD1_IN4P)
+ snd_soc_component_write(component, RT5640_IRQ_CTRL1,
+ RT5640_IRQ_JD_NOR);
+ else if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
+ snd_soc_component_update_bits(component, RT5640_DUMMY2,
+ RT5640_IRQ_JD2_MASK | RT5640_JD2_MASK,
+ RT5640_IRQ_JD2_NOR | RT5640_JD2_EN);
+ } else {
+ if (rt5640->jd_src == RT5640_JD_SRC_JD1_IN4P)
+ snd_soc_component_write(component, RT5640_IRQ_CTRL1,
+ RT5640_IRQ_JD_NOR | RT5640_JD_P_INV);
+ else if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
+ snd_soc_component_update_bits(component, RT5640_DUMMY2,
+ RT5640_IRQ_JD2_MASK | RT5640_JD2_P_MASK |
+ RT5640_JD2_MASK,
+ RT5640_IRQ_JD2_NOR | RT5640_JD2_P_INV |
+ RT5640_JD2_EN);
+ }
rt5640->jack = jack;
if (rt5640->jack->status & SND_JACK_MICROPHONE) {
@@ -2463,28 +2540,111 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
rt5640_enable_micbias1_ovcd_irq(component);
}
+ if (jack_data && jack_data->codec_irq_override)
+ rt5640->irq = jack_data->codec_irq_override;
+
+ if (jack_data && jack_data->jd_gpio) {
+ rt5640->jd_gpio = jack_data->jd_gpio;
+ rt5640->jd_gpio_irq = gpiod_to_irq(rt5640->jd_gpio);
+
+ ret = request_irq(rt5640->jd_gpio_irq, rt5640_jd_gpio_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "rt5640-jd-gpio", rt5640);
+ if (ret) {
+ dev_warn(component->dev, "Failed to request jd GPIO IRQ %d: %d\n",
+ rt5640->jd_gpio_irq, ret);
+ rt5640_disable_jack_detect(component);
+ return;
+ }
+ rt5640->jd_gpio_irq_requested = true;
+ }
+
+ if (jack_data && jack_data->use_platform_clock)
+ rt5640->use_platform_clock = jack_data->use_platform_clock;
+
ret = request_irq(rt5640->irq, rt5640_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"rt5640", rt5640);
if (ret) {
dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret);
- rt5640->irq = -ENXIO;
- /* Undo above settings */
rt5640_disable_jack_detect(component);
return;
}
+ rt5640->irq_requested = true;
/* sync initial jack state */
- queue_work(system_long_wq, &rt5640->jack_work);
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+}
+
+static const struct snd_soc_dapm_route rt5640_hda_jack_dapm_routes[] = {
+ {"IN1P", NULL, "MICBIAS1"},
+ {"IN2P", NULL, "MICBIAS1"},
+ {"IN3P", NULL, "MICBIAS1"},
+};
+
+static void rt5640_enable_hda_jack_detect(
+ struct snd_soc_component *component, struct snd_soc_jack *jack)
+{
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ int ret;
+
+ /* Select JD1 for Mic */
+ snd_soc_component_update_bits(component, RT5640_JD_CTRL,
+ RT5640_JD_MASK, RT5640_JD_JD1_IN4P);
+ snd_soc_component_write(component, RT5640_IRQ_CTRL1, RT5640_IRQ_JD_NOR);
+
+ /* Select JD2 for Headphone */
+ snd_soc_component_update_bits(component, RT5640_DUMMY2, 0x1100, 0x1100);
+
+ /* 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);
+
+ snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x400, 0x0);
+
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
+ RT5640_PWR_VREF2 | RT5640_PWR_MB | RT5640_PWR_BG,
+ RT5640_PWR_VREF2 | RT5640_PWR_MB | RT5640_PWR_BG);
+ usleep_range(10000, 15000);
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
+ RT5640_PWR_FV2, RT5640_PWR_FV2);
+
+ rt5640->jack = jack;
+
+ ret = request_irq(rt5640->irq, rt5640_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5640", rt5640);
+ if (ret) {
+ dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret);
+ rt5640->irq = -ENXIO;
+ return;
+ }
+
+ /* sync initial jack state */
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+
+ snd_soc_dapm_add_routes(dapm, rt5640_hda_jack_dapm_routes,
+ ARRAY_SIZE(rt5640_hda_jack_dapm_routes));
}
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
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ if (jack) {
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER)
+ rt5640_enable_hda_jack_detect(component, jack);
+ else
+ rt5640_enable_jack_detect(component, jack, data);
+ } else {
rt5640_disable_jack_detect(component);
+ }
return 0;
}
@@ -2574,8 +2734,8 @@ static int rt5640_probe(struct snd_soc_component *component)
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;
+ if (val <= RT5640_JD_SRC_HDA_HEADER)
+ rt5640->jd_src = val;
else
dev_warn(component->dev, "Warning: Invalid jack-detect-source value: %d, leaving jack-detect disabled\n",
val);
@@ -2632,6 +2792,7 @@ static int rt5640_suspend(struct snd_soc_component *component)
{
struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+ rt5640_cancel_work(rt5640);
snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
rt5640_reset(component);
regcache_cache_only(rt5640->regmap, true);
@@ -2654,6 +2815,36 @@ static int rt5640_resume(struct snd_soc_component *component)
regcache_cache_only(rt5640->regmap, false);
regcache_sync(rt5640->regmap);
+ if (rt5640->jack) {
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) {
+ snd_soc_component_update_bits(component,
+ RT5640_DUMMY2, 0x1100, 0x1100);
+ } else {
+ if (rt5640->jd_inverted) {
+ if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
+ snd_soc_component_update_bits(
+ component, RT5640_DUMMY2,
+ RT5640_IRQ_JD2_MASK |
+ RT5640_JD2_MASK,
+ RT5640_IRQ_JD2_NOR |
+ RT5640_JD2_EN);
+
+ } else {
+ if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
+ snd_soc_component_update_bits(
+ component, RT5640_DUMMY2,
+ RT5640_IRQ_JD2_MASK |
+ RT5640_JD2_P_MASK |
+ RT5640_JD2_MASK,
+ RT5640_IRQ_JD2_NOR |
+ RT5640_JD2_P_INV |
+ RT5640_JD2_EN);
+ }
+ }
+
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+ }
+
return 0;
}
#else
@@ -2728,8 +2919,6 @@ static const struct snd_soc_component_driver soc_component_dev_rt5640 = {
.num_dapm_routes = ARRAY_SIZE(rt5640_dapm_routes),
.use_pmdown_time = 1,
.endianness = 1,
- .non_legacy_dai_naming = 1,
-
};
static const struct regmap_config rt5640_regmap = {
@@ -2795,8 +2984,7 @@ static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np)
return 0;
}
-static int rt5640_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5640_i2c_probe(struct i2c_client *i2c)
{
struct rt5640_priv *rt5640;
int ret;
@@ -2856,7 +3044,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
rt5640->hp_mute = true;
rt5640->irq = i2c->irq;
INIT_DELAYED_WORK(&rt5640->bp_work, rt5640_button_press_work);
- INIT_WORK(&rt5640->jack_work, rt5640_jack_work);
+ INIT_DELAYED_WORK(&rt5640->jack_work, rt5640_jack_work);
/* Make sure work is stopped on probe-error / remove */
ret = devm_add_action_or_reset(&i2c->dev, rt5640_cancel_work, rt5640);
@@ -2874,7 +3062,7 @@ static struct i2c_driver rt5640_i2c_driver = {
.acpi_match_table = ACPI_PTR(rt5640_acpi_match),
.of_match_table = of_match_ptr(rt5640_of_match),
},
- .probe = rt5640_i2c_probe,
+ .probe_new = rt5640_i2c_probe,
.id_table = rt5640_i2c_id,
};
module_i2c_driver(rt5640_i2c_driver);