* 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,
{ 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);
/* Interface data select */
static const char * const rt5640_data_select[] = {
- "Normal", "left copy to right", "right copy to left", "Swap"};
+ "Normal", "Swap", "left copy to right", "right copy to left"};
static SOC_ENUM_SINGLE_DECL(rt5640_if1_dac_enum, RT5640_DIG_INF_DATA,
RT5640_IF1_DAC_SEL_SFT, rt5640_data_select);
case SND_SOC_DAPM_PRE_PMD:
rt5640->hp_mute = 1;
+ rt5640_hp_gpio_ctrl(rt5640, false);
usleep_range(70000, 75000);
break;
-
default:
return 0;
}
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;
}
0x0301, 0x0301);
snd_soc_update_bits(codec, RT5640_MICBIAS,
0x0030, 0x0030);
+ snd_soc_write(codec, RT5640_PWR_ANLG2, 0x0800); //micbas1
}
break;
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);
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;
}
{ "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,
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
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);
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));
}
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",
},
.probe = rt5640_i2c_probe,
.remove = rt5640_i2c_remove,
+ .shutdown = rt5640_i2c_shutdown,
.id_table = rt5640_i2c_id,
};
module_i2c_driver(rt5640_i2c_driver);