X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=sound%2Fsoc%2Fcodecs%2Frt5640.c;h=515a5b0aefb2a59f9cb30cb1cb2008cf46dcd771;hb=2ae05321496aa27767bc33a5cc19451b3db67919;hp=f2beb1aa5763dfe99dc6de821f4c9367446773a7;hpb=4fe5e199ebcd82a05c7446e7e6b85027358dc274;p=firefly-linux-kernel-4.4.55.git diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index f2beb1aa5763..515a5b0aefb2 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -9,7 +9,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - #include #include #include @@ -30,17 +29,28 @@ #include #include #include - +#include #include "rl6231.h" #include "rt5640.h" - +#include +#include +#include +#include + +#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); @@ -359,7 +498,7 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv, /* 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); @@ -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);