123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925 |
- /*
- * da7219-aad.c - Dialog DA7219 ALSA SoC AAD Driver
- *
- * Copyright (c) 2015 Dialog Semiconductor Ltd.
- *
- * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
- #include <linux/module.h>
- #include <linux/platform_device.h>
- #include <linux/clk.h>
- #include <linux/i2c.h>
- #include <linux/property.h>
- #include <linux/pm_wakeirq.h>
- #include <linux/slab.h>
- #include <linux/delay.h>
- #include <linux/workqueue.h>
- #include <sound/soc.h>
- #include <sound/jack.h>
- #include <sound/da7219.h>
- #include "da7219.h"
- #include "da7219-aad.h"
- /*
- * Detection control
- */
- void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
- {
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- da7219->aad->jack = jack;
- da7219->aad->jack_inserted = false;
- /* Send an initial empty report */
- snd_soc_jack_report(jack, 0, DA7219_AAD_REPORT_ALL_MASK);
- /* Enable/Disable jack detection */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
- DA7219_ACCDET_EN_MASK,
- (jack ? DA7219_ACCDET_EN_MASK : 0));
- }
- EXPORT_SYMBOL_GPL(da7219_aad_jack_det);
- /*
- * Button/HPTest work
- */
- static void da7219_aad_btn_det_work(struct work_struct *work)
- {
- struct da7219_aad_priv *da7219_aad =
- container_of(work, struct da7219_aad_priv, btn_det_work);
- struct snd_soc_codec *codec = da7219_aad->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- u8 statusa, micbias_ctrl;
- bool micbias_up = false;
- int retries = 0;
- /* Drive headphones/lineout */
- snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
- DA7219_HP_L_AMP_OE_MASK,
- DA7219_HP_L_AMP_OE_MASK);
- snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
- DA7219_HP_R_AMP_OE_MASK,
- DA7219_HP_R_AMP_OE_MASK);
- /* Make sure mic bias is up */
- snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
- snd_soc_dapm_sync(dapm);
- do {
- statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A);
- if (statusa & DA7219_MICBIAS_UP_STS_MASK)
- micbias_up = true;
- else if (retries++ < DA7219_AAD_MICBIAS_CHK_RETRIES)
- msleep(DA7219_AAD_MICBIAS_CHK_DELAY);
- } while ((!micbias_up) && (retries < DA7219_AAD_MICBIAS_CHK_RETRIES));
- if (retries >= DA7219_AAD_MICBIAS_CHK_RETRIES)
- dev_warn(codec->dev, "Mic bias status check timed out");
- /*
- * Mic bias pulse required to enable mic, must be done before enabling
- * button detection to prevent erroneous button readings.
- */
- if (da7219_aad->micbias_pulse_lvl && da7219_aad->micbias_pulse_time) {
- /* Pulse higher level voltage */
- micbias_ctrl = snd_soc_read(codec, DA7219_MICBIAS_CTRL);
- snd_soc_update_bits(codec, DA7219_MICBIAS_CTRL,
- DA7219_MICBIAS1_LEVEL_MASK,
- da7219_aad->micbias_pulse_lvl);
- msleep(da7219_aad->micbias_pulse_time);
- snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_ctrl);
- }
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
- DA7219_BUTTON_CONFIG_MASK,
- da7219_aad->btn_cfg);
- }
- static void da7219_aad_hptest_work(struct work_struct *work)
- {
- struct da7219_aad_priv *da7219_aad =
- container_of(work, struct da7219_aad_priv, hptest_work);
- struct snd_soc_codec *codec = da7219_aad->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- u16 tonegen_freq_hptest;
- u8 pll_srm_sts, gain_ramp_ctrl, accdet_cfg8;
- int report = 0, ret = 0;
- /* Lock DAPM and any Kcontrols that are affected by this test */
- snd_soc_dapm_mutex_lock(dapm);
- mutex_lock(&da7219->lock);
- /* Ensure MCLK is available for HP test procedure */
- if (da7219->mclk) {
- ret = clk_prepare_enable(da7219->mclk);
- if (ret) {
- dev_err(codec->dev, "Failed to enable mclk - %d\n", ret);
- mutex_unlock(&da7219->lock);
- snd_soc_dapm_mutex_unlock(dapm);
- return;
- }
- }
- /*
- * If MCLK not present, then we're using the internal oscillator and
- * require different frequency settings to achieve the same result.
- */
- pll_srm_sts = snd_soc_read(codec, DA7219_PLL_SRM_STS);
- if (pll_srm_sts & DA7219_PLL_SRM_STS_MCLK)
- tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ);
- else
- tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ_INT_OSC);
- /* Ensure gain ramping at fastest rate */
- gain_ramp_ctrl = snd_soc_read(codec, DA7219_GAIN_RAMP_CTRL);
- snd_soc_write(codec, DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_X8);
- /* Bypass cache so it saves current settings */
- regcache_cache_bypass(da7219->regmap, true);
- /* Make sure Tone Generator is disabled */
- snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0);
- /* Enable HPTest block, 1KOhms check */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8,
- DA7219_HPTEST_EN_MASK | DA7219_HPTEST_RES_SEL_MASK,
- DA7219_HPTEST_EN_MASK |
- DA7219_HPTEST_RES_SEL_1KOHMS);
- /* Set gains to 0db */
- snd_soc_write(codec, DA7219_DAC_L_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
- snd_soc_write(codec, DA7219_DAC_R_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
- snd_soc_write(codec, DA7219_HP_L_GAIN, DA7219_HP_AMP_GAIN_0DB);
- snd_soc_write(codec, DA7219_HP_R_GAIN, DA7219_HP_AMP_GAIN_0DB);
- /* Disable DAC filters, EQs and soft mute */
- snd_soc_update_bits(codec, DA7219_DAC_FILTERS1, DA7219_HPF_MODE_MASK,
- 0);
- snd_soc_update_bits(codec, DA7219_DAC_FILTERS4, DA7219_DAC_EQ_EN_MASK,
- 0);
- snd_soc_update_bits(codec, DA7219_DAC_FILTERS5,
- DA7219_DAC_SOFTMUTE_EN_MASK, 0);
- /* Enable HP left & right paths */
- snd_soc_update_bits(codec, DA7219_CP_CTRL, DA7219_CP_EN_MASK,
- DA7219_CP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_DIG_ROUTING_DAC,
- DA7219_DAC_L_SRC_MASK | DA7219_DAC_R_SRC_MASK,
- DA7219_DAC_L_SRC_TONEGEN |
- DA7219_DAC_R_SRC_TONEGEN);
- snd_soc_update_bits(codec, DA7219_DAC_L_CTRL,
- DA7219_DAC_L_EN_MASK | DA7219_DAC_L_MUTE_EN_MASK,
- DA7219_DAC_L_EN_MASK);
- snd_soc_update_bits(codec, DA7219_DAC_R_CTRL,
- DA7219_DAC_R_EN_MASK | DA7219_DAC_R_MUTE_EN_MASK,
- DA7219_DAC_R_EN_MASK);
- snd_soc_update_bits(codec, DA7219_MIXOUT_L_SELECT,
- DA7219_MIXOUT_L_MIX_SELECT_MASK,
- DA7219_MIXOUT_L_MIX_SELECT_MASK);
- snd_soc_update_bits(codec, DA7219_MIXOUT_R_SELECT,
- DA7219_MIXOUT_R_MIX_SELECT_MASK,
- DA7219_MIXOUT_R_MIX_SELECT_MASK);
- snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1L,
- DA7219_OUTFILT_ST_1L_SRC_MASK,
- DA7219_DMIX_ST_SRC_OUTFILT1L);
- snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1R,
- DA7219_OUTFILT_ST_1R_SRC_MASK,
- DA7219_DMIX_ST_SRC_OUTFILT1R);
- snd_soc_update_bits(codec, DA7219_MIXOUT_L_CTRL,
- DA7219_MIXOUT_L_AMP_EN_MASK,
- DA7219_MIXOUT_L_AMP_EN_MASK);
- snd_soc_update_bits(codec, DA7219_MIXOUT_R_CTRL,
- DA7219_MIXOUT_R_AMP_EN_MASK,
- DA7219_MIXOUT_R_AMP_EN_MASK);
- snd_soc_write(codec, DA7219_HP_L_CTRL,
- DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK);
- snd_soc_write(codec, DA7219_HP_R_CTRL,
- DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK);
- /*
- * If we're running from the internal oscillator then give audio paths
- * time to settle before running test.
- */
- if (!(pll_srm_sts & DA7219_PLL_SRM_STS_MCLK))
- msleep(DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY);
- /* Configure & start Tone Generator */
- snd_soc_write(codec, DA7219_TONE_GEN_ON_PER, DA7219_BEEP_ON_PER_MASK);
- regmap_raw_write(da7219->regmap, DA7219_TONE_GEN_FREQ1_L,
- &tonegen_freq_hptest, sizeof(tonegen_freq_hptest));
- snd_soc_update_bits(codec, DA7219_TONE_GEN_CFG2,
- DA7219_SWG_SEL_MASK | DA7219_TONE_GEN_GAIN_MASK,
- DA7219_SWG_SEL_SRAMP |
- DA7219_TONE_GEN_GAIN_MINUS_15DB);
- snd_soc_write(codec, DA7219_TONE_GEN_CFG1, DA7219_START_STOPN_MASK);
- msleep(DA7219_AAD_HPTEST_PERIOD);
- /* Grab comparator reading */
- accdet_cfg8 = snd_soc_read(codec, DA7219_ACCDET_CONFIG_8);
- if (accdet_cfg8 & DA7219_HPTEST_COMP_MASK)
- report |= SND_JACK_HEADPHONE;
- else
- report |= SND_JACK_LINEOUT;
- /* Stop tone generator */
- snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0);
- msleep(DA7219_AAD_HPTEST_PERIOD);
- /* Restore original settings from cache */
- regcache_mark_dirty(da7219->regmap);
- regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL,
- DA7219_HP_R_CTRL);
- regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL,
- DA7219_MIXOUT_R_CTRL);
- regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L,
- DA7219_DROUTING_ST_OUTFILT_1R);
- regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_SELECT,
- DA7219_MIXOUT_R_SELECT);
- regcache_sync_region(da7219->regmap, DA7219_DAC_L_CTRL,
- DA7219_DAC_R_CTRL);
- regcache_sync_region(da7219->regmap, DA7219_DIG_ROUTING_DAC,
- DA7219_DIG_ROUTING_DAC);
- regcache_sync_region(da7219->regmap, DA7219_CP_CTRL, DA7219_CP_CTRL);
- regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS5,
- DA7219_DAC_FILTERS5);
- regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS4,
- DA7219_DAC_FILTERS1);
- regcache_sync_region(da7219->regmap, DA7219_HP_L_GAIN,
- DA7219_HP_R_GAIN);
- regcache_sync_region(da7219->regmap, DA7219_DAC_L_GAIN,
- DA7219_DAC_R_GAIN);
- regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_ON_PER,
- DA7219_TONE_GEN_ON_PER);
- regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_FREQ1_L,
- DA7219_TONE_GEN_FREQ1_U);
- regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_CFG1,
- DA7219_TONE_GEN_CFG2);
- regcache_cache_bypass(da7219->regmap, false);
- /* Disable HPTest block */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8,
- DA7219_HPTEST_EN_MASK, 0);
- /*
- * If we're running from the internal oscillator then give audio paths
- * time to settle before allowing headphones to be driven as required.
- */
- if (!(pll_srm_sts & DA7219_PLL_SRM_STS_MCLK))
- msleep(DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY);
- /* Restore gain ramping rate */
- snd_soc_write(codec, DA7219_GAIN_RAMP_CTRL, gain_ramp_ctrl);
- /* Drive Headphones/lineout */
- snd_soc_update_bits(codec, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK,
- DA7219_HP_L_AMP_OE_MASK);
- snd_soc_update_bits(codec, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK,
- DA7219_HP_R_AMP_OE_MASK);
- /* Remove MCLK, if previously enabled */
- if (da7219->mclk)
- clk_disable_unprepare(da7219->mclk);
- mutex_unlock(&da7219->lock);
- snd_soc_dapm_mutex_unlock(dapm);
- /*
- * Only send report if jack hasn't been removed during process,
- * otherwise it's invalid and we drop it.
- */
- if (da7219_aad->jack_inserted)
- snd_soc_jack_report(da7219_aad->jack, report,
- SND_JACK_HEADSET | SND_JACK_LINEOUT);
- }
- /*
- * IRQ
- */
- static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
- {
- struct da7219_aad_priv *da7219_aad = data;
- struct snd_soc_codec *codec = da7219_aad->codec;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- u8 events[DA7219_AAD_IRQ_REG_MAX];
- u8 statusa;
- int i, report = 0, mask = 0;
- /* Read current IRQ events */
- regmap_bulk_read(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
- events, DA7219_AAD_IRQ_REG_MAX);
- if (!events[DA7219_AAD_IRQ_REG_A] && !events[DA7219_AAD_IRQ_REG_B])
- return IRQ_NONE;
- /* Read status register for jack insertion & type status */
- statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A);
- /* Clear events */
- regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
- events, DA7219_AAD_IRQ_REG_MAX);
- dev_dbg(codec->dev, "IRQ events = 0x%x|0x%x, status = 0x%x\n",
- events[DA7219_AAD_IRQ_REG_A], events[DA7219_AAD_IRQ_REG_B],
- statusa);
- if (statusa & DA7219_JACK_INSERTION_STS_MASK) {
- /* Jack Insertion */
- if (events[DA7219_AAD_IRQ_REG_A] &
- DA7219_E_JACK_INSERTED_MASK) {
- report |= SND_JACK_MECHANICAL;
- mask |= SND_JACK_MECHANICAL;
- da7219_aad->jack_inserted = true;
- }
- /* Jack type detection */
- if (events[DA7219_AAD_IRQ_REG_A] &
- DA7219_E_JACK_DETECT_COMPLETE_MASK) {
- /*
- * If 4-pole, then enable button detection, else perform
- * HP impedance test to determine output type to report.
- *
- * We schedule work here as the tasks themselves can
- * take time to complete, and in particular for hptest
- * we want to be able to check if the jack was removed
- * during the procedure as this will invalidate the
- * result. By doing this as work, the IRQ thread can
- * handle a removal, and we can check at the end of
- * hptest if we have a valid result or not.
- */
- if (statusa & DA7219_JACK_TYPE_STS_MASK) {
- report |= SND_JACK_HEADSET;
- mask |= SND_JACK_HEADSET | SND_JACK_LINEOUT;
- schedule_work(&da7219_aad->btn_det_work);
- } else {
- schedule_work(&da7219_aad->hptest_work);
- }
- }
- /* Button support for 4-pole jack */
- if (statusa & DA7219_JACK_TYPE_STS_MASK) {
- for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) {
- /* Button Press */
- if (events[DA7219_AAD_IRQ_REG_B] &
- (DA7219_E_BUTTON_A_PRESSED_MASK << i)) {
- report |= SND_JACK_BTN_0 >> i;
- mask |= SND_JACK_BTN_0 >> i;
- }
- }
- snd_soc_jack_report(da7219_aad->jack, report, mask);
- for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) {
- /* Button Release */
- if (events[DA7219_AAD_IRQ_REG_B] &
- (DA7219_E_BUTTON_A_RELEASED_MASK >> i)) {
- report &= ~(SND_JACK_BTN_0 >> i);
- mask |= SND_JACK_BTN_0 >> i;
- }
- }
- }
- } else {
- /* Jack removal */
- if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_REMOVED_MASK) {
- report = 0;
- mask |= DA7219_AAD_REPORT_ALL_MASK;
- da7219_aad->jack_inserted = false;
- /* Un-drive headphones/lineout */
- snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
- DA7219_HP_R_AMP_OE_MASK, 0);
- snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
- DA7219_HP_L_AMP_OE_MASK, 0);
- /* Ensure button detection disabled */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
- DA7219_BUTTON_CONFIG_MASK, 0);
- /* Disable mic bias */
- snd_soc_dapm_disable_pin(dapm, "Mic Bias");
- snd_soc_dapm_sync(dapm);
- /* Cancel any pending work */
- cancel_work_sync(&da7219_aad->btn_det_work);
- cancel_work_sync(&da7219_aad->hptest_work);
- }
- }
- snd_soc_jack_report(da7219_aad->jack, report, mask);
- return IRQ_HANDLED;
- }
- /*
- * DT/ACPI to pdata conversion
- */
- static enum da7219_aad_micbias_pulse_lvl
- da7219_aad_fw_micbias_pulse_lvl(struct snd_soc_codec *codec, u32 val)
- {
- switch (val) {
- case 2800:
- return DA7219_AAD_MICBIAS_PULSE_LVL_2_8V;
- case 2900:
- return DA7219_AAD_MICBIAS_PULSE_LVL_2_9V;
- default:
- dev_warn(codec->dev, "Invalid micbias pulse level");
- return DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
- }
- }
- static enum da7219_aad_btn_cfg
- da7219_aad_fw_btn_cfg(struct snd_soc_codec *codec, u32 val)
- {
- switch (val) {
- case 2:
- return DA7219_AAD_BTN_CFG_2MS;
- case 5:
- return DA7219_AAD_BTN_CFG_5MS;
- case 10:
- return DA7219_AAD_BTN_CFG_10MS;
- case 50:
- return DA7219_AAD_BTN_CFG_50MS;
- case 100:
- return DA7219_AAD_BTN_CFG_100MS;
- case 200:
- return DA7219_AAD_BTN_CFG_200MS;
- case 500:
- return DA7219_AAD_BTN_CFG_500MS;
- default:
- dev_warn(codec->dev, "Invalid button config");
- return DA7219_AAD_BTN_CFG_10MS;
- }
- }
- static enum da7219_aad_mic_det_thr
- da7219_aad_fw_mic_det_thr(struct snd_soc_codec *codec, u32 val)
- {
- switch (val) {
- case 200:
- return DA7219_AAD_MIC_DET_THR_200_OHMS;
- case 500:
- return DA7219_AAD_MIC_DET_THR_500_OHMS;
- case 750:
- return DA7219_AAD_MIC_DET_THR_750_OHMS;
- case 1000:
- return DA7219_AAD_MIC_DET_THR_1000_OHMS;
- default:
- dev_warn(codec->dev, "Invalid mic detect threshold");
- return DA7219_AAD_MIC_DET_THR_500_OHMS;
- }
- }
- static enum da7219_aad_jack_ins_deb
- da7219_aad_fw_jack_ins_deb(struct snd_soc_codec *codec, u32 val)
- {
- switch (val) {
- case 5:
- return DA7219_AAD_JACK_INS_DEB_5MS;
- case 10:
- return DA7219_AAD_JACK_INS_DEB_10MS;
- case 20:
- return DA7219_AAD_JACK_INS_DEB_20MS;
- case 50:
- return DA7219_AAD_JACK_INS_DEB_50MS;
- case 100:
- return DA7219_AAD_JACK_INS_DEB_100MS;
- case 200:
- return DA7219_AAD_JACK_INS_DEB_200MS;
- case 500:
- return DA7219_AAD_JACK_INS_DEB_500MS;
- case 1000:
- return DA7219_AAD_JACK_INS_DEB_1S;
- default:
- dev_warn(codec->dev, "Invalid jack insert debounce");
- return DA7219_AAD_JACK_INS_DEB_20MS;
- }
- }
- static enum da7219_aad_jack_det_rate
- da7219_aad_fw_jack_det_rate(struct snd_soc_codec *codec, const char *str)
- {
- if (!strcmp(str, "32ms_64ms")) {
- return DA7219_AAD_JACK_DET_RATE_32_64MS;
- } else if (!strcmp(str, "64ms_128ms")) {
- return DA7219_AAD_JACK_DET_RATE_64_128MS;
- } else if (!strcmp(str, "128ms_256ms")) {
- return DA7219_AAD_JACK_DET_RATE_128_256MS;
- } else if (!strcmp(str, "256ms_512ms")) {
- return DA7219_AAD_JACK_DET_RATE_256_512MS;
- } else {
- dev_warn(codec->dev, "Invalid jack detect rate");
- return DA7219_AAD_JACK_DET_RATE_256_512MS;
- }
- }
- static enum da7219_aad_jack_rem_deb
- da7219_aad_fw_jack_rem_deb(struct snd_soc_codec *codec, u32 val)
- {
- switch (val) {
- case 1:
- return DA7219_AAD_JACK_REM_DEB_1MS;
- case 5:
- return DA7219_AAD_JACK_REM_DEB_5MS;
- case 10:
- return DA7219_AAD_JACK_REM_DEB_10MS;
- case 20:
- return DA7219_AAD_JACK_REM_DEB_20MS;
- default:
- dev_warn(codec->dev, "Invalid jack removal debounce");
- return DA7219_AAD_JACK_REM_DEB_1MS;
- }
- }
- static enum da7219_aad_btn_avg
- da7219_aad_fw_btn_avg(struct snd_soc_codec *codec, u32 val)
- {
- switch (val) {
- case 1:
- return DA7219_AAD_BTN_AVG_1;
- case 2:
- return DA7219_AAD_BTN_AVG_2;
- case 4:
- return DA7219_AAD_BTN_AVG_4;
- case 8:
- return DA7219_AAD_BTN_AVG_8;
- default:
- dev_warn(codec->dev, "Invalid button average value");
- return DA7219_AAD_BTN_AVG_2;
- }
- }
- static enum da7219_aad_adc_1bit_rpt
- da7219_aad_fw_adc_1bit_rpt(struct snd_soc_codec *codec, u32 val)
- {
- switch (val) {
- case 1:
- return DA7219_AAD_ADC_1BIT_RPT_1;
- case 2:
- return DA7219_AAD_ADC_1BIT_RPT_2;
- case 4:
- return DA7219_AAD_ADC_1BIT_RPT_4;
- case 8:
- return DA7219_AAD_ADC_1BIT_RPT_8;
- default:
- dev_warn(codec->dev, "Invalid ADC 1-bit repeat value");
- return DA7219_AAD_ADC_1BIT_RPT_1;
- }
- }
- static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct snd_soc_codec *codec)
- {
- struct device *dev = codec->dev;
- struct i2c_client *i2c = to_i2c_client(dev);
- struct fwnode_handle *aad_np;
- struct da7219_aad_pdata *aad_pdata;
- const char *fw_str;
- u32 fw_val32;
- aad_np = device_get_named_child_node(dev, "da7219_aad");
- if (!aad_np)
- return NULL;
- aad_pdata = devm_kzalloc(codec->dev, sizeof(*aad_pdata), GFP_KERNEL);
- if (!aad_pdata)
- return NULL;
- aad_pdata->irq = i2c->irq;
- if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-lvl",
- &fw_val32) >= 0)
- aad_pdata->micbias_pulse_lvl =
- da7219_aad_fw_micbias_pulse_lvl(codec, fw_val32);
- else
- aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
- if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-time",
- &fw_val32) >= 0)
- aad_pdata->micbias_pulse_time = fw_val32;
- if (fwnode_property_read_u32(aad_np, "dlg,btn-cfg", &fw_val32) >= 0)
- aad_pdata->btn_cfg = da7219_aad_fw_btn_cfg(codec, fw_val32);
- else
- aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS;
- if (fwnode_property_read_u32(aad_np, "dlg,mic-det-thr", &fw_val32) >= 0)
- aad_pdata->mic_det_thr =
- da7219_aad_fw_mic_det_thr(codec, fw_val32);
- else
- aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS;
- if (fwnode_property_read_u32(aad_np, "dlg,jack-ins-deb", &fw_val32) >= 0)
- aad_pdata->jack_ins_deb =
- da7219_aad_fw_jack_ins_deb(codec, fw_val32);
- else
- aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS;
- if (!fwnode_property_read_string(aad_np, "dlg,jack-det-rate", &fw_str))
- aad_pdata->jack_det_rate =
- da7219_aad_fw_jack_det_rate(codec, fw_str);
- else
- aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS;
- if (fwnode_property_read_u32(aad_np, "dlg,jack-rem-deb", &fw_val32) >= 0)
- aad_pdata->jack_rem_deb =
- da7219_aad_fw_jack_rem_deb(codec, fw_val32);
- else
- aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS;
- if (fwnode_property_read_u32(aad_np, "dlg,a-d-btn-thr", &fw_val32) >= 0)
- aad_pdata->a_d_btn_thr = (u8) fw_val32;
- else
- aad_pdata->a_d_btn_thr = 0xA;
- if (fwnode_property_read_u32(aad_np, "dlg,d-b-btn-thr", &fw_val32) >= 0)
- aad_pdata->d_b_btn_thr = (u8) fw_val32;
- else
- aad_pdata->d_b_btn_thr = 0x16;
- if (fwnode_property_read_u32(aad_np, "dlg,b-c-btn-thr", &fw_val32) >= 0)
- aad_pdata->b_c_btn_thr = (u8) fw_val32;
- else
- aad_pdata->b_c_btn_thr = 0x21;
- if (fwnode_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &fw_val32) >= 0)
- aad_pdata->c_mic_btn_thr = (u8) fw_val32;
- else
- aad_pdata->c_mic_btn_thr = 0x3E;
- if (fwnode_property_read_u32(aad_np, "dlg,btn-avg", &fw_val32) >= 0)
- aad_pdata->btn_avg = da7219_aad_fw_btn_avg(codec, fw_val32);
- else
- aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2;
- if (fwnode_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &fw_val32) >= 0)
- aad_pdata->adc_1bit_rpt =
- da7219_aad_fw_adc_1bit_rpt(codec, fw_val32);
- else
- aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1;
- return aad_pdata;
- }
- static void da7219_aad_handle_pdata(struct snd_soc_codec *codec)
- {
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- struct da7219_aad_priv *da7219_aad = da7219->aad;
- struct da7219_pdata *pdata = da7219->pdata;
- if ((pdata) && (pdata->aad_pdata)) {
- struct da7219_aad_pdata *aad_pdata = pdata->aad_pdata;
- u8 cfg, mask;
- da7219_aad->irq = aad_pdata->irq;
- switch (aad_pdata->micbias_pulse_lvl) {
- case DA7219_AAD_MICBIAS_PULSE_LVL_2_8V:
- case DA7219_AAD_MICBIAS_PULSE_LVL_2_9V:
- da7219_aad->micbias_pulse_lvl =
- (aad_pdata->micbias_pulse_lvl <<
- DA7219_MICBIAS1_LEVEL_SHIFT);
- break;
- default:
- break;
- }
- da7219_aad->micbias_pulse_time = aad_pdata->micbias_pulse_time;
- switch (aad_pdata->btn_cfg) {
- case DA7219_AAD_BTN_CFG_2MS:
- case DA7219_AAD_BTN_CFG_5MS:
- case DA7219_AAD_BTN_CFG_10MS:
- case DA7219_AAD_BTN_CFG_50MS:
- case DA7219_AAD_BTN_CFG_100MS:
- case DA7219_AAD_BTN_CFG_200MS:
- case DA7219_AAD_BTN_CFG_500MS:
- da7219_aad->btn_cfg = (aad_pdata->btn_cfg <<
- DA7219_BUTTON_CONFIG_SHIFT);
- }
- cfg = 0;
- mask = 0;
- switch (aad_pdata->mic_det_thr) {
- case DA7219_AAD_MIC_DET_THR_200_OHMS:
- case DA7219_AAD_MIC_DET_THR_500_OHMS:
- case DA7219_AAD_MIC_DET_THR_750_OHMS:
- case DA7219_AAD_MIC_DET_THR_1000_OHMS:
- cfg |= (aad_pdata->mic_det_thr <<
- DA7219_MIC_DET_THRESH_SHIFT);
- mask |= DA7219_MIC_DET_THRESH_MASK;
- }
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, mask, cfg);
- cfg = 0;
- mask = 0;
- switch (aad_pdata->jack_ins_deb) {
- case DA7219_AAD_JACK_INS_DEB_5MS:
- case DA7219_AAD_JACK_INS_DEB_10MS:
- case DA7219_AAD_JACK_INS_DEB_20MS:
- case DA7219_AAD_JACK_INS_DEB_50MS:
- case DA7219_AAD_JACK_INS_DEB_100MS:
- case DA7219_AAD_JACK_INS_DEB_200MS:
- case DA7219_AAD_JACK_INS_DEB_500MS:
- case DA7219_AAD_JACK_INS_DEB_1S:
- cfg |= (aad_pdata->jack_ins_deb <<
- DA7219_JACKDET_DEBOUNCE_SHIFT);
- mask |= DA7219_JACKDET_DEBOUNCE_MASK;
- }
- switch (aad_pdata->jack_det_rate) {
- case DA7219_AAD_JACK_DET_RATE_32_64MS:
- case DA7219_AAD_JACK_DET_RATE_64_128MS:
- case DA7219_AAD_JACK_DET_RATE_128_256MS:
- case DA7219_AAD_JACK_DET_RATE_256_512MS:
- cfg |= (aad_pdata->jack_det_rate <<
- DA7219_JACK_DETECT_RATE_SHIFT);
- mask |= DA7219_JACK_DETECT_RATE_MASK;
- }
- switch (aad_pdata->jack_rem_deb) {
- case DA7219_AAD_JACK_REM_DEB_1MS:
- case DA7219_AAD_JACK_REM_DEB_5MS:
- case DA7219_AAD_JACK_REM_DEB_10MS:
- case DA7219_AAD_JACK_REM_DEB_20MS:
- cfg |= (aad_pdata->jack_rem_deb <<
- DA7219_JACKDET_REM_DEB_SHIFT);
- mask |= DA7219_JACKDET_REM_DEB_MASK;
- }
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_2, mask, cfg);
- snd_soc_write(codec, DA7219_ACCDET_CONFIG_3,
- aad_pdata->a_d_btn_thr);
- snd_soc_write(codec, DA7219_ACCDET_CONFIG_4,
- aad_pdata->d_b_btn_thr);
- snd_soc_write(codec, DA7219_ACCDET_CONFIG_5,
- aad_pdata->b_c_btn_thr);
- snd_soc_write(codec, DA7219_ACCDET_CONFIG_6,
- aad_pdata->c_mic_btn_thr);
- cfg = 0;
- mask = 0;
- switch (aad_pdata->btn_avg) {
- case DA7219_AAD_BTN_AVG_1:
- case DA7219_AAD_BTN_AVG_2:
- case DA7219_AAD_BTN_AVG_4:
- case DA7219_AAD_BTN_AVG_8:
- cfg |= (aad_pdata->btn_avg <<
- DA7219_BUTTON_AVERAGE_SHIFT);
- mask |= DA7219_BUTTON_AVERAGE_MASK;
- }
- switch (aad_pdata->adc_1bit_rpt) {
- case DA7219_AAD_ADC_1BIT_RPT_1:
- case DA7219_AAD_ADC_1BIT_RPT_2:
- case DA7219_AAD_ADC_1BIT_RPT_4:
- case DA7219_AAD_ADC_1BIT_RPT_8:
- cfg |= (aad_pdata->adc_1bit_rpt <<
- DA7219_ADC_1_BIT_REPEAT_SHIFT);
- mask |= DA7219_ADC_1_BIT_REPEAT_MASK;
- }
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_7, mask, cfg);
- }
- }
- /*
- * Suspend/Resume
- */
- void da7219_aad_suspend(struct snd_soc_codec *codec)
- {
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- struct da7219_aad_priv *da7219_aad = da7219->aad;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- u8 micbias_ctrl;
- if (da7219_aad->jack) {
- /* Disable jack detection during suspend */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
- DA7219_ACCDET_EN_MASK, 0);
- /*
- * If we have a 4-pole jack inserted, then micbias will be
- * enabled. We can disable micbias here, and keep a note to
- * re-enable it on resume. If jack removal occurred during
- * suspend then this will be dealt with through the IRQ handler.
- */
- if (da7219_aad->jack_inserted) {
- micbias_ctrl = snd_soc_read(codec, DA7219_MICBIAS_CTRL);
- if (micbias_ctrl & DA7219_MICBIAS1_EN_MASK) {
- snd_soc_dapm_disable_pin(dapm, "Mic Bias");
- snd_soc_dapm_sync(dapm);
- da7219_aad->micbias_resume_enable = true;
- }
- }
- }
- }
- void da7219_aad_resume(struct snd_soc_codec *codec)
- {
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- struct da7219_aad_priv *da7219_aad = da7219->aad;
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
- if (da7219_aad->jack) {
- /* Re-enable micbias if previously enabled for 4-pole jack */
- if (da7219_aad->jack_inserted &&
- da7219_aad->micbias_resume_enable) {
- snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
- snd_soc_dapm_sync(dapm);
- da7219_aad->micbias_resume_enable = false;
- }
- /* Re-enable jack detection */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
- DA7219_ACCDET_EN_MASK,
- DA7219_ACCDET_EN_MASK);
- }
- }
- /*
- * Init/Exit
- */
- int da7219_aad_init(struct snd_soc_codec *codec)
- {
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- struct da7219_aad_priv *da7219_aad;
- u8 mask[DA7219_AAD_IRQ_REG_MAX];
- int ret;
- da7219_aad = devm_kzalloc(codec->dev, sizeof(*da7219_aad), GFP_KERNEL);
- if (!da7219_aad)
- return -ENOMEM;
- da7219->aad = da7219_aad;
- da7219_aad->codec = codec;
- /* Handle any DT/ACPI/platform data */
- if (da7219->pdata && !da7219->pdata->aad_pdata)
- da7219->pdata->aad_pdata = da7219_aad_fw_to_pdata(codec);
- da7219_aad_handle_pdata(codec);
- /* Disable button detection */
- snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
- DA7219_BUTTON_CONFIG_MASK, 0);
- INIT_WORK(&da7219_aad->btn_det_work, da7219_aad_btn_det_work);
- INIT_WORK(&da7219_aad->hptest_work, da7219_aad_hptest_work);
- ret = request_threaded_irq(da7219_aad->irq, NULL,
- da7219_aad_irq_thread,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- "da7219-aad", da7219_aad);
- if (ret) {
- dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
- return ret;
- }
- /* Unmask AAD IRQs */
- memset(mask, 0, DA7219_AAD_IRQ_REG_MAX);
- regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A,
- &mask, DA7219_AAD_IRQ_REG_MAX);
- return 0;
- }
- EXPORT_SYMBOL_GPL(da7219_aad_init);
- void da7219_aad_exit(struct snd_soc_codec *codec)
- {
- struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- struct da7219_aad_priv *da7219_aad = da7219->aad;
- u8 mask[DA7219_AAD_IRQ_REG_MAX];
- /* Mask off AAD IRQs */
- memset(mask, DA7219_BYTE_MASK, DA7219_AAD_IRQ_REG_MAX);
- regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A,
- mask, DA7219_AAD_IRQ_REG_MAX);
- free_irq(da7219_aad->irq, da7219_aad);
- cancel_work_sync(&da7219_aad->btn_det_work);
- cancel_work_sync(&da7219_aad->hptest_work);
- }
- EXPORT_SYMBOL_GPL(da7219_aad_exit);
- MODULE_DESCRIPTION("ASoC DA7219 AAD Driver");
- MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
- MODULE_LICENSE("GPL");
|