ARM64: firefly: Add rk3399-firefly board support
[firefly-linux-kernel-4.4.55.git] / sound / soc / codecs / rt5640.c
index b1c8bb39cdf1e829e9ea79d4c1d2ae974da432d6..ed29619df53f92331b57cc608521596c3c8e3a0d 100644 (file)
@@ -9,7 +9,6 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <sound/soc-dapm.h>
 #include <sound/initval.h>
 #include <sound/tlv.h>
-
+#include <sound/jack.h>
 #include "rl6231.h"
 #include "rt5640.h"
-
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/consumer.h>
+
+#define RT5640_ADC_EMPTY_ADVALUE                       950
+#define RT5640_ADC_DRIFT_ADVALUE                       70
+#define RT5640_ADC_INVALID_ADVALUE                     -1
+#define RT5640_ADC_SAMPLE_JIFFIES (100 / (MSEC_PER_SEC / HZ))  /* 100ms */
 #define RT5640_DEVICE_ID 0x6231
-
 #define RT5640_PR_RANGE_BASE (0xff + 1)
 #define RT5640_PR_SPACING 0x100
-
 #define RT5640_PR_BASE (RT5640_PR_RANGE_BASE + (0 * RT5640_PR_SPACING))
 
 static const struct regmap_range_cfg rt5640_ranges[] = {
@@ -166,6 +171,17 @@ static const struct reg_default rt5640_reg[] = {
        { 0xff, 0x6231 },
 };
 
+static void rt5640_hp_gpio_ctrl(struct rt5640_priv *rt5640, bool enable)
+{
+       dev_dbg(rt5640->codec->dev, "hp-con-gpio enable=%d\n", enable);
+       if (enable)
+               gpio_set_value(rt5640->hp_con_gpio,
+                              rt5640->hp_con_gpio_active_high ? 1 : 0);
+       else
+               gpio_set_value(rt5640->hp_con_gpio,
+                              rt5640->hp_con_gpio_active_high ? 0 : 1);
+}
+
 static int rt5640_reset(struct snd_soc_codec *codec)
 {
        return snd_soc_write(codec, RT5640_RESET, 0);
@@ -981,9 +997,9 @@ static int rt5640_hp_event(struct snd_soc_dapm_widget *w,
 
        case SND_SOC_DAPM_PRE_PMD:
                rt5640->hp_mute = 1;
+               rt5640_hp_gpio_ctrl(rt5640, false);
                usleep_range(70000, 75000);
                break;
-
        default:
                return 0;
        }
@@ -1044,9 +1060,56 @@ static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
-               if (!rt5640->hp_mute)
-                       usleep_range(80000, 85000);
+               if (!rt5640->hp_mute && rt5640->hp_insert) {
+                       usleep_range(120000, 125000);
+                       rt5640_hp_gpio_ctrl(rt5640, true);
+               }
+
+               break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5640_mono_adcl_event(struct snd_soc_dapm_widget *w,
+                                 struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec, RT5640_GEN_CTRL1,
+                                   RT5640_M_MAMIX_L, 0);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, RT5640_GEN_CTRL1,
+                                   RT5640_M_MAMIX_L,
+                                   RT5640_M_MAMIX_L);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5640_mono_adcr_event(struct snd_soc_dapm_widget *w,
+                                 struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec, RT5640_GEN_CTRL1,
+                                   RT5640_M_MAMIX_R, 0);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, RT5640_GEN_CTRL1,
+                                   RT5640_M_MAMIX_R,
+                                   RT5640_M_MAMIX_R);
                break;
 
        default:
@@ -1133,12 +1196,18 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
                rt5640_sto_adc_r_mix, ARRAY_SIZE(rt5640_sto_adc_r_mix)),
        SND_SOC_DAPM_SUPPLY("Mono Left Filter", RT5640_PWR_DIG2,
                RT5640_PWR_ADC_MF_L_BIT, 0, NULL, 0),
-       SND_SOC_DAPM_MIXER("Mono ADC MIXL", SND_SOC_NOPM, 0, 0,
-               rt5640_mono_adc_l_mix, ARRAY_SIZE(rt5640_mono_adc_l_mix)),
+       SND_SOC_DAPM_MIXER_E("Mono ADC MIXL", SND_SOC_NOPM, 0, 0,
+                            rt5640_mono_adc_l_mix,
+                            ARRAY_SIZE(rt5640_mono_adc_l_mix),
+                            rt5640_mono_adcl_event,
+                            SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
        SND_SOC_DAPM_SUPPLY("Mono Right Filter", RT5640_PWR_DIG2,
                RT5640_PWR_ADC_MF_R_BIT, 0, NULL, 0),
-       SND_SOC_DAPM_MIXER("Mono ADC MIXR", SND_SOC_NOPM, 0, 0,
-               rt5640_mono_adc_r_mix, ARRAY_SIZE(rt5640_mono_adc_r_mix)),
+       SND_SOC_DAPM_MIXER_E("Mono ADC MIXR", SND_SOC_NOPM, 0, 0,
+                            rt5640_mono_adc_r_mix,
+                            ARRAY_SIZE(rt5640_mono_adc_r_mix),
+                            rt5640_mono_adcr_event,
+                            SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
 
        /* Digital Interface */
        SND_SOC_DAPM_SUPPLY("I2S1", RT5640_PWR_DIG1,
@@ -1917,7 +1986,33 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
 static int rt5640_set_bias_level(struct snd_soc_codec *codec,
                        enum snd_soc_bias_level level)
 {
+       struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
        switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               /*
+                * SND_SOC_BIAS_PREPARE is called while preparing for a
+                * transition to ON or away from ON. If current bias_level
+                * is SND_SOC_BIAS_ON, then it is preparing for a transition
+                * away from ON. Disable the clock in that case, otherwise
+                * enable it.
+                */
+               if (IS_ERR(rt5640->mclk))
+                       break;
+
+               if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+                       clk_disable_unprepare(rt5640->mclk);
+               } else {
+                       ret = clk_prepare_enable(rt5640->mclk);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
        case SND_SOC_BIAS_STANDBY:
                if (SND_SOC_BIAS_OFF == snd_soc_codec_get_bias_level(codec)) {
                        snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
@@ -1979,13 +2074,89 @@ int rt5640_dmic_enable(struct snd_soc_codec *codec,
 
        return 0;
 }
+
 EXPORT_SYMBOL_GPL(rt5640_dmic_enable);
 
+static int rt5640_hp_jack_change(struct notifier_block *nb,
+                                unsigned long flags, void *data)
+{
+       return NOTIFY_OK;
+}
+
+static struct notifier_block rt5640_hp_jack_nb = {
+       .notifier_call = rt5640_hp_jack_change,
+};
+
+static void rt5640_jack_init(struct rt5640_priv *rt5640)
+{
+       snd_soc_card_jack_new(rt5640->codec->component.card,
+                             "Headphone Jack", SND_JACK_HEADPHONE,
+                       &rt5640->hp_jack, NULL, 0);
+       snd_soc_jack_notifier_register(&rt5640->hp_jack, &rt5640_hp_jack_nb);
+}
+
+static int rt5640_hp_adc_iio_read(struct rt5640_priv *rt5640)
+{
+       struct iio_channel *channel = rt5640->chan;
+       int val, ret;
+
+       if (!channel)
+               return RT5640_ADC_INVALID_ADVALUE;
+       ret = iio_read_channel_raw(channel, &val);
+       if (ret < 0) {
+               dev_err(rt5640->codec->dev,
+                       "read hp_det channel() error: %d\n", ret);
+               return ret;
+       }
+       return val;
+}
+
+static void rt5640_hp_adc_poll(struct work_struct *work)
+{
+       struct rt5640_priv *rt5640;
+       int result = -1;
+
+       rt5640 = container_of(work, struct rt5640_priv, adc_poll_work.work);
+               result = rt5640_hp_adc_iio_read(rt5640);
+               if (result > RT5640_ADC_INVALID_ADVALUE &&
+                   result < RT5640_ADC_EMPTY_ADVALUE) {
+                       if (result < rt5640->hp_det_adc_value + RT5640_ADC_DRIFT_ADVALUE &&
+                           result > rt5640->hp_det_adc_value - RT5640_ADC_DRIFT_ADVALUE){
+               if (!rt5640->hp_insert) {
+                       dev_dbg(rt5640->codec->dev,
+                               "headphone insert,adc=%d\n", result);
+                       rt5640->hp_insert = true;
+                               snd_soc_jack_report(&rt5640->hp_jack, SND_JACK_HEADPHONE,
+                                                   SND_JACK_HEADPHONE);
+                       if (!rt5640->hp_mute)
+                               rt5640_hp_gpio_ctrl(rt5640, true);
+               }
+       } else{
+               if (rt5640->hp_insert) {
+                       dev_dbg(rt5640->codec->dev,
+                               "headphone not insert,adc=%d\n", result);
+                       rt5640->hp_insert = false;
+                                       rt5640_hp_gpio_ctrl(rt5640, false);
+                                       snd_soc_jack_report(&rt5640->hp_jack, 0,
+                                                           SND_JACK_HEADPHONE);
+                       }
+               }
+       }
+
+       schedule_delayed_work(&rt5640->adc_poll_work,
+                             RT5640_ADC_SAMPLE_JIFFIES);
+}
+
 static int rt5640_probe(struct snd_soc_codec *codec)
 {
        struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
        struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
 
+       /* Check if MCLK provided */
+       rt5640->mclk = devm_clk_get(codec->dev, "mclk");
+       if (PTR_ERR(rt5640->mclk) == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
+
        rt5640->codec = codec;
 
        snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
@@ -2025,6 +2196,16 @@ static int rt5640_probe(struct snd_soc_codec *codec)
                rt5640_dmic_enable(codec, rt5640->pdata.dmic1_data_pin,
                                          rt5640->pdata.dmic2_data_pin);
 
+       rt5640->hp_mute = 1;
+       rt5640->hp_insert = false;
+       /* adc polling work */
+       if (rt5640->chan) {
+               rt5640_jack_init(rt5640);
+               INIT_DELAYED_WORK(&rt5640->adc_poll_work, rt5640_hp_adc_poll);
+               schedule_delayed_work(&rt5640->adc_poll_work,
+                                     1000);
+       }
+
        return 0;
 }
 
@@ -2177,11 +2358,46 @@ static const struct acpi_device_id rt5640_acpi_match[] = {
        { "10EC5642", 0 },
        { },
 };
+
 MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match);
 #endif
 
-static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np)
+static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device *dev)
 {
+       struct device_node *np = dev->of_node;
+       struct iio_channel *chan;
+       u32 adc_value;
+       enum of_gpio_flags flags;
+       int gpio, ret;
+
+       chan = iio_channel_get(dev, NULL);
+       if (IS_ERR(chan)) {
+               dev_warn(dev, "rt5640 have no io-channels defined\n");
+               chan = NULL;
+       } else{
+               if (!of_property_read_u32(np, "hp-det-adc-value", &adc_value)) {
+                       rt5640->hp_det_adc_value = adc_value;
+               } else{
+                       chan = NULL;
+                       dev_err(dev, "rt5640 have no hp_det_adc_value defined\n");
+               }
+       }
+       rt5640->chan = chan;
+
+       gpio = of_get_named_gpio_flags(np, "hp-con-gpio", 0, &flags);
+       if (gpio < 0) {
+               dev_err(dev, "Can not read property hp-con-gpio\n");
+       } else {
+               rt5640->hp_con_gpio_active_high =
+                       (flags & OF_GPIO_ACTIVE_LOW) ? false : true;
+               ret = devm_gpio_request(dev, gpio, "hp-con-gpio");
+               if (ret < 0)
+                       dev_err(dev, "hp-con-gpio request ERROR\n");
+               else
+                       gpio_direction_output(gpio, rt5640->hp_con_gpio_active_high ? 0 : 1);
+       }
+       rt5640->hp_con_gpio = gpio;
+
        rt5640->pdata.in1_diff = of_property_read_bool(np,
                                        "realtek,in1-differential");
        rt5640->pdata.in2_diff = of_property_read_bool(np,
@@ -2228,7 +2444,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
                if (!rt5640->pdata.ldo1_en)
                        rt5640->pdata.ldo1_en = -EINVAL;
        } else if (i2c->dev.of_node) {
-               ret = rt5640_parse_dt(rt5640, i2c->dev.of_node);
+               ret = rt5640_parse_dt(rt5640, &(i2c->dev));
                if (ret)
                        return ret;
        } else
@@ -2254,7 +2470,10 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
                msleep(400);
        }
 
-       regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
+       ret = regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
+       if (ret)
+               return -EPROBE_DEFER;
+
        if (val != RT5640_DEVICE_ID) {
                dev_err(&i2c->dev,
                        "Device with ID register %#x is not rt5640/39\n", val);
@@ -2280,8 +2499,6 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
                regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2,
                                        RT5640_IN_DF2, RT5640_IN_DF2);
 
-       rt5640->hp_mute = 1;
-
        return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
                                      rt5640_dai, ARRAY_SIZE(rt5640_dai));
 }
@@ -2293,6 +2510,14 @@ static int rt5640_i2c_remove(struct i2c_client *i2c)
        return 0;
 }
 
+static void rt5640_i2c_shutdown(struct i2c_client *i2c)
+{
+       struct rt5640_priv *rt5640;
+
+       rt5640 = (struct rt5640_priv *)i2c_get_clientdata(i2c);
+       rt5640_hp_gpio_ctrl(rt5640, false);
+}
+
 static struct i2c_driver rt5640_i2c_driver = {
        .driver = {
                .name = "rt5640",
@@ -2301,6 +2526,7 @@ static struct i2c_driver rt5640_i2c_driver = {
        },
        .probe = rt5640_i2c_probe,
        .remove   = rt5640_i2c_remove,
+       .shutdown = rt5640_i2c_shutdown,
        .id_table = rt5640_i2c_id,
 };
 module_i2c_driver(rt5640_i2c_driver);