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