162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * wm8904.c -- WM8904 ALSA SoC Audio driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2009-12 Wolfson Microelectronics plc 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/pm.h> 1562306a36Sopenharmony_ci#include <linux/i2c.h> 1662306a36Sopenharmony_ci#include <linux/regmap.h> 1762306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <sound/core.h> 2062306a36Sopenharmony_ci#include <sound/pcm.h> 2162306a36Sopenharmony_ci#include <sound/pcm_params.h> 2262306a36Sopenharmony_ci#include <sound/soc.h> 2362306a36Sopenharmony_ci#include <sound/initval.h> 2462306a36Sopenharmony_ci#include <sound/tlv.h> 2562306a36Sopenharmony_ci#include <sound/wm8904.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "wm8904.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cienum wm8904_type { 3062306a36Sopenharmony_ci WM8904, 3162306a36Sopenharmony_ci WM8912, 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define WM8904_NUM_DCS_CHANNELS 4 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define WM8904_NUM_SUPPLIES 5 3762306a36Sopenharmony_cistatic const char *wm8904_supply_names[WM8904_NUM_SUPPLIES] = { 3862306a36Sopenharmony_ci "DCVDD", 3962306a36Sopenharmony_ci "DBVDD", 4062306a36Sopenharmony_ci "AVDD", 4162306a36Sopenharmony_ci "CPVDD", 4262306a36Sopenharmony_ci "MICVDD", 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* codec private data */ 4662306a36Sopenharmony_cistruct wm8904_priv { 4762306a36Sopenharmony_ci struct regmap *regmap; 4862306a36Sopenharmony_ci struct clk *mclk; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci enum wm8904_type devtype; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci struct regulator_bulk_data supplies[WM8904_NUM_SUPPLIES]; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci struct wm8904_pdata *pdata; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci int deemph; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* Platform provided DRC configuration */ 5962306a36Sopenharmony_ci const char **drc_texts; 6062306a36Sopenharmony_ci int drc_cfg; 6162306a36Sopenharmony_ci struct soc_enum drc_enum; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* Platform provided ReTune mobile configuration */ 6462306a36Sopenharmony_ci int num_retune_mobile_texts; 6562306a36Sopenharmony_ci const char **retune_mobile_texts; 6662306a36Sopenharmony_ci int retune_mobile_cfg; 6762306a36Sopenharmony_ci struct soc_enum retune_mobile_enum; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* FLL setup */ 7062306a36Sopenharmony_ci int fll_src; 7162306a36Sopenharmony_ci int fll_fref; 7262306a36Sopenharmony_ci int fll_fout; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* Clocking configuration */ 7562306a36Sopenharmony_ci unsigned int mclk_rate; 7662306a36Sopenharmony_ci int sysclk_src; 7762306a36Sopenharmony_ci unsigned int sysclk_rate; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci int tdm_width; 8062306a36Sopenharmony_ci int tdm_slots; 8162306a36Sopenharmony_ci int bclk; 8262306a36Sopenharmony_ci int fs; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* DC servo configuration - cached offset values */ 8562306a36Sopenharmony_ci int dcs_state[WM8904_NUM_DCS_CHANNELS]; 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic const struct reg_default wm8904_reg_defaults[] = { 8962306a36Sopenharmony_ci { 4, 0x0018 }, /* R4 - Bias Control 0 */ 9062306a36Sopenharmony_ci { 5, 0x0000 }, /* R5 - VMID Control 0 */ 9162306a36Sopenharmony_ci { 6, 0x0000 }, /* R6 - Mic Bias Control 0 */ 9262306a36Sopenharmony_ci { 7, 0x0000 }, /* R7 - Mic Bias Control 1 */ 9362306a36Sopenharmony_ci { 8, 0x0001 }, /* R8 - Analogue DAC 0 */ 9462306a36Sopenharmony_ci { 9, 0x9696 }, /* R9 - mic Filter Control */ 9562306a36Sopenharmony_ci { 10, 0x0001 }, /* R10 - Analogue ADC 0 */ 9662306a36Sopenharmony_ci { 12, 0x0000 }, /* R12 - Power Management 0 */ 9762306a36Sopenharmony_ci { 14, 0x0000 }, /* R14 - Power Management 2 */ 9862306a36Sopenharmony_ci { 15, 0x0000 }, /* R15 - Power Management 3 */ 9962306a36Sopenharmony_ci { 18, 0x0000 }, /* R18 - Power Management 6 */ 10062306a36Sopenharmony_ci { 20, 0x945E }, /* R20 - Clock Rates 0 */ 10162306a36Sopenharmony_ci { 21, 0x0C05 }, /* R21 - Clock Rates 1 */ 10262306a36Sopenharmony_ci { 22, 0x0006 }, /* R22 - Clock Rates 2 */ 10362306a36Sopenharmony_ci { 24, 0x0050 }, /* R24 - Audio Interface 0 */ 10462306a36Sopenharmony_ci { 25, 0x000A }, /* R25 - Audio Interface 1 */ 10562306a36Sopenharmony_ci { 26, 0x00E4 }, /* R26 - Audio Interface 2 */ 10662306a36Sopenharmony_ci { 27, 0x0040 }, /* R27 - Audio Interface 3 */ 10762306a36Sopenharmony_ci { 30, 0x00C0 }, /* R30 - DAC Digital Volume Left */ 10862306a36Sopenharmony_ci { 31, 0x00C0 }, /* R31 - DAC Digital Volume Right */ 10962306a36Sopenharmony_ci { 32, 0x0000 }, /* R32 - DAC Digital 0 */ 11062306a36Sopenharmony_ci { 33, 0x0008 }, /* R33 - DAC Digital 1 */ 11162306a36Sopenharmony_ci { 36, 0x00C0 }, /* R36 - ADC Digital Volume Left */ 11262306a36Sopenharmony_ci { 37, 0x00C0 }, /* R37 - ADC Digital Volume Right */ 11362306a36Sopenharmony_ci { 38, 0x0010 }, /* R38 - ADC Digital 0 */ 11462306a36Sopenharmony_ci { 39, 0x0000 }, /* R39 - Digital Microphone 0 */ 11562306a36Sopenharmony_ci { 40, 0x01AF }, /* R40 - DRC 0 */ 11662306a36Sopenharmony_ci { 41, 0x3248 }, /* R41 - DRC 1 */ 11762306a36Sopenharmony_ci { 42, 0x0000 }, /* R42 - DRC 2 */ 11862306a36Sopenharmony_ci { 43, 0x0000 }, /* R43 - DRC 3 */ 11962306a36Sopenharmony_ci { 44, 0x0085 }, /* R44 - Analogue Left Input 0 */ 12062306a36Sopenharmony_ci { 45, 0x0085 }, /* R45 - Analogue Right Input 0 */ 12162306a36Sopenharmony_ci { 46, 0x0044 }, /* R46 - Analogue Left Input 1 */ 12262306a36Sopenharmony_ci { 47, 0x0044 }, /* R47 - Analogue Right Input 1 */ 12362306a36Sopenharmony_ci { 57, 0x002D }, /* R57 - Analogue OUT1 Left */ 12462306a36Sopenharmony_ci { 58, 0x002D }, /* R58 - Analogue OUT1 Right */ 12562306a36Sopenharmony_ci { 59, 0x0039 }, /* R59 - Analogue OUT2 Left */ 12662306a36Sopenharmony_ci { 60, 0x0039 }, /* R60 - Analogue OUT2 Right */ 12762306a36Sopenharmony_ci { 61, 0x0000 }, /* R61 - Analogue OUT12 ZC */ 12862306a36Sopenharmony_ci { 67, 0x0000 }, /* R67 - DC Servo 0 */ 12962306a36Sopenharmony_ci { 69, 0xAAAA }, /* R69 - DC Servo 2 */ 13062306a36Sopenharmony_ci { 71, 0xAAAA }, /* R71 - DC Servo 4 */ 13162306a36Sopenharmony_ci { 72, 0xAAAA }, /* R72 - DC Servo 5 */ 13262306a36Sopenharmony_ci { 90, 0x0000 }, /* R90 - Analogue HP 0 */ 13362306a36Sopenharmony_ci { 94, 0x0000 }, /* R94 - Analogue Lineout 0 */ 13462306a36Sopenharmony_ci { 98, 0x0000 }, /* R98 - Charge Pump 0 */ 13562306a36Sopenharmony_ci { 104, 0x0004 }, /* R104 - Class W 0 */ 13662306a36Sopenharmony_ci { 108, 0x0000 }, /* R108 - Write Sequencer 0 */ 13762306a36Sopenharmony_ci { 109, 0x0000 }, /* R109 - Write Sequencer 1 */ 13862306a36Sopenharmony_ci { 110, 0x0000 }, /* R110 - Write Sequencer 2 */ 13962306a36Sopenharmony_ci { 111, 0x0000 }, /* R111 - Write Sequencer 3 */ 14062306a36Sopenharmony_ci { 112, 0x0000 }, /* R112 - Write Sequencer 4 */ 14162306a36Sopenharmony_ci { 116, 0x0000 }, /* R116 - FLL Control 1 */ 14262306a36Sopenharmony_ci { 117, 0x0007 }, /* R117 - FLL Control 2 */ 14362306a36Sopenharmony_ci { 118, 0x0000 }, /* R118 - FLL Control 3 */ 14462306a36Sopenharmony_ci { 119, 0x2EE0 }, /* R119 - FLL Control 4 */ 14562306a36Sopenharmony_ci { 120, 0x0004 }, /* R120 - FLL Control 5 */ 14662306a36Sopenharmony_ci { 121, 0x0014 }, /* R121 - GPIO Control 1 */ 14762306a36Sopenharmony_ci { 122, 0x0010 }, /* R122 - GPIO Control 2 */ 14862306a36Sopenharmony_ci { 123, 0x0010 }, /* R123 - GPIO Control 3 */ 14962306a36Sopenharmony_ci { 124, 0x0000 }, /* R124 - GPIO Control 4 */ 15062306a36Sopenharmony_ci { 126, 0x0000 }, /* R126 - Digital Pulls */ 15162306a36Sopenharmony_ci { 128, 0xFFFF }, /* R128 - Interrupt Status Mask */ 15262306a36Sopenharmony_ci { 129, 0x0000 }, /* R129 - Interrupt Polarity */ 15362306a36Sopenharmony_ci { 130, 0x0000 }, /* R130 - Interrupt Debounce */ 15462306a36Sopenharmony_ci { 134, 0x0000 }, /* R134 - EQ1 */ 15562306a36Sopenharmony_ci { 135, 0x000C }, /* R135 - EQ2 */ 15662306a36Sopenharmony_ci { 136, 0x000C }, /* R136 - EQ3 */ 15762306a36Sopenharmony_ci { 137, 0x000C }, /* R137 - EQ4 */ 15862306a36Sopenharmony_ci { 138, 0x000C }, /* R138 - EQ5 */ 15962306a36Sopenharmony_ci { 139, 0x000C }, /* R139 - EQ6 */ 16062306a36Sopenharmony_ci { 140, 0x0FCA }, /* R140 - EQ7 */ 16162306a36Sopenharmony_ci { 141, 0x0400 }, /* R141 - EQ8 */ 16262306a36Sopenharmony_ci { 142, 0x00D8 }, /* R142 - EQ9 */ 16362306a36Sopenharmony_ci { 143, 0x1EB5 }, /* R143 - EQ10 */ 16462306a36Sopenharmony_ci { 144, 0xF145 }, /* R144 - EQ11 */ 16562306a36Sopenharmony_ci { 145, 0x0B75 }, /* R145 - EQ12 */ 16662306a36Sopenharmony_ci { 146, 0x01C5 }, /* R146 - EQ13 */ 16762306a36Sopenharmony_ci { 147, 0x1C58 }, /* R147 - EQ14 */ 16862306a36Sopenharmony_ci { 148, 0xF373 }, /* R148 - EQ15 */ 16962306a36Sopenharmony_ci { 149, 0x0A54 }, /* R149 - EQ16 */ 17062306a36Sopenharmony_ci { 150, 0x0558 }, /* R150 - EQ17 */ 17162306a36Sopenharmony_ci { 151, 0x168E }, /* R151 - EQ18 */ 17262306a36Sopenharmony_ci { 152, 0xF829 }, /* R152 - EQ19 */ 17362306a36Sopenharmony_ci { 153, 0x07AD }, /* R153 - EQ20 */ 17462306a36Sopenharmony_ci { 154, 0x1103 }, /* R154 - EQ21 */ 17562306a36Sopenharmony_ci { 155, 0x0564 }, /* R155 - EQ22 */ 17662306a36Sopenharmony_ci { 156, 0x0559 }, /* R156 - EQ23 */ 17762306a36Sopenharmony_ci { 157, 0x4000 }, /* R157 - EQ24 */ 17862306a36Sopenharmony_ci { 161, 0x0000 }, /* R161 - Control Interface Test 1 */ 17962306a36Sopenharmony_ci { 204, 0x0000 }, /* R204 - Analogue Output Bias 0 */ 18062306a36Sopenharmony_ci { 247, 0x0000 }, /* R247 - FLL NCO Test 0 */ 18162306a36Sopenharmony_ci { 248, 0x0019 }, /* R248 - FLL NCO Test 1 */ 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic bool wm8904_volatile_register(struct device *dev, unsigned int reg) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci switch (reg) { 18762306a36Sopenharmony_ci case WM8904_SW_RESET_AND_ID: 18862306a36Sopenharmony_ci case WM8904_REVISION: 18962306a36Sopenharmony_ci case WM8904_DC_SERVO_1: 19062306a36Sopenharmony_ci case WM8904_DC_SERVO_6: 19162306a36Sopenharmony_ci case WM8904_DC_SERVO_7: 19262306a36Sopenharmony_ci case WM8904_DC_SERVO_8: 19362306a36Sopenharmony_ci case WM8904_DC_SERVO_9: 19462306a36Sopenharmony_ci case WM8904_DC_SERVO_READBACK_0: 19562306a36Sopenharmony_ci case WM8904_INTERRUPT_STATUS: 19662306a36Sopenharmony_ci return true; 19762306a36Sopenharmony_ci default: 19862306a36Sopenharmony_ci return false; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic bool wm8904_readable_register(struct device *dev, unsigned int reg) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci switch (reg) { 20562306a36Sopenharmony_ci case WM8904_SW_RESET_AND_ID: 20662306a36Sopenharmony_ci case WM8904_REVISION: 20762306a36Sopenharmony_ci case WM8904_BIAS_CONTROL_0: 20862306a36Sopenharmony_ci case WM8904_VMID_CONTROL_0: 20962306a36Sopenharmony_ci case WM8904_MIC_BIAS_CONTROL_0: 21062306a36Sopenharmony_ci case WM8904_MIC_BIAS_CONTROL_1: 21162306a36Sopenharmony_ci case WM8904_ANALOGUE_DAC_0: 21262306a36Sopenharmony_ci case WM8904_MIC_FILTER_CONTROL: 21362306a36Sopenharmony_ci case WM8904_ANALOGUE_ADC_0: 21462306a36Sopenharmony_ci case WM8904_POWER_MANAGEMENT_0: 21562306a36Sopenharmony_ci case WM8904_POWER_MANAGEMENT_2: 21662306a36Sopenharmony_ci case WM8904_POWER_MANAGEMENT_3: 21762306a36Sopenharmony_ci case WM8904_POWER_MANAGEMENT_6: 21862306a36Sopenharmony_ci case WM8904_CLOCK_RATES_0: 21962306a36Sopenharmony_ci case WM8904_CLOCK_RATES_1: 22062306a36Sopenharmony_ci case WM8904_CLOCK_RATES_2: 22162306a36Sopenharmony_ci case WM8904_AUDIO_INTERFACE_0: 22262306a36Sopenharmony_ci case WM8904_AUDIO_INTERFACE_1: 22362306a36Sopenharmony_ci case WM8904_AUDIO_INTERFACE_2: 22462306a36Sopenharmony_ci case WM8904_AUDIO_INTERFACE_3: 22562306a36Sopenharmony_ci case WM8904_DAC_DIGITAL_VOLUME_LEFT: 22662306a36Sopenharmony_ci case WM8904_DAC_DIGITAL_VOLUME_RIGHT: 22762306a36Sopenharmony_ci case WM8904_DAC_DIGITAL_0: 22862306a36Sopenharmony_ci case WM8904_DAC_DIGITAL_1: 22962306a36Sopenharmony_ci case WM8904_ADC_DIGITAL_VOLUME_LEFT: 23062306a36Sopenharmony_ci case WM8904_ADC_DIGITAL_VOLUME_RIGHT: 23162306a36Sopenharmony_ci case WM8904_ADC_DIGITAL_0: 23262306a36Sopenharmony_ci case WM8904_DIGITAL_MICROPHONE_0: 23362306a36Sopenharmony_ci case WM8904_DRC_0: 23462306a36Sopenharmony_ci case WM8904_DRC_1: 23562306a36Sopenharmony_ci case WM8904_DRC_2: 23662306a36Sopenharmony_ci case WM8904_DRC_3: 23762306a36Sopenharmony_ci case WM8904_ANALOGUE_LEFT_INPUT_0: 23862306a36Sopenharmony_ci case WM8904_ANALOGUE_RIGHT_INPUT_0: 23962306a36Sopenharmony_ci case WM8904_ANALOGUE_LEFT_INPUT_1: 24062306a36Sopenharmony_ci case WM8904_ANALOGUE_RIGHT_INPUT_1: 24162306a36Sopenharmony_ci case WM8904_ANALOGUE_OUT1_LEFT: 24262306a36Sopenharmony_ci case WM8904_ANALOGUE_OUT1_RIGHT: 24362306a36Sopenharmony_ci case WM8904_ANALOGUE_OUT2_LEFT: 24462306a36Sopenharmony_ci case WM8904_ANALOGUE_OUT2_RIGHT: 24562306a36Sopenharmony_ci case WM8904_ANALOGUE_OUT12_ZC: 24662306a36Sopenharmony_ci case WM8904_DC_SERVO_0: 24762306a36Sopenharmony_ci case WM8904_DC_SERVO_1: 24862306a36Sopenharmony_ci case WM8904_DC_SERVO_2: 24962306a36Sopenharmony_ci case WM8904_DC_SERVO_4: 25062306a36Sopenharmony_ci case WM8904_DC_SERVO_5: 25162306a36Sopenharmony_ci case WM8904_DC_SERVO_6: 25262306a36Sopenharmony_ci case WM8904_DC_SERVO_7: 25362306a36Sopenharmony_ci case WM8904_DC_SERVO_8: 25462306a36Sopenharmony_ci case WM8904_DC_SERVO_9: 25562306a36Sopenharmony_ci case WM8904_DC_SERVO_READBACK_0: 25662306a36Sopenharmony_ci case WM8904_ANALOGUE_HP_0: 25762306a36Sopenharmony_ci case WM8904_ANALOGUE_LINEOUT_0: 25862306a36Sopenharmony_ci case WM8904_CHARGE_PUMP_0: 25962306a36Sopenharmony_ci case WM8904_CLASS_W_0: 26062306a36Sopenharmony_ci case WM8904_WRITE_SEQUENCER_0: 26162306a36Sopenharmony_ci case WM8904_WRITE_SEQUENCER_1: 26262306a36Sopenharmony_ci case WM8904_WRITE_SEQUENCER_2: 26362306a36Sopenharmony_ci case WM8904_WRITE_SEQUENCER_3: 26462306a36Sopenharmony_ci case WM8904_WRITE_SEQUENCER_4: 26562306a36Sopenharmony_ci case WM8904_FLL_CONTROL_1: 26662306a36Sopenharmony_ci case WM8904_FLL_CONTROL_2: 26762306a36Sopenharmony_ci case WM8904_FLL_CONTROL_3: 26862306a36Sopenharmony_ci case WM8904_FLL_CONTROL_4: 26962306a36Sopenharmony_ci case WM8904_FLL_CONTROL_5: 27062306a36Sopenharmony_ci case WM8904_GPIO_CONTROL_1: 27162306a36Sopenharmony_ci case WM8904_GPIO_CONTROL_2: 27262306a36Sopenharmony_ci case WM8904_GPIO_CONTROL_3: 27362306a36Sopenharmony_ci case WM8904_GPIO_CONTROL_4: 27462306a36Sopenharmony_ci case WM8904_DIGITAL_PULLS: 27562306a36Sopenharmony_ci case WM8904_INTERRUPT_STATUS: 27662306a36Sopenharmony_ci case WM8904_INTERRUPT_STATUS_MASK: 27762306a36Sopenharmony_ci case WM8904_INTERRUPT_POLARITY: 27862306a36Sopenharmony_ci case WM8904_INTERRUPT_DEBOUNCE: 27962306a36Sopenharmony_ci case WM8904_EQ1: 28062306a36Sopenharmony_ci case WM8904_EQ2: 28162306a36Sopenharmony_ci case WM8904_EQ3: 28262306a36Sopenharmony_ci case WM8904_EQ4: 28362306a36Sopenharmony_ci case WM8904_EQ5: 28462306a36Sopenharmony_ci case WM8904_EQ6: 28562306a36Sopenharmony_ci case WM8904_EQ7: 28662306a36Sopenharmony_ci case WM8904_EQ8: 28762306a36Sopenharmony_ci case WM8904_EQ9: 28862306a36Sopenharmony_ci case WM8904_EQ10: 28962306a36Sopenharmony_ci case WM8904_EQ11: 29062306a36Sopenharmony_ci case WM8904_EQ12: 29162306a36Sopenharmony_ci case WM8904_EQ13: 29262306a36Sopenharmony_ci case WM8904_EQ14: 29362306a36Sopenharmony_ci case WM8904_EQ15: 29462306a36Sopenharmony_ci case WM8904_EQ16: 29562306a36Sopenharmony_ci case WM8904_EQ17: 29662306a36Sopenharmony_ci case WM8904_EQ18: 29762306a36Sopenharmony_ci case WM8904_EQ19: 29862306a36Sopenharmony_ci case WM8904_EQ20: 29962306a36Sopenharmony_ci case WM8904_EQ21: 30062306a36Sopenharmony_ci case WM8904_EQ22: 30162306a36Sopenharmony_ci case WM8904_EQ23: 30262306a36Sopenharmony_ci case WM8904_EQ24: 30362306a36Sopenharmony_ci case WM8904_CONTROL_INTERFACE_TEST_1: 30462306a36Sopenharmony_ci case WM8904_ADC_TEST_0: 30562306a36Sopenharmony_ci case WM8904_ANALOGUE_OUTPUT_BIAS_0: 30662306a36Sopenharmony_ci case WM8904_FLL_NCO_TEST_0: 30762306a36Sopenharmony_ci case WM8904_FLL_NCO_TEST_1: 30862306a36Sopenharmony_ci return true; 30962306a36Sopenharmony_ci default: 31062306a36Sopenharmony_ci return false; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int wm8904_configure_clocking(struct snd_soc_component *component) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 31762306a36Sopenharmony_ci unsigned int clock0, clock2, rate; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* Gate the clock while we're updating to avoid misclocking */ 32062306a36Sopenharmony_ci clock2 = snd_soc_component_read(component, WM8904_CLOCK_RATES_2); 32162306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2, 32262306a36Sopenharmony_ci WM8904_SYSCLK_SRC, 0); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* This should be done on init() for bypass paths */ 32562306a36Sopenharmony_ci switch (wm8904->sysclk_src) { 32662306a36Sopenharmony_ci case WM8904_CLK_MCLK: 32762306a36Sopenharmony_ci dev_dbg(component->dev, "Using %dHz MCLK\n", wm8904->mclk_rate); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci clock2 &= ~WM8904_SYSCLK_SRC; 33062306a36Sopenharmony_ci rate = wm8904->mclk_rate; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* Ensure the FLL is stopped */ 33362306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, 33462306a36Sopenharmony_ci WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0); 33562306a36Sopenharmony_ci break; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci case WM8904_CLK_FLL: 33862306a36Sopenharmony_ci dev_dbg(component->dev, "Using %dHz FLL clock\n", 33962306a36Sopenharmony_ci wm8904->fll_fout); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci clock2 |= WM8904_SYSCLK_SRC; 34262306a36Sopenharmony_ci rate = wm8904->fll_fout; 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci default: 34662306a36Sopenharmony_ci dev_err(component->dev, "System clock not configured\n"); 34762306a36Sopenharmony_ci return -EINVAL; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* SYSCLK shouldn't be over 13.5MHz */ 35162306a36Sopenharmony_ci if (rate > 13500000) { 35262306a36Sopenharmony_ci clock0 = WM8904_MCLK_DIV; 35362306a36Sopenharmony_ci wm8904->sysclk_rate = rate / 2; 35462306a36Sopenharmony_ci } else { 35562306a36Sopenharmony_ci clock0 = 0; 35662306a36Sopenharmony_ci wm8904->sysclk_rate = rate; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_0, WM8904_MCLK_DIV, 36062306a36Sopenharmony_ci clock0); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2, 36362306a36Sopenharmony_ci WM8904_CLK_SYS_ENA | WM8904_SYSCLK_SRC, clock2); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci dev_dbg(component->dev, "CLK_SYS is %dHz\n", wm8904->sysclk_rate); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic void wm8904_set_drc(struct snd_soc_component *component) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 37362306a36Sopenharmony_ci struct wm8904_pdata *pdata = wm8904->pdata; 37462306a36Sopenharmony_ci int save, i; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* Save any enables; the configuration should clear them. */ 37762306a36Sopenharmony_ci save = snd_soc_component_read(component, WM8904_DRC_0); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci for (i = 0; i < WM8904_DRC_REGS; i++) 38062306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_DRC_0 + i, 0xffff, 38162306a36Sopenharmony_ci pdata->drc_cfgs[wm8904->drc_cfg].regs[i]); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* Reenable the DRC */ 38462306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_DRC_0, 38562306a36Sopenharmony_ci WM8904_DRC_ENA | WM8904_DRC_DAC_PATH, save); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int wm8904_put_drc_enum(struct snd_kcontrol *kcontrol, 38962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 39262306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 39362306a36Sopenharmony_ci struct wm8904_pdata *pdata = wm8904->pdata; 39462306a36Sopenharmony_ci int value = ucontrol->value.enumerated.item[0]; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (value >= pdata->num_drc_cfgs) 39762306a36Sopenharmony_ci return -EINVAL; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci wm8904->drc_cfg = value; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci wm8904_set_drc(component); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic int wm8904_get_drc_enum(struct snd_kcontrol *kcontrol, 40762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 41062306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = wm8904->drc_cfg; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return 0; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic void wm8904_set_retune_mobile(struct snd_soc_component *component) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 42062306a36Sopenharmony_ci struct wm8904_pdata *pdata = wm8904->pdata; 42162306a36Sopenharmony_ci int best, best_val, save, i, cfg; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (!pdata || !wm8904->num_retune_mobile_texts) 42462306a36Sopenharmony_ci return; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* Find the version of the currently selected configuration 42762306a36Sopenharmony_ci * with the nearest sample rate. */ 42862306a36Sopenharmony_ci cfg = wm8904->retune_mobile_cfg; 42962306a36Sopenharmony_ci best = 0; 43062306a36Sopenharmony_ci best_val = INT_MAX; 43162306a36Sopenharmony_ci for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) { 43262306a36Sopenharmony_ci if (strcmp(pdata->retune_mobile_cfgs[i].name, 43362306a36Sopenharmony_ci wm8904->retune_mobile_texts[cfg]) == 0 && 43462306a36Sopenharmony_ci abs(pdata->retune_mobile_cfgs[i].rate 43562306a36Sopenharmony_ci - wm8904->fs) < best_val) { 43662306a36Sopenharmony_ci best = i; 43762306a36Sopenharmony_ci best_val = abs(pdata->retune_mobile_cfgs[i].rate 43862306a36Sopenharmony_ci - wm8904->fs); 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci dev_dbg(component->dev, "ReTune Mobile %s/%dHz for %dHz sample rate\n", 44362306a36Sopenharmony_ci pdata->retune_mobile_cfgs[best].name, 44462306a36Sopenharmony_ci pdata->retune_mobile_cfgs[best].rate, 44562306a36Sopenharmony_ci wm8904->fs); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* The EQ will be disabled while reconfiguring it, remember the 44862306a36Sopenharmony_ci * current configuration. 44962306a36Sopenharmony_ci */ 45062306a36Sopenharmony_ci save = snd_soc_component_read(component, WM8904_EQ1); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci for (i = 0; i < WM8904_EQ_REGS; i++) 45362306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_EQ1 + i, 0xffff, 45462306a36Sopenharmony_ci pdata->retune_mobile_cfgs[best].regs[i]); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_EQ1, WM8904_EQ_ENA, save); 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic int wm8904_put_retune_mobile_enum(struct snd_kcontrol *kcontrol, 46062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 46362306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 46462306a36Sopenharmony_ci struct wm8904_pdata *pdata = wm8904->pdata; 46562306a36Sopenharmony_ci int value = ucontrol->value.enumerated.item[0]; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (value >= pdata->num_retune_mobile_cfgs) 46862306a36Sopenharmony_ci return -EINVAL; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci wm8904->retune_mobile_cfg = value; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci wm8904_set_retune_mobile(component); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci return 0; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic int wm8904_get_retune_mobile_enum(struct snd_kcontrol *kcontrol, 47862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 48162306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = wm8904->retune_mobile_cfg; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return 0; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic int deemph_settings[] = { 0, 32000, 44100, 48000 }; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic int wm8904_set_deemph(struct snd_soc_component *component) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 49362306a36Sopenharmony_ci int val, i, best; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* If we're using deemphasis select the nearest available sample 49662306a36Sopenharmony_ci * rate. 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_ci if (wm8904->deemph) { 49962306a36Sopenharmony_ci best = 1; 50062306a36Sopenharmony_ci for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { 50162306a36Sopenharmony_ci if (abs(deemph_settings[i] - wm8904->fs) < 50262306a36Sopenharmony_ci abs(deemph_settings[best] - wm8904->fs)) 50362306a36Sopenharmony_ci best = i; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci val = best << WM8904_DEEMPH_SHIFT; 50762306a36Sopenharmony_ci } else { 50862306a36Sopenharmony_ci val = 0; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci dev_dbg(component->dev, "Set deemphasis %d\n", val); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return snd_soc_component_update_bits(component, WM8904_DAC_DIGITAL_1, 51462306a36Sopenharmony_ci WM8904_DEEMPH_MASK, val); 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic int wm8904_get_deemph(struct snd_kcontrol *kcontrol, 51862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 52162306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci ucontrol->value.integer.value[0] = wm8904->deemph; 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic int wm8904_put_deemph(struct snd_kcontrol *kcontrol, 52862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 53162306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 53262306a36Sopenharmony_ci unsigned int deemph = ucontrol->value.integer.value[0]; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (deemph > 1) 53562306a36Sopenharmony_ci return -EINVAL; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci wm8904->deemph = deemph; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return wm8904_set_deemph(component); 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0); 54362306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); 54462306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0); 54562306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0); 54662306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic const char *hpf_mode_text[] = { 54962306a36Sopenharmony_ci "Hi-fi", "Voice 1", "Voice 2", "Voice 3" 55062306a36Sopenharmony_ci}; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(hpf_mode, WM8904_ADC_DIGITAL_0, 5, 55362306a36Sopenharmony_ci hpf_mode_text); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic int wm8904_adc_osr_put(struct snd_kcontrol *kcontrol, 55662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 55962306a36Sopenharmony_ci unsigned int val; 56062306a36Sopenharmony_ci int ret; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci ret = snd_soc_put_volsw(kcontrol, ucontrol); 56362306a36Sopenharmony_ci if (ret < 0) 56462306a36Sopenharmony_ci return ret; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (ucontrol->value.integer.value[0]) 56762306a36Sopenharmony_ci val = 0; 56862306a36Sopenharmony_ci else 56962306a36Sopenharmony_ci val = WM8904_ADC_128_OSR_TST_MODE | WM8904_ADC_BIASX1P5; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_ADC_TEST_0, 57262306a36Sopenharmony_ci WM8904_ADC_128_OSR_TST_MODE | WM8904_ADC_BIASX1P5, 57362306a36Sopenharmony_ci val); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci return ret; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic const struct snd_kcontrol_new wm8904_adc_snd_controls[] = { 57962306a36Sopenharmony_ciSOC_DOUBLE_R_TLV("Digital Capture Volume", WM8904_ADC_DIGITAL_VOLUME_LEFT, 58062306a36Sopenharmony_ci WM8904_ADC_DIGITAL_VOLUME_RIGHT, 1, 119, 0, digital_tlv), 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci/* No TLV since it depends on mode */ 58362306a36Sopenharmony_ciSOC_DOUBLE_R("Capture Volume", WM8904_ANALOGUE_LEFT_INPUT_0, 58462306a36Sopenharmony_ci WM8904_ANALOGUE_RIGHT_INPUT_0, 0, 31, 0), 58562306a36Sopenharmony_ciSOC_DOUBLE_R("Capture Switch", WM8904_ANALOGUE_LEFT_INPUT_0, 58662306a36Sopenharmony_ci WM8904_ANALOGUE_RIGHT_INPUT_0, 7, 1, 1), 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ciSOC_SINGLE("High Pass Filter Switch", WM8904_ADC_DIGITAL_0, 4, 1, 0), 58962306a36Sopenharmony_ciSOC_ENUM("High Pass Filter Mode", hpf_mode), 59062306a36Sopenharmony_ciSOC_SINGLE_EXT("ADC 128x OSR Switch", WM8904_ANALOGUE_ADC_0, 0, 1, 0, 59162306a36Sopenharmony_ci snd_soc_get_volsw, wm8904_adc_osr_put), 59262306a36Sopenharmony_ci}; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic const char *drc_path_text[] = { 59562306a36Sopenharmony_ci "ADC", "DAC" 59662306a36Sopenharmony_ci}; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(drc_path, WM8904_DRC_0, 14, drc_path_text); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic const struct snd_kcontrol_new wm8904_dac_snd_controls[] = { 60162306a36Sopenharmony_ciSOC_SINGLE_TLV("Digital Playback Boost Volume", 60262306a36Sopenharmony_ci WM8904_AUDIO_INTERFACE_0, 9, 3, 0, dac_boost_tlv), 60362306a36Sopenharmony_ciSOC_DOUBLE_R_TLV("Digital Playback Volume", WM8904_DAC_DIGITAL_VOLUME_LEFT, 60462306a36Sopenharmony_ci WM8904_DAC_DIGITAL_VOLUME_RIGHT, 1, 96, 0, digital_tlv), 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ciSOC_DOUBLE_R_TLV("Headphone Volume", WM8904_ANALOGUE_OUT1_LEFT, 60762306a36Sopenharmony_ci WM8904_ANALOGUE_OUT1_RIGHT, 0, 63, 0, out_tlv), 60862306a36Sopenharmony_ciSOC_DOUBLE_R("Headphone Switch", WM8904_ANALOGUE_OUT1_LEFT, 60962306a36Sopenharmony_ci WM8904_ANALOGUE_OUT1_RIGHT, 8, 1, 1), 61062306a36Sopenharmony_ciSOC_DOUBLE_R("Headphone ZC Switch", WM8904_ANALOGUE_OUT1_LEFT, 61162306a36Sopenharmony_ci WM8904_ANALOGUE_OUT1_RIGHT, 6, 1, 0), 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ciSOC_DOUBLE_R_TLV("Line Output Volume", WM8904_ANALOGUE_OUT2_LEFT, 61462306a36Sopenharmony_ci WM8904_ANALOGUE_OUT2_RIGHT, 0, 63, 0, out_tlv), 61562306a36Sopenharmony_ciSOC_DOUBLE_R("Line Output Switch", WM8904_ANALOGUE_OUT2_LEFT, 61662306a36Sopenharmony_ci WM8904_ANALOGUE_OUT2_RIGHT, 8, 1, 1), 61762306a36Sopenharmony_ciSOC_DOUBLE_R("Line Output ZC Switch", WM8904_ANALOGUE_OUT2_LEFT, 61862306a36Sopenharmony_ci WM8904_ANALOGUE_OUT2_RIGHT, 6, 1, 0), 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ciSOC_SINGLE("EQ Switch", WM8904_EQ1, 0, 1, 0), 62162306a36Sopenharmony_ciSOC_SINGLE("DRC Switch", WM8904_DRC_0, 15, 1, 0), 62262306a36Sopenharmony_ciSOC_ENUM("DRC Path", drc_path), 62362306a36Sopenharmony_ciSOC_SINGLE("DAC OSRx2 Switch", WM8904_DAC_DIGITAL_1, 6, 1, 0), 62462306a36Sopenharmony_ciSOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, 62562306a36Sopenharmony_ci wm8904_get_deemph, wm8904_put_deemph), 62662306a36Sopenharmony_ci}; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic const struct snd_kcontrol_new wm8904_snd_controls[] = { 62962306a36Sopenharmony_ciSOC_DOUBLE_TLV("Digital Sidetone Volume", WM8904_DAC_DIGITAL_0, 4, 8, 15, 0, 63062306a36Sopenharmony_ci sidetone_tlv), 63162306a36Sopenharmony_ci}; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic const struct snd_kcontrol_new wm8904_eq_controls[] = { 63462306a36Sopenharmony_ciSOC_SINGLE_TLV("EQ1 Volume", WM8904_EQ2, 0, 24, 0, eq_tlv), 63562306a36Sopenharmony_ciSOC_SINGLE_TLV("EQ2 Volume", WM8904_EQ3, 0, 24, 0, eq_tlv), 63662306a36Sopenharmony_ciSOC_SINGLE_TLV("EQ3 Volume", WM8904_EQ4, 0, 24, 0, eq_tlv), 63762306a36Sopenharmony_ciSOC_SINGLE_TLV("EQ4 Volume", WM8904_EQ5, 0, 24, 0, eq_tlv), 63862306a36Sopenharmony_ciSOC_SINGLE_TLV("EQ5 Volume", WM8904_EQ6, 0, 24, 0, eq_tlv), 63962306a36Sopenharmony_ci}; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic int cp_event(struct snd_soc_dapm_widget *w, 64262306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci if (WARN_ON(event != SND_SOC_DAPM_POST_PMU)) 64562306a36Sopenharmony_ci return -EINVAL; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* Maximum startup time */ 64862306a36Sopenharmony_ci udelay(500); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return 0; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic int sysclk_event(struct snd_soc_dapm_widget *w, 65462306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 65762306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci switch (event) { 66062306a36Sopenharmony_ci case SND_SOC_DAPM_PRE_PMU: 66162306a36Sopenharmony_ci /* If we're using the FLL then we only start it when 66262306a36Sopenharmony_ci * required; we assume that the configuration has been 66362306a36Sopenharmony_ci * done previously and all we need to do is kick it 66462306a36Sopenharmony_ci * off. 66562306a36Sopenharmony_ci */ 66662306a36Sopenharmony_ci switch (wm8904->sysclk_src) { 66762306a36Sopenharmony_ci case WM8904_CLK_FLL: 66862306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, 66962306a36Sopenharmony_ci WM8904_FLL_OSC_ENA, 67062306a36Sopenharmony_ci WM8904_FLL_OSC_ENA); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, 67362306a36Sopenharmony_ci WM8904_FLL_ENA, 67462306a36Sopenharmony_ci WM8904_FLL_ENA); 67562306a36Sopenharmony_ci break; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci default: 67862306a36Sopenharmony_ci break; 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci case SND_SOC_DAPM_POST_PMD: 68362306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, 68462306a36Sopenharmony_ci WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0); 68562306a36Sopenharmony_ci break; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return 0; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic int out_pga_event(struct snd_soc_dapm_widget *w, 69262306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 69562306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 69662306a36Sopenharmony_ci int reg, val; 69762306a36Sopenharmony_ci int dcs_mask; 69862306a36Sopenharmony_ci int dcs_l, dcs_r; 69962306a36Sopenharmony_ci int dcs_l_reg, dcs_r_reg; 70062306a36Sopenharmony_ci int an_out_reg; 70162306a36Sopenharmony_ci int timeout; 70262306a36Sopenharmony_ci int pwr_reg; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* This code is shared between HP and LINEOUT; we do all our 70562306a36Sopenharmony_ci * power management in stereo pairs to avoid latency issues so 70662306a36Sopenharmony_ci * we reuse shift to identify which rather than strcmp() the 70762306a36Sopenharmony_ci * name. */ 70862306a36Sopenharmony_ci reg = w->shift; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci switch (reg) { 71162306a36Sopenharmony_ci case WM8904_ANALOGUE_HP_0: 71262306a36Sopenharmony_ci pwr_reg = WM8904_POWER_MANAGEMENT_2; 71362306a36Sopenharmony_ci dcs_mask = WM8904_DCS_ENA_CHAN_0 | WM8904_DCS_ENA_CHAN_1; 71462306a36Sopenharmony_ci dcs_r_reg = WM8904_DC_SERVO_8; 71562306a36Sopenharmony_ci dcs_l_reg = WM8904_DC_SERVO_9; 71662306a36Sopenharmony_ci an_out_reg = WM8904_ANALOGUE_OUT1_LEFT; 71762306a36Sopenharmony_ci dcs_l = 0; 71862306a36Sopenharmony_ci dcs_r = 1; 71962306a36Sopenharmony_ci break; 72062306a36Sopenharmony_ci case WM8904_ANALOGUE_LINEOUT_0: 72162306a36Sopenharmony_ci pwr_reg = WM8904_POWER_MANAGEMENT_3; 72262306a36Sopenharmony_ci dcs_mask = WM8904_DCS_ENA_CHAN_2 | WM8904_DCS_ENA_CHAN_3; 72362306a36Sopenharmony_ci dcs_r_reg = WM8904_DC_SERVO_6; 72462306a36Sopenharmony_ci dcs_l_reg = WM8904_DC_SERVO_7; 72562306a36Sopenharmony_ci an_out_reg = WM8904_ANALOGUE_OUT2_LEFT; 72662306a36Sopenharmony_ci dcs_l = 2; 72762306a36Sopenharmony_ci dcs_r = 3; 72862306a36Sopenharmony_ci break; 72962306a36Sopenharmony_ci default: 73062306a36Sopenharmony_ci WARN(1, "Invalid reg %d\n", reg); 73162306a36Sopenharmony_ci return -EINVAL; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci switch (event) { 73562306a36Sopenharmony_ci case SND_SOC_DAPM_PRE_PMU: 73662306a36Sopenharmony_ci /* Power on the PGAs */ 73762306a36Sopenharmony_ci snd_soc_component_update_bits(component, pwr_reg, 73862306a36Sopenharmony_ci WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA, 73962306a36Sopenharmony_ci WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Power on the amplifier */ 74262306a36Sopenharmony_ci snd_soc_component_update_bits(component, reg, 74362306a36Sopenharmony_ci WM8904_HPL_ENA | WM8904_HPR_ENA, 74462306a36Sopenharmony_ci WM8904_HPL_ENA | WM8904_HPR_ENA); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* Enable the first stage */ 74862306a36Sopenharmony_ci snd_soc_component_update_bits(component, reg, 74962306a36Sopenharmony_ci WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY, 75062306a36Sopenharmony_ci WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* Power up the DC servo */ 75362306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_DC_SERVO_0, 75462306a36Sopenharmony_ci dcs_mask, dcs_mask); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* Either calibrate the DC servo or restore cached state 75762306a36Sopenharmony_ci * if we have that. 75862306a36Sopenharmony_ci */ 75962306a36Sopenharmony_ci if (wm8904->dcs_state[dcs_l] || wm8904->dcs_state[dcs_r]) { 76062306a36Sopenharmony_ci dev_dbg(component->dev, "Restoring DC servo state\n"); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci snd_soc_component_write(component, dcs_l_reg, 76362306a36Sopenharmony_ci wm8904->dcs_state[dcs_l]); 76462306a36Sopenharmony_ci snd_soc_component_write(component, dcs_r_reg, 76562306a36Sopenharmony_ci wm8904->dcs_state[dcs_r]); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci snd_soc_component_write(component, WM8904_DC_SERVO_1, dcs_mask); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci timeout = 20; 77062306a36Sopenharmony_ci } else { 77162306a36Sopenharmony_ci dev_dbg(component->dev, "Calibrating DC servo\n"); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci snd_soc_component_write(component, WM8904_DC_SERVO_1, 77462306a36Sopenharmony_ci dcs_mask << WM8904_DCS_TRIG_STARTUP_0_SHIFT); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci timeout = 500; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* Wait for DC servo to complete */ 78062306a36Sopenharmony_ci dcs_mask <<= WM8904_DCS_CAL_COMPLETE_SHIFT; 78162306a36Sopenharmony_ci do { 78262306a36Sopenharmony_ci val = snd_soc_component_read(component, WM8904_DC_SERVO_READBACK_0); 78362306a36Sopenharmony_ci if ((val & dcs_mask) == dcs_mask) 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci msleep(1); 78762306a36Sopenharmony_ci } while (--timeout); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if ((val & dcs_mask) != dcs_mask) 79062306a36Sopenharmony_ci dev_warn(component->dev, "DC servo timed out\n"); 79162306a36Sopenharmony_ci else 79262306a36Sopenharmony_ci dev_dbg(component->dev, "DC servo ready\n"); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* Enable the output stage */ 79562306a36Sopenharmony_ci snd_soc_component_update_bits(component, reg, 79662306a36Sopenharmony_ci WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP, 79762306a36Sopenharmony_ci WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* Update volume, requires PGA to be powered */ 80062306a36Sopenharmony_ci val = snd_soc_component_read(component, an_out_reg); 80162306a36Sopenharmony_ci snd_soc_component_write(component, an_out_reg, val); 80262306a36Sopenharmony_ci break; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci case SND_SOC_DAPM_POST_PMU: 80562306a36Sopenharmony_ci /* Unshort the output itself */ 80662306a36Sopenharmony_ci snd_soc_component_update_bits(component, reg, 80762306a36Sopenharmony_ci WM8904_HPL_RMV_SHORT | 80862306a36Sopenharmony_ci WM8904_HPR_RMV_SHORT, 80962306a36Sopenharmony_ci WM8904_HPL_RMV_SHORT | 81062306a36Sopenharmony_ci WM8904_HPR_RMV_SHORT); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci break; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci case SND_SOC_DAPM_PRE_PMD: 81562306a36Sopenharmony_ci /* Short the output */ 81662306a36Sopenharmony_ci snd_soc_component_update_bits(component, reg, 81762306a36Sopenharmony_ci WM8904_HPL_RMV_SHORT | 81862306a36Sopenharmony_ci WM8904_HPR_RMV_SHORT, 0); 81962306a36Sopenharmony_ci break; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci case SND_SOC_DAPM_POST_PMD: 82262306a36Sopenharmony_ci /* Cache the DC servo configuration; this will be 82362306a36Sopenharmony_ci * invalidated if we change the configuration. */ 82462306a36Sopenharmony_ci wm8904->dcs_state[dcs_l] = snd_soc_component_read(component, dcs_l_reg); 82562306a36Sopenharmony_ci wm8904->dcs_state[dcs_r] = snd_soc_component_read(component, dcs_r_reg); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_DC_SERVO_0, 82862306a36Sopenharmony_ci dcs_mask, 0); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci /* Disable the amplifier input and output stages */ 83162306a36Sopenharmony_ci snd_soc_component_update_bits(component, reg, 83262306a36Sopenharmony_ci WM8904_HPL_ENA | WM8904_HPR_ENA | 83362306a36Sopenharmony_ci WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY | 83462306a36Sopenharmony_ci WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP, 83562306a36Sopenharmony_ci 0); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* PGAs too */ 83862306a36Sopenharmony_ci snd_soc_component_update_bits(component, pwr_reg, 83962306a36Sopenharmony_ci WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA, 84062306a36Sopenharmony_ci 0); 84162306a36Sopenharmony_ci break; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci return 0; 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cistatic const char *input_mode_text[] = { 84862306a36Sopenharmony_ci "Single-Ended", "Differential Line", "Differential Mic" 84962306a36Sopenharmony_ci}; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic const char *lin_text[] = { 85262306a36Sopenharmony_ci "IN1L", "IN2L", "IN3L" 85362306a36Sopenharmony_ci}; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(lin_enum, WM8904_ANALOGUE_LEFT_INPUT_1, 2, 85662306a36Sopenharmony_ci lin_text); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic const struct snd_kcontrol_new lin_mux = 85962306a36Sopenharmony_ci SOC_DAPM_ENUM("Left Capture Mux", lin_enum); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(lin_inv_enum, WM8904_ANALOGUE_LEFT_INPUT_1, 4, 86262306a36Sopenharmony_ci lin_text); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic const struct snd_kcontrol_new lin_inv_mux = 86562306a36Sopenharmony_ci SOC_DAPM_ENUM("Left Capture Inverting Mux", lin_inv_enum); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(lin_mode_enum, 86862306a36Sopenharmony_ci WM8904_ANALOGUE_LEFT_INPUT_1, 0, 86962306a36Sopenharmony_ci input_mode_text); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic const struct snd_kcontrol_new lin_mode = 87262306a36Sopenharmony_ci SOC_DAPM_ENUM("Left Capture Mode", lin_mode_enum); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic const char *rin_text[] = { 87562306a36Sopenharmony_ci "IN1R", "IN2R", "IN3R" 87662306a36Sopenharmony_ci}; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(rin_enum, WM8904_ANALOGUE_RIGHT_INPUT_1, 2, 87962306a36Sopenharmony_ci rin_text); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic const struct snd_kcontrol_new rin_mux = 88262306a36Sopenharmony_ci SOC_DAPM_ENUM("Right Capture Mux", rin_enum); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(rin_inv_enum, WM8904_ANALOGUE_RIGHT_INPUT_1, 4, 88562306a36Sopenharmony_ci rin_text); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_cistatic const struct snd_kcontrol_new rin_inv_mux = 88862306a36Sopenharmony_ci SOC_DAPM_ENUM("Right Capture Inverting Mux", rin_inv_enum); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(rin_mode_enum, 89162306a36Sopenharmony_ci WM8904_ANALOGUE_RIGHT_INPUT_1, 0, 89262306a36Sopenharmony_ci input_mode_text); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_cistatic const struct snd_kcontrol_new rin_mode = 89562306a36Sopenharmony_ci SOC_DAPM_ENUM("Right Capture Mode", rin_mode_enum); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_cistatic const char *aif_text[] = { 89862306a36Sopenharmony_ci "Left", "Right" 89962306a36Sopenharmony_ci}; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(aifoutl_enum, WM8904_AUDIO_INTERFACE_0, 7, 90262306a36Sopenharmony_ci aif_text); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_cistatic const struct snd_kcontrol_new aifoutl_mux = 90562306a36Sopenharmony_ci SOC_DAPM_ENUM("AIFOUTL Mux", aifoutl_enum); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(aifoutr_enum, WM8904_AUDIO_INTERFACE_0, 6, 90862306a36Sopenharmony_ci aif_text); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cistatic const struct snd_kcontrol_new aifoutr_mux = 91162306a36Sopenharmony_ci SOC_DAPM_ENUM("AIFOUTR Mux", aifoutr_enum); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(aifinl_enum, WM8904_AUDIO_INTERFACE_0, 5, 91462306a36Sopenharmony_ci aif_text); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic const struct snd_kcontrol_new aifinl_mux = 91762306a36Sopenharmony_ci SOC_DAPM_ENUM("AIFINL Mux", aifinl_enum); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(aifinr_enum, WM8904_AUDIO_INTERFACE_0, 4, 92062306a36Sopenharmony_ci aif_text); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic const struct snd_kcontrol_new aifinr_mux = 92362306a36Sopenharmony_ci SOC_DAPM_ENUM("AIFINR Mux", aifinr_enum); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget wm8904_core_dapm_widgets[] = { 92662306a36Sopenharmony_ciSND_SOC_DAPM_SUPPLY("SYSCLK", WM8904_CLOCK_RATES_2, 2, 0, sysclk_event, 92762306a36Sopenharmony_ci SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), 92862306a36Sopenharmony_ciSND_SOC_DAPM_SUPPLY("CLK_DSP", WM8904_CLOCK_RATES_2, 1, 0, NULL, 0), 92962306a36Sopenharmony_ciSND_SOC_DAPM_SUPPLY("TOCLK", WM8904_CLOCK_RATES_2, 0, 0, NULL, 0), 93062306a36Sopenharmony_ci}; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget wm8904_adc_dapm_widgets[] = { 93362306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("IN1L"), 93462306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("IN1R"), 93562306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("IN2L"), 93662306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("IN2R"), 93762306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("IN3L"), 93862306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("IN3R"), 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ciSND_SOC_DAPM_SUPPLY("MICBIAS", WM8904_MIC_BIAS_CONTROL_0, 0, 0, NULL, 0), 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ciSND_SOC_DAPM_MUX("Left Capture Mux", SND_SOC_NOPM, 0, 0, &lin_mux), 94362306a36Sopenharmony_ciSND_SOC_DAPM_MUX("Left Capture Inverting Mux", SND_SOC_NOPM, 0, 0, 94462306a36Sopenharmony_ci &lin_inv_mux), 94562306a36Sopenharmony_ciSND_SOC_DAPM_MUX("Left Capture Mode", SND_SOC_NOPM, 0, 0, &lin_mode), 94662306a36Sopenharmony_ciSND_SOC_DAPM_MUX("Right Capture Mux", SND_SOC_NOPM, 0, 0, &rin_mux), 94762306a36Sopenharmony_ciSND_SOC_DAPM_MUX("Right Capture Inverting Mux", SND_SOC_NOPM, 0, 0, 94862306a36Sopenharmony_ci &rin_inv_mux), 94962306a36Sopenharmony_ciSND_SOC_DAPM_MUX("Right Capture Mode", SND_SOC_NOPM, 0, 0, &rin_mode), 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ciSND_SOC_DAPM_PGA("Left Capture PGA", WM8904_POWER_MANAGEMENT_0, 1, 0, 95262306a36Sopenharmony_ci NULL, 0), 95362306a36Sopenharmony_ciSND_SOC_DAPM_PGA("Right Capture PGA", WM8904_POWER_MANAGEMENT_0, 0, 0, 95462306a36Sopenharmony_ci NULL, 0), 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ciSND_SOC_DAPM_ADC("ADCL", NULL, WM8904_POWER_MANAGEMENT_6, 1, 0), 95762306a36Sopenharmony_ciSND_SOC_DAPM_ADC("ADCR", NULL, WM8904_POWER_MANAGEMENT_6, 0, 0), 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ciSND_SOC_DAPM_MUX("AIFOUTL Mux", SND_SOC_NOPM, 0, 0, &aifoutl_mux), 96062306a36Sopenharmony_ciSND_SOC_DAPM_MUX("AIFOUTR Mux", SND_SOC_NOPM, 0, 0, &aifoutr_mux), 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ciSND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0), 96362306a36Sopenharmony_ciSND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0), 96462306a36Sopenharmony_ci}; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget wm8904_dac_dapm_widgets[] = { 96762306a36Sopenharmony_ciSND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0), 96862306a36Sopenharmony_ciSND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0), 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ciSND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &aifinl_mux), 97162306a36Sopenharmony_ciSND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &aifinr_mux), 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ciSND_SOC_DAPM_DAC("DACL", NULL, WM8904_POWER_MANAGEMENT_6, 3, 0), 97462306a36Sopenharmony_ciSND_SOC_DAPM_DAC("DACR", NULL, WM8904_POWER_MANAGEMENT_6, 2, 0), 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ciSND_SOC_DAPM_SUPPLY("Charge pump", WM8904_CHARGE_PUMP_0, 0, 0, cp_event, 97762306a36Sopenharmony_ci SND_SOC_DAPM_POST_PMU), 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ciSND_SOC_DAPM_PGA("HPL PGA", SND_SOC_NOPM, 1, 0, NULL, 0), 98062306a36Sopenharmony_ciSND_SOC_DAPM_PGA("HPR PGA", SND_SOC_NOPM, 0, 0, NULL, 0), 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ciSND_SOC_DAPM_PGA("LINEL PGA", SND_SOC_NOPM, 1, 0, NULL, 0), 98362306a36Sopenharmony_ciSND_SOC_DAPM_PGA("LINER PGA", SND_SOC_NOPM, 0, 0, NULL, 0), 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ciSND_SOC_DAPM_PGA_E("Headphone Output", SND_SOC_NOPM, WM8904_ANALOGUE_HP_0, 98662306a36Sopenharmony_ci 0, NULL, 0, out_pga_event, 98762306a36Sopenharmony_ci SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | 98862306a36Sopenharmony_ci SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), 98962306a36Sopenharmony_ciSND_SOC_DAPM_PGA_E("Line Output", SND_SOC_NOPM, WM8904_ANALOGUE_LINEOUT_0, 99062306a36Sopenharmony_ci 0, NULL, 0, out_pga_event, 99162306a36Sopenharmony_ci SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | 99262306a36Sopenharmony_ci SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("HPOUTL"), 99562306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("HPOUTR"), 99662306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("LINEOUTL"), 99762306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("LINEOUTR"), 99862306a36Sopenharmony_ci}; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_cistatic const char *out_mux_text[] = { 100162306a36Sopenharmony_ci "DAC", "Bypass" 100262306a36Sopenharmony_ci}; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(hpl_enum, WM8904_ANALOGUE_OUT12_ZC, 3, 100562306a36Sopenharmony_ci out_mux_text); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_cistatic const struct snd_kcontrol_new hpl_mux = 100862306a36Sopenharmony_ci SOC_DAPM_ENUM("HPL Mux", hpl_enum); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(hpr_enum, WM8904_ANALOGUE_OUT12_ZC, 2, 101162306a36Sopenharmony_ci out_mux_text); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistatic const struct snd_kcontrol_new hpr_mux = 101462306a36Sopenharmony_ci SOC_DAPM_ENUM("HPR Mux", hpr_enum); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(linel_enum, WM8904_ANALOGUE_OUT12_ZC, 1, 101762306a36Sopenharmony_ci out_mux_text); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_cistatic const struct snd_kcontrol_new linel_mux = 102062306a36Sopenharmony_ci SOC_DAPM_ENUM("LINEL Mux", linel_enum); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(liner_enum, WM8904_ANALOGUE_OUT12_ZC, 0, 102362306a36Sopenharmony_ci out_mux_text); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_cistatic const struct snd_kcontrol_new liner_mux = 102662306a36Sopenharmony_ci SOC_DAPM_ENUM("LINER Mux", liner_enum); 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_cistatic const char *sidetone_text[] = { 102962306a36Sopenharmony_ci "None", "Left", "Right" 103062306a36Sopenharmony_ci}; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(dacl_sidetone_enum, WM8904_DAC_DIGITAL_0, 2, 103362306a36Sopenharmony_ci sidetone_text); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_cistatic const struct snd_kcontrol_new dacl_sidetone_mux = 103662306a36Sopenharmony_ci SOC_DAPM_ENUM("Left Sidetone Mux", dacl_sidetone_enum); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(dacr_sidetone_enum, WM8904_DAC_DIGITAL_0, 0, 103962306a36Sopenharmony_ci sidetone_text); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_cistatic const struct snd_kcontrol_new dacr_sidetone_mux = 104262306a36Sopenharmony_ci SOC_DAPM_ENUM("Right Sidetone Mux", dacr_sidetone_enum); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget wm8904_dapm_widgets[] = { 104562306a36Sopenharmony_ciSND_SOC_DAPM_SUPPLY("Class G", WM8904_CLASS_W_0, 0, 1, NULL, 0), 104662306a36Sopenharmony_ciSND_SOC_DAPM_PGA("Left Bypass", SND_SOC_NOPM, 0, 0, NULL, 0), 104762306a36Sopenharmony_ciSND_SOC_DAPM_PGA("Right Bypass", SND_SOC_NOPM, 0, 0, NULL, 0), 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ciSND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &dacl_sidetone_mux), 105062306a36Sopenharmony_ciSND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &dacr_sidetone_mux), 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ciSND_SOC_DAPM_MUX("HPL Mux", SND_SOC_NOPM, 0, 0, &hpl_mux), 105362306a36Sopenharmony_ciSND_SOC_DAPM_MUX("HPR Mux", SND_SOC_NOPM, 0, 0, &hpr_mux), 105462306a36Sopenharmony_ciSND_SOC_DAPM_MUX("LINEL Mux", SND_SOC_NOPM, 0, 0, &linel_mux), 105562306a36Sopenharmony_ciSND_SOC_DAPM_MUX("LINER Mux", SND_SOC_NOPM, 0, 0, &liner_mux), 105662306a36Sopenharmony_ci}; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_cistatic const struct snd_soc_dapm_route core_intercon[] = { 105962306a36Sopenharmony_ci { "CLK_DSP", NULL, "SYSCLK" }, 106062306a36Sopenharmony_ci { "TOCLK", NULL, "SYSCLK" }, 106162306a36Sopenharmony_ci}; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_cistatic const struct snd_soc_dapm_route adc_intercon[] = { 106462306a36Sopenharmony_ci { "Left Capture Mux", "IN1L", "IN1L" }, 106562306a36Sopenharmony_ci { "Left Capture Mux", "IN2L", "IN2L" }, 106662306a36Sopenharmony_ci { "Left Capture Mux", "IN3L", "IN3L" }, 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci { "Left Capture Inverting Mux", "IN1L", "IN1L" }, 106962306a36Sopenharmony_ci { "Left Capture Inverting Mux", "IN2L", "IN2L" }, 107062306a36Sopenharmony_ci { "Left Capture Inverting Mux", "IN3L", "IN3L" }, 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci { "Left Capture Mode", "Single-Ended", "Left Capture Inverting Mux" }, 107362306a36Sopenharmony_ci { "Left Capture Mode", "Differential Line", "Left Capture Mux" }, 107462306a36Sopenharmony_ci { "Left Capture Mode", "Differential Line", "Left Capture Inverting Mux" }, 107562306a36Sopenharmony_ci { "Left Capture Mode", "Differential Mic", "Left Capture Mux" }, 107662306a36Sopenharmony_ci { "Left Capture Mode", "Differential Mic", "Left Capture Inverting Mux" }, 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci { "Right Capture Mux", "IN1R", "IN1R" }, 107962306a36Sopenharmony_ci { "Right Capture Mux", "IN2R", "IN2R" }, 108062306a36Sopenharmony_ci { "Right Capture Mux", "IN3R", "IN3R" }, 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci { "Right Capture Inverting Mux", "IN1R", "IN1R" }, 108362306a36Sopenharmony_ci { "Right Capture Inverting Mux", "IN2R", "IN2R" }, 108462306a36Sopenharmony_ci { "Right Capture Inverting Mux", "IN3R", "IN3R" }, 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci { "Right Capture Mode", "Single-Ended", "Right Capture Inverting Mux" }, 108762306a36Sopenharmony_ci { "Right Capture Mode", "Differential Line", "Right Capture Mux" }, 108862306a36Sopenharmony_ci { "Right Capture Mode", "Differential Line", "Right Capture Inverting Mux" }, 108962306a36Sopenharmony_ci { "Right Capture Mode", "Differential Mic", "Right Capture Mux" }, 109062306a36Sopenharmony_ci { "Right Capture Mode", "Differential Mic", "Right Capture Inverting Mux" }, 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci { "Left Capture PGA", NULL, "Left Capture Mode" }, 109362306a36Sopenharmony_ci { "Right Capture PGA", NULL, "Right Capture Mode" }, 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci { "AIFOUTL Mux", "Left", "ADCL" }, 109662306a36Sopenharmony_ci { "AIFOUTL Mux", "Right", "ADCR" }, 109762306a36Sopenharmony_ci { "AIFOUTR Mux", "Left", "ADCL" }, 109862306a36Sopenharmony_ci { "AIFOUTR Mux", "Right", "ADCR" }, 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci { "AIFOUTL", NULL, "AIFOUTL Mux" }, 110162306a36Sopenharmony_ci { "AIFOUTR", NULL, "AIFOUTR Mux" }, 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci { "ADCL", NULL, "CLK_DSP" }, 110462306a36Sopenharmony_ci { "ADCL", NULL, "Left Capture PGA" }, 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci { "ADCR", NULL, "CLK_DSP" }, 110762306a36Sopenharmony_ci { "ADCR", NULL, "Right Capture PGA" }, 110862306a36Sopenharmony_ci}; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_cistatic const struct snd_soc_dapm_route dac_intercon[] = { 111162306a36Sopenharmony_ci { "DACL Mux", "Left", "AIFINL" }, 111262306a36Sopenharmony_ci { "DACL Mux", "Right", "AIFINR" }, 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci { "DACR Mux", "Left", "AIFINL" }, 111562306a36Sopenharmony_ci { "DACR Mux", "Right", "AIFINR" }, 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci { "DACL", NULL, "DACL Mux" }, 111862306a36Sopenharmony_ci { "DACL", NULL, "CLK_DSP" }, 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci { "DACR", NULL, "DACR Mux" }, 112162306a36Sopenharmony_ci { "DACR", NULL, "CLK_DSP" }, 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci { "Charge pump", NULL, "SYSCLK" }, 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci { "Headphone Output", NULL, "HPL PGA" }, 112662306a36Sopenharmony_ci { "Headphone Output", NULL, "HPR PGA" }, 112762306a36Sopenharmony_ci { "Headphone Output", NULL, "Charge pump" }, 112862306a36Sopenharmony_ci { "Headphone Output", NULL, "TOCLK" }, 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci { "Line Output", NULL, "LINEL PGA" }, 113162306a36Sopenharmony_ci { "Line Output", NULL, "LINER PGA" }, 113262306a36Sopenharmony_ci { "Line Output", NULL, "Charge pump" }, 113362306a36Sopenharmony_ci { "Line Output", NULL, "TOCLK" }, 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci { "HPOUTL", NULL, "Headphone Output" }, 113662306a36Sopenharmony_ci { "HPOUTR", NULL, "Headphone Output" }, 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci { "LINEOUTL", NULL, "Line Output" }, 113962306a36Sopenharmony_ci { "LINEOUTR", NULL, "Line Output" }, 114062306a36Sopenharmony_ci}; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_cistatic const struct snd_soc_dapm_route wm8904_intercon[] = { 114362306a36Sopenharmony_ci { "Left Sidetone", "Left", "ADCL" }, 114462306a36Sopenharmony_ci { "Left Sidetone", "Right", "ADCR" }, 114562306a36Sopenharmony_ci { "DACL", NULL, "Left Sidetone" }, 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci { "Right Sidetone", "Left", "ADCL" }, 114862306a36Sopenharmony_ci { "Right Sidetone", "Right", "ADCR" }, 114962306a36Sopenharmony_ci { "DACR", NULL, "Right Sidetone" }, 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci { "Left Bypass", NULL, "Class G" }, 115262306a36Sopenharmony_ci { "Left Bypass", NULL, "Left Capture PGA" }, 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci { "Right Bypass", NULL, "Class G" }, 115562306a36Sopenharmony_ci { "Right Bypass", NULL, "Right Capture PGA" }, 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci { "HPL Mux", "DAC", "DACL" }, 115862306a36Sopenharmony_ci { "HPL Mux", "Bypass", "Left Bypass" }, 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci { "HPR Mux", "DAC", "DACR" }, 116162306a36Sopenharmony_ci { "HPR Mux", "Bypass", "Right Bypass" }, 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci { "LINEL Mux", "DAC", "DACL" }, 116462306a36Sopenharmony_ci { "LINEL Mux", "Bypass", "Left Bypass" }, 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci { "LINER Mux", "DAC", "DACR" }, 116762306a36Sopenharmony_ci { "LINER Mux", "Bypass", "Right Bypass" }, 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci { "HPL PGA", NULL, "HPL Mux" }, 117062306a36Sopenharmony_ci { "HPR PGA", NULL, "HPR Mux" }, 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci { "LINEL PGA", NULL, "LINEL Mux" }, 117362306a36Sopenharmony_ci { "LINER PGA", NULL, "LINER Mux" }, 117462306a36Sopenharmony_ci}; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic const struct snd_soc_dapm_route wm8912_intercon[] = { 117762306a36Sopenharmony_ci { "HPL PGA", NULL, "DACL" }, 117862306a36Sopenharmony_ci { "HPR PGA", NULL, "DACR" }, 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci { "LINEL PGA", NULL, "DACL" }, 118162306a36Sopenharmony_ci { "LINER PGA", NULL, "DACR" }, 118262306a36Sopenharmony_ci}; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_cistatic int wm8904_add_widgets(struct snd_soc_component *component) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 118762306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci snd_soc_dapm_new_controls(dapm, wm8904_core_dapm_widgets, 119062306a36Sopenharmony_ci ARRAY_SIZE(wm8904_core_dapm_widgets)); 119162306a36Sopenharmony_ci snd_soc_dapm_add_routes(dapm, core_intercon, 119262306a36Sopenharmony_ci ARRAY_SIZE(core_intercon)); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci switch (wm8904->devtype) { 119562306a36Sopenharmony_ci case WM8904: 119662306a36Sopenharmony_ci snd_soc_add_component_controls(component, wm8904_adc_snd_controls, 119762306a36Sopenharmony_ci ARRAY_SIZE(wm8904_adc_snd_controls)); 119862306a36Sopenharmony_ci snd_soc_add_component_controls(component, wm8904_dac_snd_controls, 119962306a36Sopenharmony_ci ARRAY_SIZE(wm8904_dac_snd_controls)); 120062306a36Sopenharmony_ci snd_soc_add_component_controls(component, wm8904_snd_controls, 120162306a36Sopenharmony_ci ARRAY_SIZE(wm8904_snd_controls)); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci snd_soc_dapm_new_controls(dapm, wm8904_adc_dapm_widgets, 120462306a36Sopenharmony_ci ARRAY_SIZE(wm8904_adc_dapm_widgets)); 120562306a36Sopenharmony_ci snd_soc_dapm_new_controls(dapm, wm8904_dac_dapm_widgets, 120662306a36Sopenharmony_ci ARRAY_SIZE(wm8904_dac_dapm_widgets)); 120762306a36Sopenharmony_ci snd_soc_dapm_new_controls(dapm, wm8904_dapm_widgets, 120862306a36Sopenharmony_ci ARRAY_SIZE(wm8904_dapm_widgets)); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci snd_soc_dapm_add_routes(dapm, adc_intercon, 121162306a36Sopenharmony_ci ARRAY_SIZE(adc_intercon)); 121262306a36Sopenharmony_ci snd_soc_dapm_add_routes(dapm, dac_intercon, 121362306a36Sopenharmony_ci ARRAY_SIZE(dac_intercon)); 121462306a36Sopenharmony_ci snd_soc_dapm_add_routes(dapm, wm8904_intercon, 121562306a36Sopenharmony_ci ARRAY_SIZE(wm8904_intercon)); 121662306a36Sopenharmony_ci break; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci case WM8912: 121962306a36Sopenharmony_ci snd_soc_add_component_controls(component, wm8904_dac_snd_controls, 122062306a36Sopenharmony_ci ARRAY_SIZE(wm8904_dac_snd_controls)); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci snd_soc_dapm_new_controls(dapm, wm8904_dac_dapm_widgets, 122362306a36Sopenharmony_ci ARRAY_SIZE(wm8904_dac_dapm_widgets)); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci snd_soc_dapm_add_routes(dapm, dac_intercon, 122662306a36Sopenharmony_ci ARRAY_SIZE(dac_intercon)); 122762306a36Sopenharmony_ci snd_soc_dapm_add_routes(dapm, wm8912_intercon, 122862306a36Sopenharmony_ci ARRAY_SIZE(wm8912_intercon)); 122962306a36Sopenharmony_ci break; 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci return 0; 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_cistatic struct { 123662306a36Sopenharmony_ci int ratio; 123762306a36Sopenharmony_ci unsigned int clk_sys_rate; 123862306a36Sopenharmony_ci} clk_sys_rates[] = { 123962306a36Sopenharmony_ci { 64, 0 }, 124062306a36Sopenharmony_ci { 128, 1 }, 124162306a36Sopenharmony_ci { 192, 2 }, 124262306a36Sopenharmony_ci { 256, 3 }, 124362306a36Sopenharmony_ci { 384, 4 }, 124462306a36Sopenharmony_ci { 512, 5 }, 124562306a36Sopenharmony_ci { 786, 6 }, 124662306a36Sopenharmony_ci { 1024, 7 }, 124762306a36Sopenharmony_ci { 1408, 8 }, 124862306a36Sopenharmony_ci { 1536, 9 }, 124962306a36Sopenharmony_ci}; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_cistatic struct { 125262306a36Sopenharmony_ci int rate; 125362306a36Sopenharmony_ci int sample_rate; 125462306a36Sopenharmony_ci} sample_rates[] = { 125562306a36Sopenharmony_ci { 8000, 0 }, 125662306a36Sopenharmony_ci { 11025, 1 }, 125762306a36Sopenharmony_ci { 12000, 1 }, 125862306a36Sopenharmony_ci { 16000, 2 }, 125962306a36Sopenharmony_ci { 22050, 3 }, 126062306a36Sopenharmony_ci { 24000, 3 }, 126162306a36Sopenharmony_ci { 32000, 4 }, 126262306a36Sopenharmony_ci { 44100, 5 }, 126362306a36Sopenharmony_ci { 48000, 5 }, 126462306a36Sopenharmony_ci}; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_cistatic struct { 126762306a36Sopenharmony_ci int div; /* *10 due to .5s */ 126862306a36Sopenharmony_ci int bclk_div; 126962306a36Sopenharmony_ci} bclk_divs[] = { 127062306a36Sopenharmony_ci { 10, 0 }, 127162306a36Sopenharmony_ci { 15, 1 }, 127262306a36Sopenharmony_ci { 20, 2 }, 127362306a36Sopenharmony_ci { 30, 3 }, 127462306a36Sopenharmony_ci { 40, 4 }, 127562306a36Sopenharmony_ci { 50, 5 }, 127662306a36Sopenharmony_ci { 55, 6 }, 127762306a36Sopenharmony_ci { 60, 7 }, 127862306a36Sopenharmony_ci { 80, 8 }, 127962306a36Sopenharmony_ci { 100, 9 }, 128062306a36Sopenharmony_ci { 110, 10 }, 128162306a36Sopenharmony_ci { 120, 11 }, 128262306a36Sopenharmony_ci { 160, 12 }, 128362306a36Sopenharmony_ci { 200, 13 }, 128462306a36Sopenharmony_ci { 220, 14 }, 128562306a36Sopenharmony_ci { 240, 16 }, 128662306a36Sopenharmony_ci { 200, 17 }, 128762306a36Sopenharmony_ci { 320, 18 }, 128862306a36Sopenharmony_ci { 440, 19 }, 128962306a36Sopenharmony_ci { 480, 20 }, 129062306a36Sopenharmony_ci}; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_cistatic int wm8904_hw_params(struct snd_pcm_substream *substream, 129462306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 129562306a36Sopenharmony_ci struct snd_soc_dai *dai) 129662306a36Sopenharmony_ci{ 129762306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 129862306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 129962306a36Sopenharmony_ci int ret, i, best, best_val, cur_val; 130062306a36Sopenharmony_ci unsigned int aif1 = 0; 130162306a36Sopenharmony_ci unsigned int aif2 = 0; 130262306a36Sopenharmony_ci unsigned int aif3 = 0; 130362306a36Sopenharmony_ci unsigned int clock1 = 0; 130462306a36Sopenharmony_ci unsigned int dac_digital1 = 0; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci /* What BCLK do we need? */ 130762306a36Sopenharmony_ci wm8904->fs = params_rate(params); 130862306a36Sopenharmony_ci if (wm8904->tdm_slots) { 130962306a36Sopenharmony_ci dev_dbg(component->dev, "Configuring for %d %d bit TDM slots\n", 131062306a36Sopenharmony_ci wm8904->tdm_slots, wm8904->tdm_width); 131162306a36Sopenharmony_ci wm8904->bclk = snd_soc_calc_bclk(wm8904->fs, 131262306a36Sopenharmony_ci wm8904->tdm_width, 2, 131362306a36Sopenharmony_ci wm8904->tdm_slots); 131462306a36Sopenharmony_ci } else { 131562306a36Sopenharmony_ci wm8904->bclk = snd_soc_params_to_bclk(params); 131662306a36Sopenharmony_ci } 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci switch (params_width(params)) { 131962306a36Sopenharmony_ci case 16: 132062306a36Sopenharmony_ci break; 132162306a36Sopenharmony_ci case 20: 132262306a36Sopenharmony_ci aif1 |= 0x40; 132362306a36Sopenharmony_ci break; 132462306a36Sopenharmony_ci case 24: 132562306a36Sopenharmony_ci aif1 |= 0x80; 132662306a36Sopenharmony_ci break; 132762306a36Sopenharmony_ci case 32: 132862306a36Sopenharmony_ci aif1 |= 0xc0; 132962306a36Sopenharmony_ci break; 133062306a36Sopenharmony_ci default: 133162306a36Sopenharmony_ci return -EINVAL; 133262306a36Sopenharmony_ci } 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci dev_dbg(component->dev, "Target BCLK is %dHz\n", wm8904->bclk); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci ret = wm8904_configure_clocking(component); 133862306a36Sopenharmony_ci if (ret != 0) 133962306a36Sopenharmony_ci return ret; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci /* Select nearest CLK_SYS_RATE */ 134262306a36Sopenharmony_ci best = 0; 134362306a36Sopenharmony_ci best_val = abs((wm8904->sysclk_rate / clk_sys_rates[0].ratio) 134462306a36Sopenharmony_ci - wm8904->fs); 134562306a36Sopenharmony_ci for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) { 134662306a36Sopenharmony_ci cur_val = abs((wm8904->sysclk_rate / 134762306a36Sopenharmony_ci clk_sys_rates[i].ratio) - wm8904->fs); 134862306a36Sopenharmony_ci if (cur_val < best_val) { 134962306a36Sopenharmony_ci best = i; 135062306a36Sopenharmony_ci best_val = cur_val; 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci } 135362306a36Sopenharmony_ci dev_dbg(component->dev, "Selected CLK_SYS_RATIO of %d\n", 135462306a36Sopenharmony_ci clk_sys_rates[best].ratio); 135562306a36Sopenharmony_ci clock1 |= (clk_sys_rates[best].clk_sys_rate 135662306a36Sopenharmony_ci << WM8904_CLK_SYS_RATE_SHIFT); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci /* SAMPLE_RATE */ 135962306a36Sopenharmony_ci best = 0; 136062306a36Sopenharmony_ci best_val = abs(wm8904->fs - sample_rates[0].rate); 136162306a36Sopenharmony_ci for (i = 1; i < ARRAY_SIZE(sample_rates); i++) { 136262306a36Sopenharmony_ci /* Closest match */ 136362306a36Sopenharmony_ci cur_val = abs(wm8904->fs - sample_rates[i].rate); 136462306a36Sopenharmony_ci if (cur_val < best_val) { 136562306a36Sopenharmony_ci best = i; 136662306a36Sopenharmony_ci best_val = cur_val; 136762306a36Sopenharmony_ci } 136862306a36Sopenharmony_ci } 136962306a36Sopenharmony_ci dev_dbg(component->dev, "Selected SAMPLE_RATE of %dHz\n", 137062306a36Sopenharmony_ci sample_rates[best].rate); 137162306a36Sopenharmony_ci clock1 |= (sample_rates[best].sample_rate 137262306a36Sopenharmony_ci << WM8904_SAMPLE_RATE_SHIFT); 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci /* Enable sloping stopband filter for low sample rates */ 137562306a36Sopenharmony_ci if (wm8904->fs <= 24000) 137662306a36Sopenharmony_ci dac_digital1 |= WM8904_DAC_SB_FILT; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci /* BCLK_DIV */ 137962306a36Sopenharmony_ci best = 0; 138062306a36Sopenharmony_ci best_val = INT_MAX; 138162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { 138262306a36Sopenharmony_ci cur_val = ((wm8904->sysclk_rate * 10) / bclk_divs[i].div) 138362306a36Sopenharmony_ci - wm8904->bclk; 138462306a36Sopenharmony_ci if (cur_val < 0) /* Table is sorted */ 138562306a36Sopenharmony_ci break; 138662306a36Sopenharmony_ci if (cur_val < best_val) { 138762306a36Sopenharmony_ci best = i; 138862306a36Sopenharmony_ci best_val = cur_val; 138962306a36Sopenharmony_ci } 139062306a36Sopenharmony_ci } 139162306a36Sopenharmony_ci wm8904->bclk = (wm8904->sysclk_rate * 10) / bclk_divs[best].div; 139262306a36Sopenharmony_ci dev_dbg(component->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n", 139362306a36Sopenharmony_ci bclk_divs[best].div, wm8904->bclk); 139462306a36Sopenharmony_ci aif2 |= bclk_divs[best].bclk_div; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci /* LRCLK is a simple fraction of BCLK */ 139762306a36Sopenharmony_ci dev_dbg(component->dev, "LRCLK_RATE is %d\n", wm8904->bclk / wm8904->fs); 139862306a36Sopenharmony_ci aif3 |= wm8904->bclk / wm8904->fs; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci /* Apply the settings */ 140162306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_DAC_DIGITAL_1, 140262306a36Sopenharmony_ci WM8904_DAC_SB_FILT, dac_digital1); 140362306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_1, 140462306a36Sopenharmony_ci WM8904_AIF_WL_MASK, aif1); 140562306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_2, 140662306a36Sopenharmony_ci WM8904_BCLK_DIV_MASK, aif2); 140762306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_3, 140862306a36Sopenharmony_ci WM8904_LRCLK_RATE_MASK, aif3); 140962306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_1, 141062306a36Sopenharmony_ci WM8904_SAMPLE_RATE_MASK | 141162306a36Sopenharmony_ci WM8904_CLK_SYS_RATE_MASK, clock1); 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci /* Update filters for the new settings */ 141462306a36Sopenharmony_ci wm8904_set_retune_mobile(component); 141562306a36Sopenharmony_ci wm8904_set_deemph(component); 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci return 0; 141862306a36Sopenharmony_ci} 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_cistatic int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 142162306a36Sopenharmony_ci{ 142262306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 142362306a36Sopenharmony_ci unsigned int aif1 = 0; 142462306a36Sopenharmony_ci unsigned int aif3 = 0; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 142762306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 142862306a36Sopenharmony_ci break; 142962306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFM: 143062306a36Sopenharmony_ci aif3 |= WM8904_LRCLK_DIR; 143162306a36Sopenharmony_ci break; 143262306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFS: 143362306a36Sopenharmony_ci aif1 |= WM8904_BCLK_DIR; 143462306a36Sopenharmony_ci break; 143562306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 143662306a36Sopenharmony_ci aif1 |= WM8904_BCLK_DIR; 143762306a36Sopenharmony_ci aif3 |= WM8904_LRCLK_DIR; 143862306a36Sopenharmony_ci break; 143962306a36Sopenharmony_ci default: 144062306a36Sopenharmony_ci return -EINVAL; 144162306a36Sopenharmony_ci } 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 144462306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 144562306a36Sopenharmony_ci aif1 |= 0x3 | WM8904_AIF_LRCLK_INV; 144662306a36Sopenharmony_ci fallthrough; 144762306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 144862306a36Sopenharmony_ci aif1 |= 0x3; 144962306a36Sopenharmony_ci break; 145062306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 145162306a36Sopenharmony_ci aif1 |= 0x2; 145262306a36Sopenharmony_ci break; 145362306a36Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 145462306a36Sopenharmony_ci break; 145562306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 145662306a36Sopenharmony_ci aif1 |= 0x1; 145762306a36Sopenharmony_ci break; 145862306a36Sopenharmony_ci default: 145962306a36Sopenharmony_ci return -EINVAL; 146062306a36Sopenharmony_ci } 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 146362306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 146462306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 146562306a36Sopenharmony_ci /* frame inversion not valid for DSP modes */ 146662306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 146762306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 146862306a36Sopenharmony_ci break; 146962306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 147062306a36Sopenharmony_ci aif1 |= WM8904_AIF_BCLK_INV; 147162306a36Sopenharmony_ci break; 147262306a36Sopenharmony_ci default: 147362306a36Sopenharmony_ci return -EINVAL; 147462306a36Sopenharmony_ci } 147562306a36Sopenharmony_ci break; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 147862306a36Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 147962306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 148062306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 148162306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 148262306a36Sopenharmony_ci break; 148362306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 148462306a36Sopenharmony_ci aif1 |= WM8904_AIF_BCLK_INV | WM8904_AIF_LRCLK_INV; 148562306a36Sopenharmony_ci break; 148662306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 148762306a36Sopenharmony_ci aif1 |= WM8904_AIF_BCLK_INV; 148862306a36Sopenharmony_ci break; 148962306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 149062306a36Sopenharmony_ci aif1 |= WM8904_AIF_LRCLK_INV; 149162306a36Sopenharmony_ci break; 149262306a36Sopenharmony_ci default: 149362306a36Sopenharmony_ci return -EINVAL; 149462306a36Sopenharmony_ci } 149562306a36Sopenharmony_ci break; 149662306a36Sopenharmony_ci default: 149762306a36Sopenharmony_ci return -EINVAL; 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_1, 150162306a36Sopenharmony_ci WM8904_AIF_BCLK_INV | WM8904_AIF_LRCLK_INV | 150262306a36Sopenharmony_ci WM8904_AIF_FMT_MASK | WM8904_BCLK_DIR, aif1); 150362306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_3, 150462306a36Sopenharmony_ci WM8904_LRCLK_DIR, aif3); 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci return 0; 150762306a36Sopenharmony_ci} 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_cistatic int wm8904_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, 151162306a36Sopenharmony_ci unsigned int rx_mask, int slots, int slot_width) 151262306a36Sopenharmony_ci{ 151362306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 151462306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 151562306a36Sopenharmony_ci int aif1 = 0; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci /* Don't need to validate anything if we're turning off TDM */ 151862306a36Sopenharmony_ci if (slots == 0) 151962306a36Sopenharmony_ci goto out; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci /* Note that we allow configurations we can't handle ourselves - 152262306a36Sopenharmony_ci * for example, we can generate clocks for slots 2 and up even if 152362306a36Sopenharmony_ci * we can't use those slots ourselves. 152462306a36Sopenharmony_ci */ 152562306a36Sopenharmony_ci aif1 |= WM8904_AIFADC_TDM | WM8904_AIFDAC_TDM; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci switch (rx_mask) { 152862306a36Sopenharmony_ci case 3: 152962306a36Sopenharmony_ci break; 153062306a36Sopenharmony_ci case 0xc: 153162306a36Sopenharmony_ci aif1 |= WM8904_AIFADC_TDM_CHAN; 153262306a36Sopenharmony_ci break; 153362306a36Sopenharmony_ci default: 153462306a36Sopenharmony_ci return -EINVAL; 153562306a36Sopenharmony_ci } 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci switch (tx_mask) { 153962306a36Sopenharmony_ci case 3: 154062306a36Sopenharmony_ci break; 154162306a36Sopenharmony_ci case 0xc: 154262306a36Sopenharmony_ci aif1 |= WM8904_AIFDAC_TDM_CHAN; 154362306a36Sopenharmony_ci break; 154462306a36Sopenharmony_ci default: 154562306a36Sopenharmony_ci return -EINVAL; 154662306a36Sopenharmony_ci } 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ciout: 154962306a36Sopenharmony_ci wm8904->tdm_width = slot_width; 155062306a36Sopenharmony_ci wm8904->tdm_slots = slots / 2; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_1, 155362306a36Sopenharmony_ci WM8904_AIFADC_TDM | WM8904_AIFADC_TDM_CHAN | 155462306a36Sopenharmony_ci WM8904_AIFDAC_TDM | WM8904_AIFDAC_TDM_CHAN, aif1); 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci return 0; 155762306a36Sopenharmony_ci} 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_cistruct _fll_div { 156062306a36Sopenharmony_ci u16 fll_fratio; 156162306a36Sopenharmony_ci u16 fll_outdiv; 156262306a36Sopenharmony_ci u16 fll_clk_ref_div; 156362306a36Sopenharmony_ci u16 n; 156462306a36Sopenharmony_ci u16 k; 156562306a36Sopenharmony_ci}; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci/* The size in bits of the FLL divide multiplied by 10 156862306a36Sopenharmony_ci * to allow rounding later */ 156962306a36Sopenharmony_ci#define FIXED_FLL_SIZE ((1 << 16) * 10) 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_cistatic struct { 157262306a36Sopenharmony_ci unsigned int min; 157362306a36Sopenharmony_ci unsigned int max; 157462306a36Sopenharmony_ci u16 fll_fratio; 157562306a36Sopenharmony_ci int ratio; 157662306a36Sopenharmony_ci} fll_fratios[] = { 157762306a36Sopenharmony_ci { 0, 64000, 4, 16 }, 157862306a36Sopenharmony_ci { 64000, 128000, 3, 8 }, 157962306a36Sopenharmony_ci { 128000, 256000, 2, 4 }, 158062306a36Sopenharmony_ci { 256000, 1000000, 1, 2 }, 158162306a36Sopenharmony_ci { 1000000, 13500000, 0, 1 }, 158262306a36Sopenharmony_ci}; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_cistatic int fll_factors(struct _fll_div *fll_div, unsigned int Fref, 158562306a36Sopenharmony_ci unsigned int Fout) 158662306a36Sopenharmony_ci{ 158762306a36Sopenharmony_ci u64 Kpart; 158862306a36Sopenharmony_ci unsigned int K, Ndiv, Nmod, target; 158962306a36Sopenharmony_ci unsigned int div; 159062306a36Sopenharmony_ci int i; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci /* Fref must be <=13.5MHz */ 159362306a36Sopenharmony_ci div = 1; 159462306a36Sopenharmony_ci fll_div->fll_clk_ref_div = 0; 159562306a36Sopenharmony_ci while ((Fref / div) > 13500000) { 159662306a36Sopenharmony_ci div *= 2; 159762306a36Sopenharmony_ci fll_div->fll_clk_ref_div++; 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci if (div > 8) { 160062306a36Sopenharmony_ci pr_err("Can't scale %dMHz input down to <=13.5MHz\n", 160162306a36Sopenharmony_ci Fref); 160262306a36Sopenharmony_ci return -EINVAL; 160362306a36Sopenharmony_ci } 160462306a36Sopenharmony_ci } 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci pr_debug("Fref=%u Fout=%u\n", Fref, Fout); 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci /* Apply the division for our remaining calculations */ 160962306a36Sopenharmony_ci Fref /= div; 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci /* Fvco should be 90-100MHz; don't check the upper bound */ 161262306a36Sopenharmony_ci div = 4; 161362306a36Sopenharmony_ci while (Fout * div < 90000000) { 161462306a36Sopenharmony_ci div++; 161562306a36Sopenharmony_ci if (div > 64) { 161662306a36Sopenharmony_ci pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n", 161762306a36Sopenharmony_ci Fout); 161862306a36Sopenharmony_ci return -EINVAL; 161962306a36Sopenharmony_ci } 162062306a36Sopenharmony_ci } 162162306a36Sopenharmony_ci target = Fout * div; 162262306a36Sopenharmony_ci fll_div->fll_outdiv = div - 1; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci pr_debug("Fvco=%dHz\n", target); 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci /* Find an appropriate FLL_FRATIO and factor it out of the target */ 162762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { 162862306a36Sopenharmony_ci if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { 162962306a36Sopenharmony_ci fll_div->fll_fratio = fll_fratios[i].fll_fratio; 163062306a36Sopenharmony_ci target /= fll_fratios[i].ratio; 163162306a36Sopenharmony_ci break; 163262306a36Sopenharmony_ci } 163362306a36Sopenharmony_ci } 163462306a36Sopenharmony_ci if (i == ARRAY_SIZE(fll_fratios)) { 163562306a36Sopenharmony_ci pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref); 163662306a36Sopenharmony_ci return -EINVAL; 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci /* Now, calculate N.K */ 164062306a36Sopenharmony_ci Ndiv = target / Fref; 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci fll_div->n = Ndiv; 164362306a36Sopenharmony_ci Nmod = target % Fref; 164462306a36Sopenharmony_ci pr_debug("Nmod=%d\n", Nmod); 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci /* Calculate fractional part - scale up so we can round. */ 164762306a36Sopenharmony_ci Kpart = FIXED_FLL_SIZE * (long long)Nmod; 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci do_div(Kpart, Fref); 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci K = Kpart & 0xFFFFFFFF; 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci if ((K % 10) >= 5) 165462306a36Sopenharmony_ci K += 5; 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci /* Move down to proper range now rounding is done */ 165762306a36Sopenharmony_ci fll_div->k = K / 10; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci pr_debug("N=%x K=%x FLL_FRATIO=%x FLL_OUTDIV=%x FLL_CLK_REF_DIV=%x\n", 166062306a36Sopenharmony_ci fll_div->n, fll_div->k, 166162306a36Sopenharmony_ci fll_div->fll_fratio, fll_div->fll_outdiv, 166262306a36Sopenharmony_ci fll_div->fll_clk_ref_div); 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci return 0; 166562306a36Sopenharmony_ci} 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_cistatic int wm8904_set_fll(struct snd_soc_dai *dai, int fll_id, int source, 166862306a36Sopenharmony_ci unsigned int Fref, unsigned int Fout) 166962306a36Sopenharmony_ci{ 167062306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 167162306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 167262306a36Sopenharmony_ci struct _fll_div fll_div; 167362306a36Sopenharmony_ci int ret, val; 167462306a36Sopenharmony_ci int clock2, fll1; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci /* Any change? */ 167762306a36Sopenharmony_ci if (source == wm8904->fll_src && Fref == wm8904->fll_fref && 167862306a36Sopenharmony_ci Fout == wm8904->fll_fout) 167962306a36Sopenharmony_ci return 0; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci clock2 = snd_soc_component_read(component, WM8904_CLOCK_RATES_2); 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci if (Fout == 0) { 168462306a36Sopenharmony_ci dev_dbg(component->dev, "FLL disabled\n"); 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci wm8904->fll_fref = 0; 168762306a36Sopenharmony_ci wm8904->fll_fout = 0; 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci /* Gate SYSCLK to avoid glitches */ 169062306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2, 169162306a36Sopenharmony_ci WM8904_CLK_SYS_ENA, 0); 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, 169462306a36Sopenharmony_ci WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0); 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci goto out; 169762306a36Sopenharmony_ci } 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci /* Validate the FLL ID */ 170062306a36Sopenharmony_ci switch (source) { 170162306a36Sopenharmony_ci case WM8904_FLL_MCLK: 170262306a36Sopenharmony_ci case WM8904_FLL_LRCLK: 170362306a36Sopenharmony_ci case WM8904_FLL_BCLK: 170462306a36Sopenharmony_ci ret = fll_factors(&fll_div, Fref, Fout); 170562306a36Sopenharmony_ci if (ret != 0) 170662306a36Sopenharmony_ci return ret; 170762306a36Sopenharmony_ci break; 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci case WM8904_FLL_FREE_RUNNING: 171062306a36Sopenharmony_ci dev_dbg(component->dev, "Using free running FLL\n"); 171162306a36Sopenharmony_ci /* Force 12MHz and output/4 for now */ 171262306a36Sopenharmony_ci Fout = 12000000; 171362306a36Sopenharmony_ci Fref = 12000000; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci memset(&fll_div, 0, sizeof(fll_div)); 171662306a36Sopenharmony_ci fll_div.fll_outdiv = 3; 171762306a36Sopenharmony_ci break; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci default: 172062306a36Sopenharmony_ci dev_err(component->dev, "Unknown FLL ID %d\n", fll_id); 172162306a36Sopenharmony_ci return -EINVAL; 172262306a36Sopenharmony_ci } 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci /* Save current state then disable the FLL and SYSCLK to avoid 172562306a36Sopenharmony_ci * misclocking */ 172662306a36Sopenharmony_ci fll1 = snd_soc_component_read(component, WM8904_FLL_CONTROL_1); 172762306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2, 172862306a36Sopenharmony_ci WM8904_CLK_SYS_ENA, 0); 172962306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, 173062306a36Sopenharmony_ci WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0); 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci /* Unlock forced oscilator control to switch it on/off */ 173362306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_CONTROL_INTERFACE_TEST_1, 173462306a36Sopenharmony_ci WM8904_USER_KEY, WM8904_USER_KEY); 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci if (fll_id == WM8904_FLL_FREE_RUNNING) { 173762306a36Sopenharmony_ci val = WM8904_FLL_FRC_NCO; 173862306a36Sopenharmony_ci } else { 173962306a36Sopenharmony_ci val = 0; 174062306a36Sopenharmony_ci } 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_FLL_NCO_TEST_1, WM8904_FLL_FRC_NCO, 174362306a36Sopenharmony_ci val); 174462306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_CONTROL_INTERFACE_TEST_1, 174562306a36Sopenharmony_ci WM8904_USER_KEY, 0); 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci switch (fll_id) { 174862306a36Sopenharmony_ci case WM8904_FLL_MCLK: 174962306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5, 175062306a36Sopenharmony_ci WM8904_FLL_CLK_REF_SRC_MASK, 0); 175162306a36Sopenharmony_ci break; 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci case WM8904_FLL_LRCLK: 175462306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5, 175562306a36Sopenharmony_ci WM8904_FLL_CLK_REF_SRC_MASK, 1); 175662306a36Sopenharmony_ci break; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci case WM8904_FLL_BCLK: 175962306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5, 176062306a36Sopenharmony_ci WM8904_FLL_CLK_REF_SRC_MASK, 2); 176162306a36Sopenharmony_ci break; 176262306a36Sopenharmony_ci } 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci if (fll_div.k) 176562306a36Sopenharmony_ci val = WM8904_FLL_FRACN_ENA; 176662306a36Sopenharmony_ci else 176762306a36Sopenharmony_ci val = 0; 176862306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, 176962306a36Sopenharmony_ci WM8904_FLL_FRACN_ENA, val); 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_2, 177262306a36Sopenharmony_ci WM8904_FLL_OUTDIV_MASK | WM8904_FLL_FRATIO_MASK, 177362306a36Sopenharmony_ci (fll_div.fll_outdiv << WM8904_FLL_OUTDIV_SHIFT) | 177462306a36Sopenharmony_ci (fll_div.fll_fratio << WM8904_FLL_FRATIO_SHIFT)); 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci snd_soc_component_write(component, WM8904_FLL_CONTROL_3, fll_div.k); 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_4, WM8904_FLL_N_MASK, 177962306a36Sopenharmony_ci fll_div.n << WM8904_FLL_N_SHIFT); 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5, 178262306a36Sopenharmony_ci WM8904_FLL_CLK_REF_DIV_MASK, 178362306a36Sopenharmony_ci fll_div.fll_clk_ref_div 178462306a36Sopenharmony_ci << WM8904_FLL_CLK_REF_DIV_SHIFT); 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci dev_dbg(component->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout); 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci wm8904->fll_fref = Fref; 178962306a36Sopenharmony_ci wm8904->fll_fout = Fout; 179062306a36Sopenharmony_ci wm8904->fll_src = source; 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci /* Enable the FLL if it was previously active */ 179362306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, 179462306a36Sopenharmony_ci WM8904_FLL_OSC_ENA, fll1); 179562306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, 179662306a36Sopenharmony_ci WM8904_FLL_ENA, fll1); 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ciout: 179962306a36Sopenharmony_ci /* Reenable SYSCLK if it was previously active */ 180062306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2, 180162306a36Sopenharmony_ci WM8904_CLK_SYS_ENA, clock2); 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci return 0; 180462306a36Sopenharmony_ci} 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_cistatic int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id, 180762306a36Sopenharmony_ci unsigned int freq, int dir) 180862306a36Sopenharmony_ci{ 180962306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 181062306a36Sopenharmony_ci struct wm8904_priv *priv = snd_soc_component_get_drvdata(component); 181162306a36Sopenharmony_ci unsigned long mclk_freq; 181262306a36Sopenharmony_ci int ret; 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci switch (clk_id) { 181562306a36Sopenharmony_ci case WM8904_CLK_AUTO: 181662306a36Sopenharmony_ci /* We don't have any rate constraints, so just ignore the 181762306a36Sopenharmony_ci * request to disable constraining. 181862306a36Sopenharmony_ci */ 181962306a36Sopenharmony_ci if (!freq) 182062306a36Sopenharmony_ci return 0; 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci mclk_freq = clk_get_rate(priv->mclk); 182362306a36Sopenharmony_ci /* enable FLL if a different sysclk is desired */ 182462306a36Sopenharmony_ci if (mclk_freq != freq) { 182562306a36Sopenharmony_ci priv->sysclk_src = WM8904_CLK_FLL; 182662306a36Sopenharmony_ci ret = wm8904_set_fll(dai, WM8904_FLL_MCLK, 182762306a36Sopenharmony_ci WM8904_FLL_MCLK, 182862306a36Sopenharmony_ci mclk_freq, freq); 182962306a36Sopenharmony_ci if (ret) 183062306a36Sopenharmony_ci return ret; 183162306a36Sopenharmony_ci break; 183262306a36Sopenharmony_ci } 183362306a36Sopenharmony_ci clk_id = WM8904_CLK_MCLK; 183462306a36Sopenharmony_ci fallthrough; 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci case WM8904_CLK_MCLK: 183762306a36Sopenharmony_ci priv->sysclk_src = clk_id; 183862306a36Sopenharmony_ci priv->mclk_rate = freq; 183962306a36Sopenharmony_ci break; 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci case WM8904_CLK_FLL: 184262306a36Sopenharmony_ci priv->sysclk_src = clk_id; 184362306a36Sopenharmony_ci break; 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci default: 184662306a36Sopenharmony_ci return -EINVAL; 184762306a36Sopenharmony_ci } 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq); 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci wm8904_configure_clocking(component); 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci return 0; 185462306a36Sopenharmony_ci} 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_cistatic int wm8904_mute(struct snd_soc_dai *codec_dai, int mute, int direction) 185762306a36Sopenharmony_ci{ 185862306a36Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 185962306a36Sopenharmony_ci int val; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci if (mute) 186262306a36Sopenharmony_ci val = WM8904_DAC_MUTE; 186362306a36Sopenharmony_ci else 186462306a36Sopenharmony_ci val = 0; 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_DAC_DIGITAL_1, WM8904_DAC_MUTE, val); 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci return 0; 186962306a36Sopenharmony_ci} 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_cistatic int wm8904_set_bias_level(struct snd_soc_component *component, 187262306a36Sopenharmony_ci enum snd_soc_bias_level level) 187362306a36Sopenharmony_ci{ 187462306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 187562306a36Sopenharmony_ci int ret; 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci switch (level) { 187862306a36Sopenharmony_ci case SND_SOC_BIAS_ON: 187962306a36Sopenharmony_ci break; 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 188262306a36Sopenharmony_ci /* VMID resistance 2*50k */ 188362306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_VMID_CONTROL_0, 188462306a36Sopenharmony_ci WM8904_VMID_RES_MASK, 188562306a36Sopenharmony_ci 0x1 << WM8904_VMID_RES_SHIFT); 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci /* Normal bias current */ 188862306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_BIAS_CONTROL_0, 188962306a36Sopenharmony_ci WM8904_ISEL_MASK, 2 << WM8904_ISEL_SHIFT); 189062306a36Sopenharmony_ci break; 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 189362306a36Sopenharmony_ci if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { 189462306a36Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies), 189562306a36Sopenharmony_ci wm8904->supplies); 189662306a36Sopenharmony_ci if (ret != 0) { 189762306a36Sopenharmony_ci dev_err(component->dev, 189862306a36Sopenharmony_ci "Failed to enable supplies: %d\n", 189962306a36Sopenharmony_ci ret); 190062306a36Sopenharmony_ci return ret; 190162306a36Sopenharmony_ci } 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci ret = clk_prepare_enable(wm8904->mclk); 190462306a36Sopenharmony_ci if (ret) { 190562306a36Sopenharmony_ci dev_err(component->dev, 190662306a36Sopenharmony_ci "Failed to enable MCLK: %d\n", ret); 190762306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), 190862306a36Sopenharmony_ci wm8904->supplies); 190962306a36Sopenharmony_ci return ret; 191062306a36Sopenharmony_ci } 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci regcache_cache_only(wm8904->regmap, false); 191362306a36Sopenharmony_ci regcache_sync(wm8904->regmap); 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci /* Enable bias */ 191662306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_BIAS_CONTROL_0, 191762306a36Sopenharmony_ci WM8904_BIAS_ENA, WM8904_BIAS_ENA); 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci /* Enable VMID, VMID buffering, 2*5k resistance */ 192062306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_VMID_CONTROL_0, 192162306a36Sopenharmony_ci WM8904_VMID_ENA | 192262306a36Sopenharmony_ci WM8904_VMID_RES_MASK, 192362306a36Sopenharmony_ci WM8904_VMID_ENA | 192462306a36Sopenharmony_ci 0x3 << WM8904_VMID_RES_SHIFT); 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci /* Let VMID ramp */ 192762306a36Sopenharmony_ci msleep(1); 192862306a36Sopenharmony_ci } 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci /* Maintain VMID with 2*250k */ 193162306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_VMID_CONTROL_0, 193262306a36Sopenharmony_ci WM8904_VMID_RES_MASK, 193362306a36Sopenharmony_ci 0x2 << WM8904_VMID_RES_SHIFT); 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci /* Bias current *0.5 */ 193662306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_BIAS_CONTROL_0, 193762306a36Sopenharmony_ci WM8904_ISEL_MASK, 0); 193862306a36Sopenharmony_ci break; 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci case SND_SOC_BIAS_OFF: 194162306a36Sopenharmony_ci /* Turn off VMID */ 194262306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_VMID_CONTROL_0, 194362306a36Sopenharmony_ci WM8904_VMID_RES_MASK | WM8904_VMID_ENA, 0); 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci /* Stop bias generation */ 194662306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8904_BIAS_CONTROL_0, 194762306a36Sopenharmony_ci WM8904_BIAS_ENA, 0); 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci snd_soc_component_write(component, WM8904_SW_RESET_AND_ID, 0); 195062306a36Sopenharmony_ci regcache_cache_only(wm8904->regmap, true); 195162306a36Sopenharmony_ci regcache_mark_dirty(wm8904->regmap); 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), 195462306a36Sopenharmony_ci wm8904->supplies); 195562306a36Sopenharmony_ci clk_disable_unprepare(wm8904->mclk); 195662306a36Sopenharmony_ci break; 195762306a36Sopenharmony_ci } 195862306a36Sopenharmony_ci return 0; 195962306a36Sopenharmony_ci} 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci#define WM8904_RATES SNDRV_PCM_RATE_8000_96000 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci#define WM8904_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 196462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_cistatic const struct snd_soc_dai_ops wm8904_dai_ops = { 196762306a36Sopenharmony_ci .set_sysclk = wm8904_set_sysclk, 196862306a36Sopenharmony_ci .set_fmt = wm8904_set_fmt, 196962306a36Sopenharmony_ci .set_tdm_slot = wm8904_set_tdm_slot, 197062306a36Sopenharmony_ci .set_pll = wm8904_set_fll, 197162306a36Sopenharmony_ci .hw_params = wm8904_hw_params, 197262306a36Sopenharmony_ci .mute_stream = wm8904_mute, 197362306a36Sopenharmony_ci .no_capture_mute = 1, 197462306a36Sopenharmony_ci}; 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_cistatic struct snd_soc_dai_driver wm8904_dai = { 197762306a36Sopenharmony_ci .name = "wm8904-hifi", 197862306a36Sopenharmony_ci .playback = { 197962306a36Sopenharmony_ci .stream_name = "Playback", 198062306a36Sopenharmony_ci .channels_min = 2, 198162306a36Sopenharmony_ci .channels_max = 2, 198262306a36Sopenharmony_ci .rates = WM8904_RATES, 198362306a36Sopenharmony_ci .formats = WM8904_FORMATS, 198462306a36Sopenharmony_ci }, 198562306a36Sopenharmony_ci .capture = { 198662306a36Sopenharmony_ci .stream_name = "Capture", 198762306a36Sopenharmony_ci .channels_min = 2, 198862306a36Sopenharmony_ci .channels_max = 2, 198962306a36Sopenharmony_ci .rates = WM8904_RATES, 199062306a36Sopenharmony_ci .formats = WM8904_FORMATS, 199162306a36Sopenharmony_ci }, 199262306a36Sopenharmony_ci .ops = &wm8904_dai_ops, 199362306a36Sopenharmony_ci .symmetric_rate = 1, 199462306a36Sopenharmony_ci}; 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_cistatic void wm8904_handle_retune_mobile_pdata(struct snd_soc_component *component) 199762306a36Sopenharmony_ci{ 199862306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 199962306a36Sopenharmony_ci struct wm8904_pdata *pdata = wm8904->pdata; 200062306a36Sopenharmony_ci struct snd_kcontrol_new control = 200162306a36Sopenharmony_ci SOC_ENUM_EXT("EQ Mode", 200262306a36Sopenharmony_ci wm8904->retune_mobile_enum, 200362306a36Sopenharmony_ci wm8904_get_retune_mobile_enum, 200462306a36Sopenharmony_ci wm8904_put_retune_mobile_enum); 200562306a36Sopenharmony_ci int ret, i, j; 200662306a36Sopenharmony_ci const char **t; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci /* We need an array of texts for the enum API but the number 200962306a36Sopenharmony_ci * of texts is likely to be less than the number of 201062306a36Sopenharmony_ci * configurations due to the sample rate dependency of the 201162306a36Sopenharmony_ci * configurations. */ 201262306a36Sopenharmony_ci wm8904->num_retune_mobile_texts = 0; 201362306a36Sopenharmony_ci wm8904->retune_mobile_texts = NULL; 201462306a36Sopenharmony_ci for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) { 201562306a36Sopenharmony_ci for (j = 0; j < wm8904->num_retune_mobile_texts; j++) { 201662306a36Sopenharmony_ci if (strcmp(pdata->retune_mobile_cfgs[i].name, 201762306a36Sopenharmony_ci wm8904->retune_mobile_texts[j]) == 0) 201862306a36Sopenharmony_ci break; 201962306a36Sopenharmony_ci } 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci if (j != wm8904->num_retune_mobile_texts) 202262306a36Sopenharmony_ci continue; 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci /* Expand the array... */ 202562306a36Sopenharmony_ci t = krealloc(wm8904->retune_mobile_texts, 202662306a36Sopenharmony_ci sizeof(char *) * 202762306a36Sopenharmony_ci (wm8904->num_retune_mobile_texts + 1), 202862306a36Sopenharmony_ci GFP_KERNEL); 202962306a36Sopenharmony_ci if (t == NULL) 203062306a36Sopenharmony_ci continue; 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci /* ...store the new entry... */ 203362306a36Sopenharmony_ci t[wm8904->num_retune_mobile_texts] = 203462306a36Sopenharmony_ci pdata->retune_mobile_cfgs[i].name; 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci /* ...and remember the new version. */ 203762306a36Sopenharmony_ci wm8904->num_retune_mobile_texts++; 203862306a36Sopenharmony_ci wm8904->retune_mobile_texts = t; 203962306a36Sopenharmony_ci } 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci dev_dbg(component->dev, "Allocated %d unique ReTune Mobile names\n", 204262306a36Sopenharmony_ci wm8904->num_retune_mobile_texts); 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci wm8904->retune_mobile_enum.items = wm8904->num_retune_mobile_texts; 204562306a36Sopenharmony_ci wm8904->retune_mobile_enum.texts = wm8904->retune_mobile_texts; 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci ret = snd_soc_add_component_controls(component, &control, 1); 204862306a36Sopenharmony_ci if (ret != 0) 204962306a36Sopenharmony_ci dev_err(component->dev, 205062306a36Sopenharmony_ci "Failed to add ReTune Mobile control: %d\n", ret); 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_cistatic void wm8904_handle_pdata(struct snd_soc_component *component) 205462306a36Sopenharmony_ci{ 205562306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 205662306a36Sopenharmony_ci struct wm8904_pdata *pdata = wm8904->pdata; 205762306a36Sopenharmony_ci int ret, i; 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci if (!pdata) { 206062306a36Sopenharmony_ci snd_soc_add_component_controls(component, wm8904_eq_controls, 206162306a36Sopenharmony_ci ARRAY_SIZE(wm8904_eq_controls)); 206262306a36Sopenharmony_ci return; 206362306a36Sopenharmony_ci } 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci dev_dbg(component->dev, "%d DRC configurations\n", pdata->num_drc_cfgs); 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci if (pdata->num_drc_cfgs) { 206862306a36Sopenharmony_ci struct snd_kcontrol_new control = 206962306a36Sopenharmony_ci SOC_ENUM_EXT("DRC Mode", wm8904->drc_enum, 207062306a36Sopenharmony_ci wm8904_get_drc_enum, wm8904_put_drc_enum); 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci /* We need an array of texts for the enum API */ 207362306a36Sopenharmony_ci wm8904->drc_texts = kmalloc_array(pdata->num_drc_cfgs, 207462306a36Sopenharmony_ci sizeof(char *), 207562306a36Sopenharmony_ci GFP_KERNEL); 207662306a36Sopenharmony_ci if (!wm8904->drc_texts) 207762306a36Sopenharmony_ci return; 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci for (i = 0; i < pdata->num_drc_cfgs; i++) 208062306a36Sopenharmony_ci wm8904->drc_texts[i] = pdata->drc_cfgs[i].name; 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci wm8904->drc_enum.items = pdata->num_drc_cfgs; 208362306a36Sopenharmony_ci wm8904->drc_enum.texts = wm8904->drc_texts; 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci ret = snd_soc_add_component_controls(component, &control, 1); 208662306a36Sopenharmony_ci if (ret != 0) 208762306a36Sopenharmony_ci dev_err(component->dev, 208862306a36Sopenharmony_ci "Failed to add DRC mode control: %d\n", ret); 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci wm8904_set_drc(component); 209162306a36Sopenharmony_ci } 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci dev_dbg(component->dev, "%d ReTune Mobile configurations\n", 209462306a36Sopenharmony_ci pdata->num_retune_mobile_cfgs); 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci if (pdata->num_retune_mobile_cfgs) 209762306a36Sopenharmony_ci wm8904_handle_retune_mobile_pdata(component); 209862306a36Sopenharmony_ci else 209962306a36Sopenharmony_ci snd_soc_add_component_controls(component, wm8904_eq_controls, 210062306a36Sopenharmony_ci ARRAY_SIZE(wm8904_eq_controls)); 210162306a36Sopenharmony_ci} 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_cistatic int wm8904_probe(struct snd_soc_component *component) 210562306a36Sopenharmony_ci{ 210662306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci switch (wm8904->devtype) { 210962306a36Sopenharmony_ci case WM8904: 211062306a36Sopenharmony_ci break; 211162306a36Sopenharmony_ci case WM8912: 211262306a36Sopenharmony_ci memset(&wm8904_dai.capture, 0, sizeof(wm8904_dai.capture)); 211362306a36Sopenharmony_ci break; 211462306a36Sopenharmony_ci default: 211562306a36Sopenharmony_ci dev_err(component->dev, "Unknown device type %d\n", 211662306a36Sopenharmony_ci wm8904->devtype); 211762306a36Sopenharmony_ci return -EINVAL; 211862306a36Sopenharmony_ci } 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci wm8904_handle_pdata(component); 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci wm8904_add_widgets(component); 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci return 0; 212562306a36Sopenharmony_ci} 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_cistatic void wm8904_remove(struct snd_soc_component *component) 212862306a36Sopenharmony_ci{ 212962306a36Sopenharmony_ci struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci kfree(wm8904->retune_mobile_texts); 213262306a36Sopenharmony_ci kfree(wm8904->drc_texts); 213362306a36Sopenharmony_ci} 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_wm8904 = { 213662306a36Sopenharmony_ci .probe = wm8904_probe, 213762306a36Sopenharmony_ci .remove = wm8904_remove, 213862306a36Sopenharmony_ci .set_bias_level = wm8904_set_bias_level, 213962306a36Sopenharmony_ci .use_pmdown_time = 1, 214062306a36Sopenharmony_ci .endianness = 1, 214162306a36Sopenharmony_ci}; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_cistatic const struct regmap_config wm8904_regmap = { 214462306a36Sopenharmony_ci .reg_bits = 8, 214562306a36Sopenharmony_ci .val_bits = 16, 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci .max_register = WM8904_MAX_REGISTER, 214862306a36Sopenharmony_ci .volatile_reg = wm8904_volatile_register, 214962306a36Sopenharmony_ci .readable_reg = wm8904_readable_register, 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci .cache_type = REGCACHE_MAPLE, 215262306a36Sopenharmony_ci .reg_defaults = wm8904_reg_defaults, 215362306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(wm8904_reg_defaults), 215462306a36Sopenharmony_ci}; 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci#ifdef CONFIG_OF 215762306a36Sopenharmony_cistatic const struct of_device_id wm8904_of_match[] = { 215862306a36Sopenharmony_ci { 215962306a36Sopenharmony_ci .compatible = "wlf,wm8904", 216062306a36Sopenharmony_ci .data = (void *)WM8904, 216162306a36Sopenharmony_ci }, { 216262306a36Sopenharmony_ci .compatible = "wlf,wm8912", 216362306a36Sopenharmony_ci .data = (void *)WM8912, 216462306a36Sopenharmony_ci }, { 216562306a36Sopenharmony_ci /* sentinel */ 216662306a36Sopenharmony_ci } 216762306a36Sopenharmony_ci}; 216862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, wm8904_of_match); 216962306a36Sopenharmony_ci#endif 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_cistatic const struct i2c_device_id wm8904_i2c_id[]; 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_cistatic int wm8904_i2c_probe(struct i2c_client *i2c) 217462306a36Sopenharmony_ci{ 217562306a36Sopenharmony_ci struct wm8904_priv *wm8904; 217662306a36Sopenharmony_ci unsigned int val; 217762306a36Sopenharmony_ci int ret, i; 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci wm8904 = devm_kzalloc(&i2c->dev, sizeof(struct wm8904_priv), 218062306a36Sopenharmony_ci GFP_KERNEL); 218162306a36Sopenharmony_ci if (wm8904 == NULL) 218262306a36Sopenharmony_ci return -ENOMEM; 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci wm8904->mclk = devm_clk_get(&i2c->dev, "mclk"); 218562306a36Sopenharmony_ci if (IS_ERR(wm8904->mclk)) { 218662306a36Sopenharmony_ci ret = PTR_ERR(wm8904->mclk); 218762306a36Sopenharmony_ci dev_err(&i2c->dev, "Failed to get MCLK\n"); 218862306a36Sopenharmony_ci return ret; 218962306a36Sopenharmony_ci } 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci wm8904->regmap = devm_regmap_init_i2c(i2c, &wm8904_regmap); 219262306a36Sopenharmony_ci if (IS_ERR(wm8904->regmap)) { 219362306a36Sopenharmony_ci ret = PTR_ERR(wm8904->regmap); 219462306a36Sopenharmony_ci dev_err(&i2c->dev, "Failed to allocate register map: %d\n", 219562306a36Sopenharmony_ci ret); 219662306a36Sopenharmony_ci return ret; 219762306a36Sopenharmony_ci } 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci if (i2c->dev.of_node) { 220062306a36Sopenharmony_ci const struct of_device_id *match; 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci match = of_match_node(wm8904_of_match, i2c->dev.of_node); 220362306a36Sopenharmony_ci if (match == NULL) 220462306a36Sopenharmony_ci return -EINVAL; 220562306a36Sopenharmony_ci wm8904->devtype = (uintptr_t)match->data; 220662306a36Sopenharmony_ci } else { 220762306a36Sopenharmony_ci const struct i2c_device_id *id = 220862306a36Sopenharmony_ci i2c_match_id(wm8904_i2c_id, i2c); 220962306a36Sopenharmony_ci wm8904->devtype = id->driver_data; 221062306a36Sopenharmony_ci } 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci i2c_set_clientdata(i2c, wm8904); 221362306a36Sopenharmony_ci wm8904->pdata = i2c->dev.platform_data; 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wm8904->supplies); i++) 221662306a36Sopenharmony_ci wm8904->supplies[i].supply = wm8904_supply_names[i]; 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8904->supplies), 221962306a36Sopenharmony_ci wm8904->supplies); 222062306a36Sopenharmony_ci if (ret != 0) { 222162306a36Sopenharmony_ci dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); 222262306a36Sopenharmony_ci return ret; 222362306a36Sopenharmony_ci } 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies), 222662306a36Sopenharmony_ci wm8904->supplies); 222762306a36Sopenharmony_ci if (ret != 0) { 222862306a36Sopenharmony_ci dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); 222962306a36Sopenharmony_ci return ret; 223062306a36Sopenharmony_ci } 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci ret = regmap_read(wm8904->regmap, WM8904_SW_RESET_AND_ID, &val); 223362306a36Sopenharmony_ci if (ret < 0) { 223462306a36Sopenharmony_ci dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret); 223562306a36Sopenharmony_ci goto err_enable; 223662306a36Sopenharmony_ci } 223762306a36Sopenharmony_ci if (val != 0x8904) { 223862306a36Sopenharmony_ci dev_err(&i2c->dev, "Device is not a WM8904, ID is %x\n", val); 223962306a36Sopenharmony_ci ret = -EINVAL; 224062306a36Sopenharmony_ci goto err_enable; 224162306a36Sopenharmony_ci } 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci ret = regmap_read(wm8904->regmap, WM8904_REVISION, &val); 224462306a36Sopenharmony_ci if (ret < 0) { 224562306a36Sopenharmony_ci dev_err(&i2c->dev, "Failed to read device revision: %d\n", 224662306a36Sopenharmony_ci ret); 224762306a36Sopenharmony_ci goto err_enable; 224862306a36Sopenharmony_ci } 224962306a36Sopenharmony_ci dev_info(&i2c->dev, "revision %c\n", val + 'A'); 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci ret = regmap_write(wm8904->regmap, WM8904_SW_RESET_AND_ID, 0); 225262306a36Sopenharmony_ci if (ret < 0) { 225362306a36Sopenharmony_ci dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret); 225462306a36Sopenharmony_ci goto err_enable; 225562306a36Sopenharmony_ci } 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci /* Change some default settings - latch VU and enable ZC */ 225862306a36Sopenharmony_ci regmap_update_bits(wm8904->regmap, WM8904_ADC_DIGITAL_VOLUME_LEFT, 225962306a36Sopenharmony_ci WM8904_ADC_VU, WM8904_ADC_VU); 226062306a36Sopenharmony_ci regmap_update_bits(wm8904->regmap, WM8904_ADC_DIGITAL_VOLUME_RIGHT, 226162306a36Sopenharmony_ci WM8904_ADC_VU, WM8904_ADC_VU); 226262306a36Sopenharmony_ci regmap_update_bits(wm8904->regmap, WM8904_DAC_DIGITAL_VOLUME_LEFT, 226362306a36Sopenharmony_ci WM8904_DAC_VU, WM8904_DAC_VU); 226462306a36Sopenharmony_ci regmap_update_bits(wm8904->regmap, WM8904_DAC_DIGITAL_VOLUME_RIGHT, 226562306a36Sopenharmony_ci WM8904_DAC_VU, WM8904_DAC_VU); 226662306a36Sopenharmony_ci regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT1_LEFT, 226762306a36Sopenharmony_ci WM8904_HPOUT_VU | WM8904_HPOUTLZC, 226862306a36Sopenharmony_ci WM8904_HPOUT_VU | WM8904_HPOUTLZC); 226962306a36Sopenharmony_ci regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT1_RIGHT, 227062306a36Sopenharmony_ci WM8904_HPOUT_VU | WM8904_HPOUTRZC, 227162306a36Sopenharmony_ci WM8904_HPOUT_VU | WM8904_HPOUTRZC); 227262306a36Sopenharmony_ci regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT2_LEFT, 227362306a36Sopenharmony_ci WM8904_LINEOUT_VU | WM8904_LINEOUTLZC, 227462306a36Sopenharmony_ci WM8904_LINEOUT_VU | WM8904_LINEOUTLZC); 227562306a36Sopenharmony_ci regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT2_RIGHT, 227662306a36Sopenharmony_ci WM8904_LINEOUT_VU | WM8904_LINEOUTRZC, 227762306a36Sopenharmony_ci WM8904_LINEOUT_VU | WM8904_LINEOUTRZC); 227862306a36Sopenharmony_ci regmap_update_bits(wm8904->regmap, WM8904_CLOCK_RATES_0, 227962306a36Sopenharmony_ci WM8904_SR_MODE, 0); 228062306a36Sopenharmony_ci 228162306a36Sopenharmony_ci /* Apply configuration from the platform data. */ 228262306a36Sopenharmony_ci if (wm8904->pdata) { 228362306a36Sopenharmony_ci for (i = 0; i < WM8904_GPIO_REGS; i++) { 228462306a36Sopenharmony_ci if (!wm8904->pdata->gpio_cfg[i]) 228562306a36Sopenharmony_ci continue; 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci regmap_update_bits(wm8904->regmap, 228862306a36Sopenharmony_ci WM8904_GPIO_CONTROL_1 + i, 228962306a36Sopenharmony_ci 0xffff, 229062306a36Sopenharmony_ci wm8904->pdata->gpio_cfg[i]); 229162306a36Sopenharmony_ci } 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci /* Zero is the default value for these anyway */ 229462306a36Sopenharmony_ci for (i = 0; i < WM8904_MIC_REGS; i++) 229562306a36Sopenharmony_ci regmap_update_bits(wm8904->regmap, 229662306a36Sopenharmony_ci WM8904_MIC_BIAS_CONTROL_0 + i, 229762306a36Sopenharmony_ci 0xffff, 229862306a36Sopenharmony_ci wm8904->pdata->mic_cfg[i]); 229962306a36Sopenharmony_ci } 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci /* Set Class W by default - this will be managed by the Class 230262306a36Sopenharmony_ci * G widget at runtime where bypass paths are available. 230362306a36Sopenharmony_ci */ 230462306a36Sopenharmony_ci regmap_update_bits(wm8904->regmap, WM8904_CLASS_W_0, 230562306a36Sopenharmony_ci WM8904_CP_DYN_PWR, WM8904_CP_DYN_PWR); 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_ci /* Use normal bias source */ 230862306a36Sopenharmony_ci regmap_update_bits(wm8904->regmap, WM8904_BIAS_CONTROL_0, 230962306a36Sopenharmony_ci WM8904_POBCTRL, 0); 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci /* Fill the cache for the ADC test register */ 231262306a36Sopenharmony_ci regmap_read(wm8904->regmap, WM8904_ADC_TEST_0, &val); 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci /* Can leave the device powered off until we need it */ 231562306a36Sopenharmony_ci regcache_cache_only(wm8904->regmap, true); 231662306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies); 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&i2c->dev, 231962306a36Sopenharmony_ci &soc_component_dev_wm8904, &wm8904_dai, 1); 232062306a36Sopenharmony_ci if (ret != 0) 232162306a36Sopenharmony_ci return ret; 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci return 0; 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_cierr_enable: 232662306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies); 232762306a36Sopenharmony_ci return ret; 232862306a36Sopenharmony_ci} 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_cistatic const struct i2c_device_id wm8904_i2c_id[] = { 233162306a36Sopenharmony_ci { "wm8904", WM8904 }, 233262306a36Sopenharmony_ci { "wm8912", WM8912 }, 233362306a36Sopenharmony_ci { "wm8918", WM8904 }, /* Actually a subset, updates to follow */ 233462306a36Sopenharmony_ci { } 233562306a36Sopenharmony_ci}; 233662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, wm8904_i2c_id); 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_cistatic struct i2c_driver wm8904_i2c_driver = { 233962306a36Sopenharmony_ci .driver = { 234062306a36Sopenharmony_ci .name = "wm8904", 234162306a36Sopenharmony_ci .of_match_table = of_match_ptr(wm8904_of_match), 234262306a36Sopenharmony_ci }, 234362306a36Sopenharmony_ci .probe = wm8904_i2c_probe, 234462306a36Sopenharmony_ci .id_table = wm8904_i2c_id, 234562306a36Sopenharmony_ci}; 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_cimodule_i2c_driver(wm8904_i2c_driver); 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC WM8904 driver"); 235062306a36Sopenharmony_ciMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 235162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2352