ARM64: DTS: Fix Firefly board audio driver
[firefly-linux-kernel-4.4.55.git] / sound / soc / codecs / rt5640.c
index b1c8bb39cdf1e829e9ea79d4c1d2ae974da432d6..515a5b0aefb2a59f9cb30cb1cb2008cf46dcd771 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))
 
+#define LINE_IN_OKAY 1
+#define LINE_IN_NO   0
+static int line_in_status = 0;
+static bool aux_irq_flag = true;  /*true:enable   false:disable*/
+
 static const struct regmap_range_cfg rt5640_ranges[] = {
        { .name = "PR", .range_min = RT5640_PR_BASE,
          .range_max = RT5640_PR_BASE + 0xb4,
@@ -166,6 +176,135 @@ 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 (line_in_status && (false == enable))
+               return;
+       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 void rt5640_set_linein(struct rt5640_priv *rt5640)//struct snd_soc_codec *code/)
+{
+       printk("%s enter\n",__func__);
+       //regmap_write(rt5640->regmap, RT5640_RESET, 0);
+       regmap_write(rt5640->regmap, RT5640_PWR_ANLG1, 0xfdfc);     //63
+       regmap_update_bits(rt5640->regmap, RT5640_REC_L2_MIXER,    //3c
+                               1<<5 | 1<<4,
+                        0<<5 | 1<<4);
+       regmap_update_bits(rt5640->regmap, RT5640_REC_R2_MIXER,    //3e
+                               1<<5 | 1<<4,
+                        0<<5 | 1<<4);
+
+       regmap_update_bits(rt5640->regmap, RT5640_OUT_L3_MIXER,    //4f
+                               1<<3 , 0<<3);
+       regmap_update_bits(rt5640->regmap, RT5640_OUT_R3_MIXER,    //52
+                               1<<3 , 0<<3);
+
+       regmap_write(rt5640->regmap, RT5640_HP_VOL, 0x0808);        //02
+       regmap_update_bits(rt5640->regmap, RT5640_HPO_MIXER,        //45
+                               1<<13 | 1<<12,
+                               0<<13 | 1<<12);
+
+       regmap_update_bits(rt5640->regmap, RT5640_PWR_MIXER,    //65
+                               1<<10 | 1<<11 | 1<<14 | 1<<15,
+                        1<<10 | 1<<11 | 1<<14 | 1<<15);
+       regmap_update_bits(rt5640->regmap, RT5640_PWR_VOL,    //66
+                               1<<8 | 1<<9 | 1<<10 | 1<<11,
+                        1<<8 | 1<<9 | 1<<10 | 1<<11);
+
+       regmap_write(rt5640->regmap, RT5640_DEPOP_M1, 0x8019);     //8e
+       regmap_write(rt5640->regmap, RT5640_DEPOP_M2, 0x3100);     //8f
+
+       regmap_write(rt5640->regmap, RT5640_DUMMY1, 0x3401);     //fa
+
+       rt5640_hp_gpio_ctrl(rt5640, 1);
+}
+
+static void aux_det_work_func(struct work_struct *work)
+{
+       struct rt5640_priv *rt5640 = container_of(work, struct rt5640_priv, aux_det_work.work);
+       line_in_status = gpio_get_value(rt5640->aux_det_gpio);
+       printk("%s", __func__);
+
+       if (line_in_status == LINE_IN_OKAY){
+               printk(" [on] \n");
+               rt5640_set_linein(rt5640);
+       }
+       else if (line_in_status == LINE_IN_NO)
+       {
+               printk(" [off]\n");
+       }
+
+       if(!aux_irq_flag) {
+               enable_irq(rt5640->aux_det_irq);
+               aux_irq_flag = true;
+       }
+}
+
+static irqreturn_t aux_det_isr(int irq, void *data)
+{
+       struct rt5640_priv *rt5640 = data;
+
+       printk("%s\n", __func__);
+
+       line_in_status = gpio_get_value(rt5640->aux_det_gpio);
+       if(aux_irq_flag) {
+               disable_irq_nosync(rt5640->aux_det_irq);
+               aux_irq_flag = false;
+       }
+
+       if (line_in_status == LINE_IN_OKAY) {
+               //set line in on
+               printk(" line_in: [on] \n");
+               line_in_status = LINE_IN_OKAY;
+               schedule_delayed_work(&rt5640->aux_det_work, msecs_to_jiffies(0));
+               //rt5640_set_linein(rt5640);
+       }
+       else if (line_in_status == LINE_IN_NO) {
+               //set line in off
+               printk(" line_in: [off] \n");
+               if (!aux_irq_flag) {
+                       enable_irq(rt5640->aux_det_irq);
+                       aux_irq_flag = true;
+               }
+               line_in_status = LINE_IN_NO;
+       }
+
+       return IRQ_HANDLED;
+}
+
+static void rt5640_delay_workq(struct work_struct *work)
+{
+       int ret;
+       struct rt5640_priv *rt5640 = container_of(work, struct rt5640_priv, init_delayed_work.work);
+       printk("%s\n", __func__);
+
+       rt5640->aux_det_irq = gpio_to_irq(rt5640->aux_det_gpio);
+       if (rt5640->aux_det_irq < 0) {
+        gpio_free(rt5640->aux_det_gpio);
+               printk("aux_det_irq req fail\n");
+       }
+       else {
+               ret = request_irq(rt5640->aux_det_irq, aux_det_isr,  IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "irq_aux_det", rt5640);
+               if (ret)
+                       printk(KERN_ALERT "Cannot allocate linein INT!ERRNO:%d\n", ret);
+               else {
+                       if (aux_irq_flag) {
+                               disable_irq(rt5640->aux_det_irq);
+                               aux_irq_flag = false;
+                       }
+               }
+       }
+
+       schedule_delayed_work(&rt5640->aux_det_work, msecs_to_jiffies(100));
+}
+
 static int rt5640_reset(struct snd_soc_codec *codec)
 {
        return snd_soc_write(codec, RT5640_RESET, 0);
@@ -981,9 +1120,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 +1183,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 +1319,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 +2109,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,
@@ -1933,6 +2151,7 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec,
                                                0x0301, 0x0301);
                        snd_soc_update_bits(codec, RT5640_MICBIAS,
                                                0x0030, 0x0030);
+                       snd_soc_write(codec, RT5640_PWR_ANLG2, 0x0800); //micbas1
                }
                break;
 
@@ -1979,13 +2198,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 +2320,22 @@ 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);
+       }
+
+    /* Init workquence to set up line in func */
+       INIT_DELAYED_WORK(&rt5640->init_delayed_work, rt5640_delay_workq);
+       INIT_DELAYED_WORK(&rt5640->aux_det_work, aux_det_work_func);
+
+       schedule_delayed_work(&rt5640->init_delayed_work, msecs_to_jiffies(20000));
+
        return 0;
 }
 
@@ -2177,11 +2488,64 @@ 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;
+       unsigned long irq_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;
+
+       gpio = of_get_named_gpio_flags(np, "aux-det-gpio", 0, (enum of_gpio_flags *)&irq_flags);
+       if (gpio < 0) {
+               dev_err(dev, "Can not read property aux-det-gpio\n");
+       } else {
+               if (gpio_is_valid(gpio)) {
+                       ret = devm_gpio_request(dev, gpio, "aux-det-gpio");
+                       if (ret < 0) {
+                               dev_err(dev, "aux-det-gpio request ERROR\n");
+                       } else {
+                               ret = gpio_direction_input(gpio);
+                               if (ret < 0)
+                                       dev_err(dev, "gpio_direction_input aux-det-gpio set ERROR\n");
+                       }
+               }
+       }
+       rt5640->aux_det_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 +2592,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 +2618,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 +2647,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 +2658,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 +2674,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);