cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

da7219-aad.c (30864B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * da7219-aad.c - Dialog DA7219 ALSA SoC AAD Driver
      4 *
      5 * Copyright (c) 2015 Dialog Semiconductor Ltd.
      6 *
      7 * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
      8 */
      9
     10#include <linux/module.h>
     11#include <linux/platform_device.h>
     12#include <linux/clk.h>
     13#include <linux/i2c.h>
     14#include <linux/property.h>
     15#include <linux/pm_wakeirq.h>
     16#include <linux/slab.h>
     17#include <linux/delay.h>
     18#include <linux/workqueue.h>
     19#include <sound/soc.h>
     20#include <sound/jack.h>
     21#include <sound/da7219.h>
     22
     23#include "da7219.h"
     24#include "da7219-aad.h"
     25
     26
     27/*
     28 * Detection control
     29 */
     30
     31void da7219_aad_jack_det(struct snd_soc_component *component, struct snd_soc_jack *jack)
     32{
     33	struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
     34
     35	da7219->aad->jack = jack;
     36	da7219->aad->jack_inserted = false;
     37
     38	/* Send an initial empty report */
     39	snd_soc_jack_report(jack, 0, DA7219_AAD_REPORT_ALL_MASK);
     40
     41	/* Enable/Disable jack detection */
     42	snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
     43			    DA7219_ACCDET_EN_MASK,
     44			    (jack ? DA7219_ACCDET_EN_MASK : 0));
     45}
     46EXPORT_SYMBOL_GPL(da7219_aad_jack_det);
     47
     48/*
     49 * Button/HPTest work
     50 */
     51
     52static void da7219_aad_btn_det_work(struct work_struct *work)
     53{
     54	struct da7219_aad_priv *da7219_aad =
     55		container_of(work, struct da7219_aad_priv, btn_det_work);
     56	struct snd_soc_component *component = da7219_aad->component;
     57	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
     58	struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
     59	u8 statusa, micbias_ctrl;
     60	bool micbias_up = false;
     61	int retries = 0;
     62
     63	/* Disable ground switch */
     64	snd_soc_component_update_bits(component, 0xFB, 0x01, 0x00);
     65
     66	/* Drive headphones/lineout */
     67	snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
     68			    DA7219_HP_L_AMP_OE_MASK,
     69			    DA7219_HP_L_AMP_OE_MASK);
     70	snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
     71			    DA7219_HP_R_AMP_OE_MASK,
     72			    DA7219_HP_R_AMP_OE_MASK);
     73
     74	/* Make sure mic bias is up */
     75	snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
     76	snd_soc_dapm_sync(dapm);
     77
     78	do {
     79		statusa = snd_soc_component_read(component, DA7219_ACCDET_STATUS_A);
     80		if (statusa & DA7219_MICBIAS_UP_STS_MASK)
     81			micbias_up = true;
     82		else if (retries++ < DA7219_AAD_MICBIAS_CHK_RETRIES)
     83			msleep(DA7219_AAD_MICBIAS_CHK_DELAY);
     84	} while ((!micbias_up) && (retries < DA7219_AAD_MICBIAS_CHK_RETRIES));
     85
     86	if (retries >= DA7219_AAD_MICBIAS_CHK_RETRIES)
     87		dev_warn(component->dev, "Mic bias status check timed out");
     88
     89	da7219->micbias_on_event = true;
     90
     91	/*
     92	 * Mic bias pulse required to enable mic, must be done before enabling
     93	 * button detection to prevent erroneous button readings.
     94	 */
     95	if (da7219_aad->micbias_pulse_lvl && da7219_aad->micbias_pulse_time) {
     96		/* Pulse higher level voltage */
     97		micbias_ctrl = snd_soc_component_read(component, DA7219_MICBIAS_CTRL);
     98		snd_soc_component_update_bits(component, DA7219_MICBIAS_CTRL,
     99				    DA7219_MICBIAS1_LEVEL_MASK,
    100				    da7219_aad->micbias_pulse_lvl);
    101		msleep(da7219_aad->micbias_pulse_time);
    102		snd_soc_component_write(component, DA7219_MICBIAS_CTRL, micbias_ctrl);
    103
    104	}
    105
    106	snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
    107			    DA7219_BUTTON_CONFIG_MASK,
    108			    da7219_aad->btn_cfg);
    109}
    110
    111static void da7219_aad_hptest_work(struct work_struct *work)
    112{
    113	struct da7219_aad_priv *da7219_aad =
    114		container_of(work, struct da7219_aad_priv, hptest_work);
    115	struct snd_soc_component *component = da7219_aad->component;
    116	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
    117	struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
    118
    119	__le16 tonegen_freq_hptest;
    120	u8 pll_srm_sts, pll_ctrl, gain_ramp_ctrl, accdet_cfg8;
    121	int report = 0, ret;
    122
    123	/* Lock DAPM, Kcontrols affected by this test and the PLL */
    124	snd_soc_dapm_mutex_lock(dapm);
    125	mutex_lock(&da7219->ctrl_lock);
    126	mutex_lock(&da7219->pll_lock);
    127
    128	/* Ensure MCLK is available for HP test procedure */
    129	if (da7219->mclk) {
    130		ret = clk_prepare_enable(da7219->mclk);
    131		if (ret) {
    132			dev_err(component->dev, "Failed to enable mclk - %d\n", ret);
    133			mutex_unlock(&da7219->pll_lock);
    134			mutex_unlock(&da7219->ctrl_lock);
    135			snd_soc_dapm_mutex_unlock(dapm);
    136			return;
    137		}
    138	}
    139
    140	/*
    141	 * If MCLK not present, then we're using the internal oscillator and
    142	 * require different frequency settings to achieve the same result.
    143	 *
    144	 * If MCLK is present, but PLL is not enabled then we enable it here to
    145	 * ensure a consistent detection procedure.
    146	 */
    147	pll_srm_sts = snd_soc_component_read(component, DA7219_PLL_SRM_STS);
    148	if (pll_srm_sts & DA7219_PLL_SRM_STS_MCLK) {
    149		tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ);
    150
    151		pll_ctrl = snd_soc_component_read(component, DA7219_PLL_CTRL);
    152		if ((pll_ctrl & DA7219_PLL_MODE_MASK) == DA7219_PLL_MODE_BYPASS)
    153			da7219_set_pll(component, DA7219_SYSCLK_PLL,
    154				       DA7219_PLL_FREQ_OUT_98304);
    155	} else {
    156		tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ_INT_OSC);
    157	}
    158
    159	/* Disable ground switch */
    160	snd_soc_component_update_bits(component, 0xFB, 0x01, 0x00);
    161
    162	/* Ensure gain ramping at fastest rate */
    163	gain_ramp_ctrl = snd_soc_component_read(component, DA7219_GAIN_RAMP_CTRL);
    164	snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_X8);
    165
    166	/* Bypass cache so it saves current settings */
    167	regcache_cache_bypass(da7219->regmap, true);
    168
    169	/* Make sure Tone Generator is disabled */
    170	snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, 0);
    171
    172	/* Enable HPTest block, 1KOhms check */
    173	snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_8,
    174			    DA7219_HPTEST_EN_MASK | DA7219_HPTEST_RES_SEL_MASK,
    175			    DA7219_HPTEST_EN_MASK |
    176			    DA7219_HPTEST_RES_SEL_1KOHMS);
    177
    178	/* Set gains to 0db */
    179	snd_soc_component_write(component, DA7219_DAC_L_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
    180	snd_soc_component_write(component, DA7219_DAC_R_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
    181	snd_soc_component_write(component, DA7219_HP_L_GAIN, DA7219_HP_AMP_GAIN_0DB);
    182	snd_soc_component_write(component, DA7219_HP_R_GAIN, DA7219_HP_AMP_GAIN_0DB);
    183
    184	/* Disable DAC filters, EQs and soft mute */
    185	snd_soc_component_update_bits(component, DA7219_DAC_FILTERS1, DA7219_HPF_MODE_MASK,
    186			    0);
    187	snd_soc_component_update_bits(component, DA7219_DAC_FILTERS4, DA7219_DAC_EQ_EN_MASK,
    188			    0);
    189	snd_soc_component_update_bits(component, DA7219_DAC_FILTERS5,
    190			    DA7219_DAC_SOFTMUTE_EN_MASK, 0);
    191
    192	/* Enable HP left & right paths */
    193	snd_soc_component_update_bits(component, DA7219_CP_CTRL, DA7219_CP_EN_MASK,
    194			    DA7219_CP_EN_MASK);
    195	snd_soc_component_update_bits(component, DA7219_DIG_ROUTING_DAC,
    196			    DA7219_DAC_L_SRC_MASK | DA7219_DAC_R_SRC_MASK,
    197			    DA7219_DAC_L_SRC_TONEGEN |
    198			    DA7219_DAC_R_SRC_TONEGEN);
    199	snd_soc_component_update_bits(component, DA7219_DAC_L_CTRL,
    200			    DA7219_DAC_L_EN_MASK | DA7219_DAC_L_MUTE_EN_MASK,
    201			    DA7219_DAC_L_EN_MASK);
    202	snd_soc_component_update_bits(component, DA7219_DAC_R_CTRL,
    203			    DA7219_DAC_R_EN_MASK | DA7219_DAC_R_MUTE_EN_MASK,
    204			    DA7219_DAC_R_EN_MASK);
    205	snd_soc_component_update_bits(component, DA7219_MIXOUT_L_SELECT,
    206			    DA7219_MIXOUT_L_MIX_SELECT_MASK,
    207			    DA7219_MIXOUT_L_MIX_SELECT_MASK);
    208	snd_soc_component_update_bits(component, DA7219_MIXOUT_R_SELECT,
    209			    DA7219_MIXOUT_R_MIX_SELECT_MASK,
    210			    DA7219_MIXOUT_R_MIX_SELECT_MASK);
    211	snd_soc_component_update_bits(component, DA7219_DROUTING_ST_OUTFILT_1L,
    212			    DA7219_OUTFILT_ST_1L_SRC_MASK,
    213			    DA7219_DMIX_ST_SRC_OUTFILT1L);
    214	snd_soc_component_update_bits(component, DA7219_DROUTING_ST_OUTFILT_1R,
    215			    DA7219_OUTFILT_ST_1R_SRC_MASK,
    216			    DA7219_DMIX_ST_SRC_OUTFILT1R);
    217	snd_soc_component_update_bits(component, DA7219_MIXOUT_L_CTRL,
    218			    DA7219_MIXOUT_L_AMP_EN_MASK,
    219			    DA7219_MIXOUT_L_AMP_EN_MASK);
    220	snd_soc_component_update_bits(component, DA7219_MIXOUT_R_CTRL,
    221			    DA7219_MIXOUT_R_AMP_EN_MASK,
    222			    DA7219_MIXOUT_R_AMP_EN_MASK);
    223	snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
    224			    DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK,
    225			    DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK);
    226	snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
    227			    DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK,
    228			    DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK);
    229	msleep(DA7219_SETTLING_DELAY);
    230	snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
    231			    DA7219_HP_L_AMP_MUTE_EN_MASK |
    232			    DA7219_HP_L_AMP_MIN_GAIN_EN_MASK, 0);
    233	snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
    234			    DA7219_HP_R_AMP_MUTE_EN_MASK |
    235			    DA7219_HP_R_AMP_MIN_GAIN_EN_MASK, 0);
    236
    237	/*
    238	 * If we're running from the internal oscillator then give audio paths
    239	 * time to settle before running test.
    240	 */
    241	if (!(pll_srm_sts & DA7219_PLL_SRM_STS_MCLK))
    242		msleep(DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY);
    243
    244	/* Configure & start Tone Generator */
    245	snd_soc_component_write(component, DA7219_TONE_GEN_ON_PER, DA7219_BEEP_ON_PER_MASK);
    246	regmap_raw_write(da7219->regmap, DA7219_TONE_GEN_FREQ1_L,
    247			 &tonegen_freq_hptest, sizeof(tonegen_freq_hptest));
    248	snd_soc_component_update_bits(component, DA7219_TONE_GEN_CFG2,
    249			    DA7219_SWG_SEL_MASK | DA7219_TONE_GEN_GAIN_MASK,
    250			    DA7219_SWG_SEL_SRAMP |
    251			    DA7219_TONE_GEN_GAIN_MINUS_15DB);
    252	snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, DA7219_START_STOPN_MASK);
    253
    254	msleep(DA7219_AAD_HPTEST_PERIOD);
    255
    256	/* Grab comparator reading */
    257	accdet_cfg8 = snd_soc_component_read(component, DA7219_ACCDET_CONFIG_8);
    258	if (accdet_cfg8 & DA7219_HPTEST_COMP_MASK)
    259		report |= SND_JACK_HEADPHONE;
    260	else
    261		report |= SND_JACK_LINEOUT;
    262
    263	/* Stop tone generator */
    264	snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, 0);
    265
    266	msleep(DA7219_AAD_HPTEST_PERIOD);
    267
    268	/* Restore original settings from cache */
    269	regcache_mark_dirty(da7219->regmap);
    270	regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL,
    271			     DA7219_HP_R_CTRL);
    272	msleep(DA7219_SETTLING_DELAY);
    273	regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL,
    274			     DA7219_MIXOUT_R_CTRL);
    275	regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L,
    276			     DA7219_DROUTING_ST_OUTFILT_1R);
    277	regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_SELECT,
    278			     DA7219_MIXOUT_R_SELECT);
    279	regcache_sync_region(da7219->regmap, DA7219_DAC_L_CTRL,
    280			     DA7219_DAC_R_CTRL);
    281	regcache_sync_region(da7219->regmap, DA7219_DIG_ROUTING_DAC,
    282			     DA7219_DIG_ROUTING_DAC);
    283	regcache_sync_region(da7219->regmap, DA7219_CP_CTRL, DA7219_CP_CTRL);
    284	regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS5,
    285			     DA7219_DAC_FILTERS5);
    286	regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS4,
    287			     DA7219_DAC_FILTERS1);
    288	regcache_sync_region(da7219->regmap, DA7219_HP_L_GAIN,
    289			     DA7219_HP_R_GAIN);
    290	regcache_sync_region(da7219->regmap, DA7219_DAC_L_GAIN,
    291			     DA7219_DAC_R_GAIN);
    292	regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_ON_PER,
    293			     DA7219_TONE_GEN_ON_PER);
    294	regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_FREQ1_L,
    295			     DA7219_TONE_GEN_FREQ1_U);
    296	regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_CFG1,
    297			     DA7219_TONE_GEN_CFG2);
    298
    299	regcache_cache_bypass(da7219->regmap, false);
    300
    301	/* Disable HPTest block */
    302	snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_8,
    303			    DA7219_HPTEST_EN_MASK, 0);
    304
    305	/*
    306	 * If we're running from the internal oscillator then give audio paths
    307	 * time to settle before allowing headphones to be driven as required.
    308	 */
    309	if (!(pll_srm_sts & DA7219_PLL_SRM_STS_MCLK))
    310		msleep(DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY);
    311
    312	/* Restore gain ramping rate */
    313	snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL, gain_ramp_ctrl);
    314
    315	/* Drive Headphones/lineout */
    316	snd_soc_component_update_bits(component, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK,
    317			    DA7219_HP_L_AMP_OE_MASK);
    318	snd_soc_component_update_bits(component, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK,
    319			    DA7219_HP_R_AMP_OE_MASK);
    320
    321	/* Restore PLL to previous configuration, if re-configured */
    322	if ((pll_srm_sts & DA7219_PLL_SRM_STS_MCLK) &&
    323	    ((pll_ctrl & DA7219_PLL_MODE_MASK) == DA7219_PLL_MODE_BYPASS))
    324		da7219_set_pll(component, DA7219_SYSCLK_MCLK, 0);
    325
    326	/* Remove MCLK, if previously enabled */
    327	if (da7219->mclk)
    328		clk_disable_unprepare(da7219->mclk);
    329
    330	mutex_unlock(&da7219->pll_lock);
    331	mutex_unlock(&da7219->ctrl_lock);
    332	snd_soc_dapm_mutex_unlock(dapm);
    333
    334	/*
    335	 * Only send report if jack hasn't been removed during process,
    336	 * otherwise it's invalid and we drop it.
    337	 */
    338	if (da7219_aad->jack_inserted)
    339		snd_soc_jack_report(da7219_aad->jack, report,
    340				    SND_JACK_HEADSET | SND_JACK_LINEOUT);
    341}
    342
    343
    344/*
    345 * IRQ
    346 */
    347
    348static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
    349{
    350	struct da7219_aad_priv *da7219_aad = data;
    351	struct snd_soc_component *component = da7219_aad->component;
    352	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
    353	struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
    354	u8 events[DA7219_AAD_IRQ_REG_MAX];
    355	u8 statusa;
    356	int i, report = 0, mask = 0;
    357
    358	/* Read current IRQ events */
    359	regmap_bulk_read(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
    360			 events, DA7219_AAD_IRQ_REG_MAX);
    361
    362	if (!events[DA7219_AAD_IRQ_REG_A] && !events[DA7219_AAD_IRQ_REG_B])
    363		return IRQ_NONE;
    364
    365	/* Read status register for jack insertion & type status */
    366	statusa = snd_soc_component_read(component, DA7219_ACCDET_STATUS_A);
    367
    368	/* Clear events */
    369	regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
    370			  events, DA7219_AAD_IRQ_REG_MAX);
    371
    372	dev_dbg(component->dev, "IRQ events = 0x%x|0x%x, status = 0x%x\n",
    373		events[DA7219_AAD_IRQ_REG_A], events[DA7219_AAD_IRQ_REG_B],
    374		statusa);
    375
    376	if (statusa & DA7219_JACK_INSERTION_STS_MASK) {
    377		/* Jack Insertion */
    378		if (events[DA7219_AAD_IRQ_REG_A] &
    379		    DA7219_E_JACK_INSERTED_MASK) {
    380			report |= SND_JACK_MECHANICAL;
    381			mask |= SND_JACK_MECHANICAL;
    382			da7219_aad->jack_inserted = true;
    383		}
    384
    385		/* Jack type detection */
    386		if (events[DA7219_AAD_IRQ_REG_A] &
    387		    DA7219_E_JACK_DETECT_COMPLETE_MASK) {
    388			/*
    389			 * If 4-pole, then enable button detection, else perform
    390			 * HP impedance test to determine output type to report.
    391			 *
    392			 * We schedule work here as the tasks themselves can
    393			 * take time to complete, and in particular for hptest
    394			 * we want to be able to check if the jack was removed
    395			 * during the procedure as this will invalidate the
    396			 * result. By doing this as work, the IRQ thread can
    397			 * handle a removal, and we can check at the end of
    398			 * hptest if we have a valid result or not.
    399			 */
    400			if (statusa & DA7219_JACK_TYPE_STS_MASK) {
    401				report |= SND_JACK_HEADSET;
    402				mask |=	SND_JACK_HEADSET | SND_JACK_LINEOUT;
    403				schedule_work(&da7219_aad->btn_det_work);
    404			} else {
    405				schedule_work(&da7219_aad->hptest_work);
    406			}
    407		}
    408
    409		/* Button support for 4-pole jack */
    410		if (statusa & DA7219_JACK_TYPE_STS_MASK) {
    411			for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) {
    412				/* Button Press */
    413				if (events[DA7219_AAD_IRQ_REG_B] &
    414				    (DA7219_E_BUTTON_A_PRESSED_MASK << i)) {
    415					report |= SND_JACK_BTN_0 >> i;
    416					mask |= SND_JACK_BTN_0 >> i;
    417				}
    418			}
    419			snd_soc_jack_report(da7219_aad->jack, report, mask);
    420
    421			for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) {
    422				/* Button Release */
    423				if (events[DA7219_AAD_IRQ_REG_B] &
    424				    (DA7219_E_BUTTON_A_RELEASED_MASK >> i)) {
    425					report &= ~(SND_JACK_BTN_0 >> i);
    426					mask |= SND_JACK_BTN_0 >> i;
    427				}
    428			}
    429		}
    430	} else {
    431		/* Jack removal */
    432		if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_REMOVED_MASK) {
    433			report = 0;
    434			mask |= DA7219_AAD_REPORT_ALL_MASK;
    435			da7219_aad->jack_inserted = false;
    436
    437			/* Cancel any pending work */
    438			cancel_work_sync(&da7219_aad->btn_det_work);
    439			cancel_work_sync(&da7219_aad->hptest_work);
    440
    441			/* Un-drive headphones/lineout */
    442			snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
    443					    DA7219_HP_R_AMP_OE_MASK, 0);
    444			snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
    445					    DA7219_HP_L_AMP_OE_MASK, 0);
    446
    447			/* Ensure button detection disabled */
    448			snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
    449					    DA7219_BUTTON_CONFIG_MASK, 0);
    450
    451			da7219->micbias_on_event = false;
    452
    453			/* Disable mic bias */
    454			snd_soc_dapm_disable_pin(dapm, "Mic Bias");
    455			snd_soc_dapm_sync(dapm);
    456
    457			/* Enable ground switch */
    458			snd_soc_component_update_bits(component, 0xFB, 0x01, 0x01);
    459		}
    460	}
    461
    462	snd_soc_jack_report(da7219_aad->jack, report, mask);
    463
    464	return IRQ_HANDLED;
    465}
    466
    467/*
    468 * DT/ACPI to pdata conversion
    469 */
    470
    471static enum da7219_aad_micbias_pulse_lvl
    472	da7219_aad_fw_micbias_pulse_lvl(struct device *dev, u32 val)
    473{
    474	switch (val) {
    475	case 2800:
    476		return DA7219_AAD_MICBIAS_PULSE_LVL_2_8V;
    477	case 2900:
    478		return DA7219_AAD_MICBIAS_PULSE_LVL_2_9V;
    479	default:
    480		dev_warn(dev, "Invalid micbias pulse level");
    481		return DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
    482	}
    483}
    484
    485static enum da7219_aad_btn_cfg
    486	da7219_aad_fw_btn_cfg(struct device *dev, u32 val)
    487{
    488	switch (val) {
    489	case 2:
    490		return DA7219_AAD_BTN_CFG_2MS;
    491	case 5:
    492		return DA7219_AAD_BTN_CFG_5MS;
    493	case 10:
    494		return DA7219_AAD_BTN_CFG_10MS;
    495	case 50:
    496		return DA7219_AAD_BTN_CFG_50MS;
    497	case 100:
    498		return DA7219_AAD_BTN_CFG_100MS;
    499	case 200:
    500		return DA7219_AAD_BTN_CFG_200MS;
    501	case 500:
    502		return DA7219_AAD_BTN_CFG_500MS;
    503	default:
    504		dev_warn(dev, "Invalid button config");
    505		return DA7219_AAD_BTN_CFG_10MS;
    506	}
    507}
    508
    509static enum da7219_aad_mic_det_thr
    510	da7219_aad_fw_mic_det_thr(struct device *dev, u32 val)
    511{
    512	switch (val) {
    513	case 200:
    514		return DA7219_AAD_MIC_DET_THR_200_OHMS;
    515	case 500:
    516		return DA7219_AAD_MIC_DET_THR_500_OHMS;
    517	case 750:
    518		return DA7219_AAD_MIC_DET_THR_750_OHMS;
    519	case 1000:
    520		return DA7219_AAD_MIC_DET_THR_1000_OHMS;
    521	default:
    522		dev_warn(dev, "Invalid mic detect threshold");
    523		return DA7219_AAD_MIC_DET_THR_500_OHMS;
    524	}
    525}
    526
    527static enum da7219_aad_jack_ins_deb
    528	da7219_aad_fw_jack_ins_deb(struct device *dev, u32 val)
    529{
    530	switch (val) {
    531	case 5:
    532		return DA7219_AAD_JACK_INS_DEB_5MS;
    533	case 10:
    534		return DA7219_AAD_JACK_INS_DEB_10MS;
    535	case 20:
    536		return DA7219_AAD_JACK_INS_DEB_20MS;
    537	case 50:
    538		return DA7219_AAD_JACK_INS_DEB_50MS;
    539	case 100:
    540		return DA7219_AAD_JACK_INS_DEB_100MS;
    541	case 200:
    542		return DA7219_AAD_JACK_INS_DEB_200MS;
    543	case 500:
    544		return DA7219_AAD_JACK_INS_DEB_500MS;
    545	case 1000:
    546		return DA7219_AAD_JACK_INS_DEB_1S;
    547	default:
    548		dev_warn(dev, "Invalid jack insert debounce");
    549		return DA7219_AAD_JACK_INS_DEB_20MS;
    550	}
    551}
    552
    553static enum da7219_aad_jack_det_rate
    554	da7219_aad_fw_jack_det_rate(struct device *dev, const char *str)
    555{
    556	if (!strcmp(str, "32ms_64ms")) {
    557		return DA7219_AAD_JACK_DET_RATE_32_64MS;
    558	} else if (!strcmp(str, "64ms_128ms")) {
    559		return DA7219_AAD_JACK_DET_RATE_64_128MS;
    560	} else if (!strcmp(str, "128ms_256ms")) {
    561		return DA7219_AAD_JACK_DET_RATE_128_256MS;
    562	} else if (!strcmp(str, "256ms_512ms")) {
    563		return DA7219_AAD_JACK_DET_RATE_256_512MS;
    564	} else {
    565		dev_warn(dev, "Invalid jack detect rate");
    566		return DA7219_AAD_JACK_DET_RATE_256_512MS;
    567	}
    568}
    569
    570static enum da7219_aad_jack_rem_deb
    571	da7219_aad_fw_jack_rem_deb(struct device *dev, u32 val)
    572{
    573	switch (val) {
    574	case 1:
    575		return DA7219_AAD_JACK_REM_DEB_1MS;
    576	case 5:
    577		return DA7219_AAD_JACK_REM_DEB_5MS;
    578	case 10:
    579		return DA7219_AAD_JACK_REM_DEB_10MS;
    580	case 20:
    581		return DA7219_AAD_JACK_REM_DEB_20MS;
    582	default:
    583		dev_warn(dev, "Invalid jack removal debounce");
    584		return DA7219_AAD_JACK_REM_DEB_1MS;
    585	}
    586}
    587
    588static enum da7219_aad_btn_avg
    589	da7219_aad_fw_btn_avg(struct device *dev, u32 val)
    590{
    591	switch (val) {
    592	case 1:
    593		return DA7219_AAD_BTN_AVG_1;
    594	case 2:
    595		return DA7219_AAD_BTN_AVG_2;
    596	case 4:
    597		return DA7219_AAD_BTN_AVG_4;
    598	case 8:
    599		return DA7219_AAD_BTN_AVG_8;
    600	default:
    601		dev_warn(dev, "Invalid button average value");
    602		return DA7219_AAD_BTN_AVG_2;
    603	}
    604}
    605
    606static enum da7219_aad_adc_1bit_rpt
    607	da7219_aad_fw_adc_1bit_rpt(struct device *dev, u32 val)
    608{
    609	switch (val) {
    610	case 1:
    611		return DA7219_AAD_ADC_1BIT_RPT_1;
    612	case 2:
    613		return DA7219_AAD_ADC_1BIT_RPT_2;
    614	case 4:
    615		return DA7219_AAD_ADC_1BIT_RPT_4;
    616	case 8:
    617		return DA7219_AAD_ADC_1BIT_RPT_8;
    618	default:
    619		dev_warn(dev, "Invalid ADC 1-bit repeat value");
    620		return DA7219_AAD_ADC_1BIT_RPT_1;
    621	}
    622}
    623
    624static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev)
    625{
    626	struct i2c_client *i2c = to_i2c_client(dev);
    627	struct fwnode_handle *aad_np;
    628	struct da7219_aad_pdata *aad_pdata;
    629	const char *fw_str;
    630	u32 fw_val32;
    631
    632	aad_np = device_get_named_child_node(dev, "da7219_aad");
    633	if (!aad_np)
    634		return NULL;
    635
    636	aad_pdata = devm_kzalloc(dev, sizeof(*aad_pdata), GFP_KERNEL);
    637	if (!aad_pdata)
    638		return NULL;
    639
    640	aad_pdata->irq = i2c->irq;
    641
    642	if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-lvl",
    643				     &fw_val32) >= 0)
    644		aad_pdata->micbias_pulse_lvl =
    645			da7219_aad_fw_micbias_pulse_lvl(dev, fw_val32);
    646	else
    647		aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
    648
    649	if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-time",
    650				     &fw_val32) >= 0)
    651		aad_pdata->micbias_pulse_time = fw_val32;
    652
    653	if (fwnode_property_read_u32(aad_np, "dlg,btn-cfg", &fw_val32) >= 0)
    654		aad_pdata->btn_cfg = da7219_aad_fw_btn_cfg(dev, fw_val32);
    655	else
    656		aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS;
    657
    658	if (fwnode_property_read_u32(aad_np, "dlg,mic-det-thr", &fw_val32) >= 0)
    659		aad_pdata->mic_det_thr =
    660			da7219_aad_fw_mic_det_thr(dev, fw_val32);
    661	else
    662		aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS;
    663
    664	if (fwnode_property_read_u32(aad_np, "dlg,jack-ins-deb", &fw_val32) >= 0)
    665		aad_pdata->jack_ins_deb =
    666			da7219_aad_fw_jack_ins_deb(dev, fw_val32);
    667	else
    668		aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS;
    669
    670	if (!fwnode_property_read_string(aad_np, "dlg,jack-det-rate", &fw_str))
    671		aad_pdata->jack_det_rate =
    672			da7219_aad_fw_jack_det_rate(dev, fw_str);
    673	else
    674		aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS;
    675
    676	if (fwnode_property_read_u32(aad_np, "dlg,jack-rem-deb", &fw_val32) >= 0)
    677		aad_pdata->jack_rem_deb =
    678			da7219_aad_fw_jack_rem_deb(dev, fw_val32);
    679	else
    680		aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS;
    681
    682	if (fwnode_property_read_u32(aad_np, "dlg,a-d-btn-thr", &fw_val32) >= 0)
    683		aad_pdata->a_d_btn_thr = (u8) fw_val32;
    684	else
    685		aad_pdata->a_d_btn_thr = 0xA;
    686
    687	if (fwnode_property_read_u32(aad_np, "dlg,d-b-btn-thr", &fw_val32) >= 0)
    688		aad_pdata->d_b_btn_thr = (u8) fw_val32;
    689	else
    690		aad_pdata->d_b_btn_thr = 0x16;
    691
    692	if (fwnode_property_read_u32(aad_np, "dlg,b-c-btn-thr", &fw_val32) >= 0)
    693		aad_pdata->b_c_btn_thr = (u8) fw_val32;
    694	else
    695		aad_pdata->b_c_btn_thr = 0x21;
    696
    697	if (fwnode_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &fw_val32) >= 0)
    698		aad_pdata->c_mic_btn_thr = (u8) fw_val32;
    699	else
    700		aad_pdata->c_mic_btn_thr = 0x3E;
    701
    702	if (fwnode_property_read_u32(aad_np, "dlg,btn-avg", &fw_val32) >= 0)
    703		aad_pdata->btn_avg = da7219_aad_fw_btn_avg(dev, fw_val32);
    704	else
    705		aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2;
    706
    707	if (fwnode_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &fw_val32) >= 0)
    708		aad_pdata->adc_1bit_rpt =
    709			da7219_aad_fw_adc_1bit_rpt(dev, fw_val32);
    710	else
    711		aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1;
    712
    713	return aad_pdata;
    714}
    715
    716static void da7219_aad_handle_pdata(struct snd_soc_component *component)
    717{
    718	struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
    719	struct da7219_aad_priv *da7219_aad = da7219->aad;
    720	struct da7219_pdata *pdata = da7219->pdata;
    721
    722	if ((pdata) && (pdata->aad_pdata)) {
    723		struct da7219_aad_pdata *aad_pdata = pdata->aad_pdata;
    724		u8 cfg, mask;
    725
    726		da7219_aad->irq = aad_pdata->irq;
    727
    728		switch (aad_pdata->micbias_pulse_lvl) {
    729		case DA7219_AAD_MICBIAS_PULSE_LVL_2_8V:
    730		case DA7219_AAD_MICBIAS_PULSE_LVL_2_9V:
    731			da7219_aad->micbias_pulse_lvl =
    732				(aad_pdata->micbias_pulse_lvl <<
    733				 DA7219_MICBIAS1_LEVEL_SHIFT);
    734			break;
    735		default:
    736			break;
    737		}
    738
    739		da7219_aad->micbias_pulse_time = aad_pdata->micbias_pulse_time;
    740
    741		switch (aad_pdata->btn_cfg) {
    742		case DA7219_AAD_BTN_CFG_2MS:
    743		case DA7219_AAD_BTN_CFG_5MS:
    744		case DA7219_AAD_BTN_CFG_10MS:
    745		case DA7219_AAD_BTN_CFG_50MS:
    746		case DA7219_AAD_BTN_CFG_100MS:
    747		case DA7219_AAD_BTN_CFG_200MS:
    748		case DA7219_AAD_BTN_CFG_500MS:
    749			da7219_aad->btn_cfg  = (aad_pdata->btn_cfg <<
    750						DA7219_BUTTON_CONFIG_SHIFT);
    751		}
    752
    753		cfg = 0;
    754		mask = 0;
    755		switch (aad_pdata->mic_det_thr) {
    756		case DA7219_AAD_MIC_DET_THR_200_OHMS:
    757		case DA7219_AAD_MIC_DET_THR_500_OHMS:
    758		case DA7219_AAD_MIC_DET_THR_750_OHMS:
    759		case DA7219_AAD_MIC_DET_THR_1000_OHMS:
    760			cfg |= (aad_pdata->mic_det_thr <<
    761				DA7219_MIC_DET_THRESH_SHIFT);
    762			mask |= DA7219_MIC_DET_THRESH_MASK;
    763		}
    764		snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, mask, cfg);
    765
    766		cfg = 0;
    767		mask = 0;
    768		switch (aad_pdata->jack_ins_deb) {
    769		case DA7219_AAD_JACK_INS_DEB_5MS:
    770		case DA7219_AAD_JACK_INS_DEB_10MS:
    771		case DA7219_AAD_JACK_INS_DEB_20MS:
    772		case DA7219_AAD_JACK_INS_DEB_50MS:
    773		case DA7219_AAD_JACK_INS_DEB_100MS:
    774		case DA7219_AAD_JACK_INS_DEB_200MS:
    775		case DA7219_AAD_JACK_INS_DEB_500MS:
    776		case DA7219_AAD_JACK_INS_DEB_1S:
    777			cfg |= (aad_pdata->jack_ins_deb <<
    778				DA7219_JACKDET_DEBOUNCE_SHIFT);
    779			mask |= DA7219_JACKDET_DEBOUNCE_MASK;
    780		}
    781		switch (aad_pdata->jack_det_rate) {
    782		case DA7219_AAD_JACK_DET_RATE_32_64MS:
    783		case DA7219_AAD_JACK_DET_RATE_64_128MS:
    784		case DA7219_AAD_JACK_DET_RATE_128_256MS:
    785		case DA7219_AAD_JACK_DET_RATE_256_512MS:
    786			cfg |= (aad_pdata->jack_det_rate <<
    787				DA7219_JACK_DETECT_RATE_SHIFT);
    788			mask |= DA7219_JACK_DETECT_RATE_MASK;
    789		}
    790		switch (aad_pdata->jack_rem_deb) {
    791		case DA7219_AAD_JACK_REM_DEB_1MS:
    792		case DA7219_AAD_JACK_REM_DEB_5MS:
    793		case DA7219_AAD_JACK_REM_DEB_10MS:
    794		case DA7219_AAD_JACK_REM_DEB_20MS:
    795			cfg |= (aad_pdata->jack_rem_deb <<
    796				DA7219_JACKDET_REM_DEB_SHIFT);
    797			mask |= DA7219_JACKDET_REM_DEB_MASK;
    798		}
    799		snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_2, mask, cfg);
    800
    801		snd_soc_component_write(component, DA7219_ACCDET_CONFIG_3,
    802			      aad_pdata->a_d_btn_thr);
    803		snd_soc_component_write(component, DA7219_ACCDET_CONFIG_4,
    804			      aad_pdata->d_b_btn_thr);
    805		snd_soc_component_write(component, DA7219_ACCDET_CONFIG_5,
    806			      aad_pdata->b_c_btn_thr);
    807		snd_soc_component_write(component, DA7219_ACCDET_CONFIG_6,
    808			      aad_pdata->c_mic_btn_thr);
    809
    810		cfg = 0;
    811		mask = 0;
    812		switch (aad_pdata->btn_avg) {
    813		case DA7219_AAD_BTN_AVG_1:
    814		case DA7219_AAD_BTN_AVG_2:
    815		case DA7219_AAD_BTN_AVG_4:
    816		case DA7219_AAD_BTN_AVG_8:
    817			cfg |= (aad_pdata->btn_avg <<
    818				DA7219_BUTTON_AVERAGE_SHIFT);
    819			mask |= DA7219_BUTTON_AVERAGE_MASK;
    820		}
    821		switch (aad_pdata->adc_1bit_rpt) {
    822		case DA7219_AAD_ADC_1BIT_RPT_1:
    823		case DA7219_AAD_ADC_1BIT_RPT_2:
    824		case DA7219_AAD_ADC_1BIT_RPT_4:
    825		case DA7219_AAD_ADC_1BIT_RPT_8:
    826			cfg |= (aad_pdata->adc_1bit_rpt <<
    827			       DA7219_ADC_1_BIT_REPEAT_SHIFT);
    828			mask |= DA7219_ADC_1_BIT_REPEAT_MASK;
    829		}
    830		snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_7, mask, cfg);
    831	}
    832}
    833
    834
    835/*
    836 * Suspend/Resume
    837 */
    838
    839void da7219_aad_suspend(struct snd_soc_component *component)
    840{
    841	struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
    842	struct da7219_aad_priv *da7219_aad = da7219->aad;
    843	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
    844	u8 micbias_ctrl;
    845
    846	if (da7219_aad->jack) {
    847		/* Disable jack detection during suspend */
    848		snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
    849				    DA7219_ACCDET_EN_MASK, 0);
    850
    851		/*
    852		 * If we have a 4-pole jack inserted, then micbias will be
    853		 * enabled. We can disable micbias here, and keep a note to
    854		 * re-enable it on resume. If jack removal occurred during
    855		 * suspend then this will be dealt with through the IRQ handler.
    856		 */
    857		if (da7219_aad->jack_inserted) {
    858			micbias_ctrl = snd_soc_component_read(component, DA7219_MICBIAS_CTRL);
    859			if (micbias_ctrl & DA7219_MICBIAS1_EN_MASK) {
    860				snd_soc_dapm_disable_pin(dapm, "Mic Bias");
    861				snd_soc_dapm_sync(dapm);
    862				da7219_aad->micbias_resume_enable = true;
    863			}
    864		}
    865	}
    866}
    867
    868void da7219_aad_resume(struct snd_soc_component *component)
    869{
    870	struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
    871	struct da7219_aad_priv *da7219_aad = da7219->aad;
    872	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
    873
    874	if (da7219_aad->jack) {
    875		/* Re-enable micbias if previously enabled for 4-pole jack */
    876		if (da7219_aad->jack_inserted &&
    877		    da7219_aad->micbias_resume_enable) {
    878			snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
    879			snd_soc_dapm_sync(dapm);
    880			da7219_aad->micbias_resume_enable = false;
    881		}
    882
    883		/* Re-enable jack detection */
    884		snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
    885				    DA7219_ACCDET_EN_MASK,
    886				    DA7219_ACCDET_EN_MASK);
    887	}
    888}
    889
    890
    891/*
    892 * Init/Exit
    893 */
    894
    895int da7219_aad_init(struct snd_soc_component *component)
    896{
    897	struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
    898	struct da7219_aad_priv *da7219_aad = da7219->aad;
    899	u8 mask[DA7219_AAD_IRQ_REG_MAX];
    900	int ret;
    901
    902	da7219_aad->component = component;
    903
    904	/* Handle any DT/ACPI/platform data */
    905	da7219_aad_handle_pdata(component);
    906
    907	/* Disable button detection */
    908	snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
    909			    DA7219_BUTTON_CONFIG_MASK, 0);
    910
    911	/* Enable ground switch */
    912	snd_soc_component_update_bits(component, 0xFB, 0x01, 0x01);
    913
    914	INIT_WORK(&da7219_aad->btn_det_work, da7219_aad_btn_det_work);
    915	INIT_WORK(&da7219_aad->hptest_work, da7219_aad_hptest_work);
    916
    917	ret = request_threaded_irq(da7219_aad->irq, NULL,
    918				   da7219_aad_irq_thread,
    919				   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
    920				   "da7219-aad", da7219_aad);
    921	if (ret) {
    922		dev_err(component->dev, "Failed to request IRQ: %d\n", ret);
    923		return ret;
    924	}
    925
    926	/* Unmask AAD IRQs */
    927	memset(mask, 0, DA7219_AAD_IRQ_REG_MAX);
    928	regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A,
    929			  &mask, DA7219_AAD_IRQ_REG_MAX);
    930
    931	return 0;
    932}
    933EXPORT_SYMBOL_GPL(da7219_aad_init);
    934
    935void da7219_aad_exit(struct snd_soc_component *component)
    936{
    937	struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
    938	struct da7219_aad_priv *da7219_aad = da7219->aad;
    939	u8 mask[DA7219_AAD_IRQ_REG_MAX];
    940
    941	/* Mask off AAD IRQs */
    942	memset(mask, DA7219_BYTE_MASK, DA7219_AAD_IRQ_REG_MAX);
    943	regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A,
    944			  mask, DA7219_AAD_IRQ_REG_MAX);
    945
    946	free_irq(da7219_aad->irq, da7219_aad);
    947
    948	cancel_work_sync(&da7219_aad->btn_det_work);
    949	cancel_work_sync(&da7219_aad->hptest_work);
    950}
    951EXPORT_SYMBOL_GPL(da7219_aad_exit);
    952
    953/*
    954 * AAD related I2C probe handling
    955 */
    956
    957int da7219_aad_probe(struct i2c_client *i2c)
    958{
    959	struct da7219_priv *da7219 = i2c_get_clientdata(i2c);
    960	struct device *dev = &i2c->dev;
    961	struct da7219_aad_priv *da7219_aad;
    962
    963	da7219_aad = devm_kzalloc(dev, sizeof(*da7219_aad), GFP_KERNEL);
    964	if (!da7219_aad)
    965		return -ENOMEM;
    966
    967	da7219->aad = da7219_aad;
    968
    969	/* Retrieve any DT/ACPI/platform data */
    970	if (da7219->pdata && !da7219->pdata->aad_pdata)
    971		da7219->pdata->aad_pdata = da7219_aad_fw_to_pdata(dev);
    972
    973	return 0;
    974}
    975EXPORT_SYMBOL_GPL(da7219_aad_probe);
    976
    977MODULE_DESCRIPTION("ASoC DA7219 AAD Driver");
    978MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
    979MODULE_LICENSE("GPL");