162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * wm2000.c -- WM2000 ALSA Soc Audio driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2008-2011 Wolfson Microelectronics PLC. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * The download image for the WM2000 will be requested as 1062306a36Sopenharmony_ci * 'wm2000_anc.bin' by default (overridable via platform data) at 1162306a36Sopenharmony_ci * runtime and is expected to be in flat binary format. This is 1262306a36Sopenharmony_ci * generated by Wolfson configuration tools and includes 1362306a36Sopenharmony_ci * system-specific calibration information. If supplied as a 1462306a36Sopenharmony_ci * sequence of ASCII-encoded hexidecimal bytes this can be converted 1562306a36Sopenharmony_ci * into a flat binary with a command such as this on the command line: 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * perl -e 'while (<>) { s/[\r\n]+// ; printf("%c", hex($_)); }' 1862306a36Sopenharmony_ci * < file > wm2000_anc.bin 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/moduleparam.h> 2362306a36Sopenharmony_ci#include <linux/kernel.h> 2462306a36Sopenharmony_ci#include <linux/init.h> 2562306a36Sopenharmony_ci#include <linux/firmware.h> 2662306a36Sopenharmony_ci#include <linux/clk.h> 2762306a36Sopenharmony_ci#include <linux/delay.h> 2862306a36Sopenharmony_ci#include <linux/pm.h> 2962306a36Sopenharmony_ci#include <linux/i2c.h> 3062306a36Sopenharmony_ci#include <linux/regmap.h> 3162306a36Sopenharmony_ci#include <linux/debugfs.h> 3262306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 3362306a36Sopenharmony_ci#include <linux/slab.h> 3462306a36Sopenharmony_ci#include <sound/core.h> 3562306a36Sopenharmony_ci#include <sound/pcm.h> 3662306a36Sopenharmony_ci#include <sound/pcm_params.h> 3762306a36Sopenharmony_ci#include <sound/soc.h> 3862306a36Sopenharmony_ci#include <sound/initval.h> 3962306a36Sopenharmony_ci#include <sound/tlv.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#include <sound/wm2000.h> 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#include "wm2000.h" 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define WM2000_NUM_SUPPLIES 3 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic const char *wm2000_supplies[WM2000_NUM_SUPPLIES] = { 4862306a36Sopenharmony_ci "SPKVDD", 4962306a36Sopenharmony_ci "DBVDD", 5062306a36Sopenharmony_ci "DCVDD", 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cienum wm2000_anc_mode { 5462306a36Sopenharmony_ci ANC_ACTIVE = 0, 5562306a36Sopenharmony_ci ANC_BYPASS = 1, 5662306a36Sopenharmony_ci ANC_STANDBY = 2, 5762306a36Sopenharmony_ci ANC_OFF = 3, 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct wm2000_priv { 6162306a36Sopenharmony_ci struct i2c_client *i2c; 6262306a36Sopenharmony_ci struct regmap *regmap; 6362306a36Sopenharmony_ci struct clk *mclk; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci struct regulator_bulk_data supplies[WM2000_NUM_SUPPLIES]; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci enum wm2000_anc_mode anc_mode; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci unsigned int anc_active:1; 7062306a36Sopenharmony_ci unsigned int anc_eng_ena:1; 7162306a36Sopenharmony_ci unsigned int spk_ena:1; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci unsigned int speech_clarity:1; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci int anc_download_size; 7662306a36Sopenharmony_ci char *anc_download; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci struct mutex lock; 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int wm2000_write(struct i2c_client *i2c, unsigned int reg, 8262306a36Sopenharmony_ci unsigned int value) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct wm2000_priv *wm2000 = i2c_get_clientdata(i2c); 8562306a36Sopenharmony_ci return regmap_write(wm2000->regmap, reg, value); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void wm2000_reset(struct wm2000_priv *wm2000) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct i2c_client *i2c = wm2000->i2c; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_CLR); 9362306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR); 9462306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_ID1, 0); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci wm2000->anc_mode = ANC_OFF; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int wm2000_poll_bit(struct i2c_client *i2c, 10062306a36Sopenharmony_ci unsigned int reg, u8 mask) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct wm2000_priv *wm2000 = i2c_get_clientdata(i2c); 10362306a36Sopenharmony_ci int timeout = 4000; 10462306a36Sopenharmony_ci unsigned int val; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci regmap_read(wm2000->regmap, reg, &val); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci while (!(val & mask) && --timeout) { 10962306a36Sopenharmony_ci msleep(1); 11062306a36Sopenharmony_ci regmap_read(wm2000->regmap, reg, &val); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (timeout == 0) 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci else 11662306a36Sopenharmony_ci return 1; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int wm2000_power_up(struct i2c_client *i2c, int analogue) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); 12262306a36Sopenharmony_ci unsigned long rate; 12362306a36Sopenharmony_ci unsigned int val; 12462306a36Sopenharmony_ci int ret; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (WARN_ON(wm2000->anc_mode != ANC_OFF)) 12762306a36Sopenharmony_ci return -EINVAL; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci dev_dbg(&i2c->dev, "Beginning power up\n"); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci ret = regulator_bulk_enable(WM2000_NUM_SUPPLIES, wm2000->supplies); 13262306a36Sopenharmony_ci if (ret != 0) { 13362306a36Sopenharmony_ci dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); 13462306a36Sopenharmony_ci return ret; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci rate = clk_get_rate(wm2000->mclk); 13862306a36Sopenharmony_ci if (rate <= 13500000) { 13962306a36Sopenharmony_ci dev_dbg(&i2c->dev, "Disabling MCLK divider\n"); 14062306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, 14162306a36Sopenharmony_ci WM2000_MCLK_DIV2_ENA_CLR); 14262306a36Sopenharmony_ci } else { 14362306a36Sopenharmony_ci dev_dbg(&i2c->dev, "Enabling MCLK divider\n"); 14462306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, 14562306a36Sopenharmony_ci WM2000_MCLK_DIV2_ENA_SET); 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_CLR); 14962306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_SET); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Wait for ANC engine to become ready */ 15262306a36Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT, 15362306a36Sopenharmony_ci WM2000_ANC_ENG_IDLE)) { 15462306a36Sopenharmony_ci dev_err(&i2c->dev, "ANC engine failed to reset\n"); 15562306a36Sopenharmony_ci regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); 15662306a36Sopenharmony_ci return -ETIMEDOUT; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, 16062306a36Sopenharmony_ci WM2000_STATUS_BOOT_COMPLETE)) { 16162306a36Sopenharmony_ci dev_err(&i2c->dev, "ANC engine failed to initialise\n"); 16262306a36Sopenharmony_ci regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); 16362306a36Sopenharmony_ci return -ETIMEDOUT; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Open code download of the data since it is the only bulk 16962306a36Sopenharmony_ci * write we do. */ 17062306a36Sopenharmony_ci dev_dbg(&i2c->dev, "Downloading %d bytes\n", 17162306a36Sopenharmony_ci wm2000->anc_download_size - 2); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci ret = i2c_master_send(i2c, wm2000->anc_download, 17462306a36Sopenharmony_ci wm2000->anc_download_size); 17562306a36Sopenharmony_ci if (ret < 0) { 17662306a36Sopenharmony_ci dev_err(&i2c->dev, "i2c_transfer() failed: %d\n", ret); 17762306a36Sopenharmony_ci regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); 17862306a36Sopenharmony_ci return ret; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci if (ret != wm2000->anc_download_size) { 18162306a36Sopenharmony_ci dev_err(&i2c->dev, "i2c_transfer() failed, %d != %d\n", 18262306a36Sopenharmony_ci ret, wm2000->anc_download_size); 18362306a36Sopenharmony_ci regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); 18462306a36Sopenharmony_ci return -EIO; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci dev_dbg(&i2c->dev, "Download complete\n"); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (analogue) { 19062306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_ANA_VMID_PU_TIME, 248 / 4); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 19362306a36Sopenharmony_ci WM2000_MODE_ANA_SEQ_INCLUDE | 19462306a36Sopenharmony_ci WM2000_MODE_MOUSE_ENABLE | 19562306a36Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE); 19662306a36Sopenharmony_ci } else { 19762306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 19862306a36Sopenharmony_ci WM2000_MODE_MOUSE_ENABLE | 19962306a36Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci ret = regmap_read(wm2000->regmap, WM2000_REG_SPEECH_CLARITY, &val); 20362306a36Sopenharmony_ci if (ret != 0) { 20462306a36Sopenharmony_ci dev_err(&i2c->dev, "Unable to read Speech Clarity: %d\n", ret); 20562306a36Sopenharmony_ci regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); 20662306a36Sopenharmony_ci return ret; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci if (wm2000->speech_clarity) 20962306a36Sopenharmony_ci val |= WM2000_SPEECH_CLARITY; 21062306a36Sopenharmony_ci else 21162306a36Sopenharmony_ci val &= ~WM2000_SPEECH_CLARITY; 21262306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SPEECH_CLARITY, val); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_START0, 0x33); 21562306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_START1, 0x02); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, 22062306a36Sopenharmony_ci WM2000_STATUS_MOUSE_ACTIVE)) { 22162306a36Sopenharmony_ci dev_err(&i2c->dev, "Timed out waiting for device\n"); 22262306a36Sopenharmony_ci regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); 22362306a36Sopenharmony_ci return -ETIMEDOUT; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci dev_dbg(&i2c->dev, "ANC active\n"); 22762306a36Sopenharmony_ci if (analogue) 22862306a36Sopenharmony_ci dev_dbg(&i2c->dev, "Analogue active\n"); 22962306a36Sopenharmony_ci wm2000->anc_mode = ANC_ACTIVE; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic int wm2000_power_down(struct i2c_client *i2c, int analogue) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (analogue) { 23962306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_ANA_VMID_PD_TIME, 248 / 4); 24062306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 24162306a36Sopenharmony_ci WM2000_MODE_ANA_SEQ_INCLUDE | 24262306a36Sopenharmony_ci WM2000_MODE_POWER_DOWN); 24362306a36Sopenharmony_ci } else { 24462306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 24562306a36Sopenharmony_ci WM2000_MODE_POWER_DOWN); 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, 24962306a36Sopenharmony_ci WM2000_STATUS_POWER_DOWN_COMPLETE)) { 25062306a36Sopenharmony_ci dev_err(&i2c->dev, "Timeout waiting for ANC power down\n"); 25162306a36Sopenharmony_ci return -ETIMEDOUT; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT, 25562306a36Sopenharmony_ci WM2000_ANC_ENG_IDLE)) { 25662306a36Sopenharmony_ci dev_err(&i2c->dev, "Timeout waiting for ANC engine idle\n"); 25762306a36Sopenharmony_ci return -ETIMEDOUT; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci dev_dbg(&i2c->dev, "powered off\n"); 26362306a36Sopenharmony_ci wm2000->anc_mode = ANC_OFF; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic int wm2000_enter_bypass(struct i2c_client *i2c, int analogue) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (WARN_ON(wm2000->anc_mode != ANC_ACTIVE)) 27362306a36Sopenharmony_ci return -EINVAL; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (analogue) { 27662306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 27762306a36Sopenharmony_ci WM2000_MODE_ANA_SEQ_INCLUDE | 27862306a36Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE | 27962306a36Sopenharmony_ci WM2000_MODE_BYPASS_ENTRY); 28062306a36Sopenharmony_ci } else { 28162306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 28262306a36Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE | 28362306a36Sopenharmony_ci WM2000_MODE_BYPASS_ENTRY); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, 28762306a36Sopenharmony_ci WM2000_STATUS_ANC_DISABLED)) { 28862306a36Sopenharmony_ci dev_err(&i2c->dev, "Timeout waiting for ANC disable\n"); 28962306a36Sopenharmony_ci return -ETIMEDOUT; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT, 29362306a36Sopenharmony_ci WM2000_ANC_ENG_IDLE)) { 29462306a36Sopenharmony_ci dev_err(&i2c->dev, "Timeout waiting for ANC engine idle\n"); 29562306a36Sopenharmony_ci return -ETIMEDOUT; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL1, WM2000_SYS_STBY); 29962306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci wm2000->anc_mode = ANC_BYPASS; 30262306a36Sopenharmony_ci dev_dbg(&i2c->dev, "bypass enabled\n"); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic int wm2000_exit_bypass(struct i2c_client *i2c, int analogue) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (WARN_ON(wm2000->anc_mode != ANC_BYPASS)) 31262306a36Sopenharmony_ci return -EINVAL; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL1, 0); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (analogue) { 31762306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 31862306a36Sopenharmony_ci WM2000_MODE_ANA_SEQ_INCLUDE | 31962306a36Sopenharmony_ci WM2000_MODE_MOUSE_ENABLE | 32062306a36Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE); 32162306a36Sopenharmony_ci } else { 32262306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 32362306a36Sopenharmony_ci WM2000_MODE_MOUSE_ENABLE | 32462306a36Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE); 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET); 32862306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, 33162306a36Sopenharmony_ci WM2000_STATUS_MOUSE_ACTIVE)) { 33262306a36Sopenharmony_ci dev_err(&i2c->dev, "Timed out waiting for MOUSE\n"); 33362306a36Sopenharmony_ci return -ETIMEDOUT; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci wm2000->anc_mode = ANC_ACTIVE; 33762306a36Sopenharmony_ci dev_dbg(&i2c->dev, "MOUSE active\n"); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci return 0; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic int wm2000_enter_standby(struct i2c_client *i2c, int analogue) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (WARN_ON(wm2000->anc_mode != ANC_ACTIVE)) 34762306a36Sopenharmony_ci return -EINVAL; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (analogue) { 35062306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_ANA_VMID_PD_TIME, 248 / 4); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 35362306a36Sopenharmony_ci WM2000_MODE_ANA_SEQ_INCLUDE | 35462306a36Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE | 35562306a36Sopenharmony_ci WM2000_MODE_STANDBY_ENTRY); 35662306a36Sopenharmony_ci } else { 35762306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 35862306a36Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE | 35962306a36Sopenharmony_ci WM2000_MODE_STANDBY_ENTRY); 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, 36362306a36Sopenharmony_ci WM2000_STATUS_ANC_DISABLED)) { 36462306a36Sopenharmony_ci dev_err(&i2c->dev, 36562306a36Sopenharmony_ci "Timed out waiting for ANC disable after 1ms\n"); 36662306a36Sopenharmony_ci return -ETIMEDOUT; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT, WM2000_ANC_ENG_IDLE)) { 37062306a36Sopenharmony_ci dev_err(&i2c->dev, 37162306a36Sopenharmony_ci "Timed out waiting for standby\n"); 37262306a36Sopenharmony_ci return -ETIMEDOUT; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL1, WM2000_SYS_STBY); 37662306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci wm2000->anc_mode = ANC_STANDBY; 37962306a36Sopenharmony_ci dev_dbg(&i2c->dev, "standby\n"); 38062306a36Sopenharmony_ci if (analogue) 38162306a36Sopenharmony_ci dev_dbg(&i2c->dev, "Analogue disabled\n"); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return 0; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic int wm2000_exit_standby(struct i2c_client *i2c, int analogue) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (WARN_ON(wm2000->anc_mode != ANC_STANDBY)) 39162306a36Sopenharmony_ci return -EINVAL; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL1, 0); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (analogue) { 39662306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_ANA_VMID_PU_TIME, 248 / 4); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 39962306a36Sopenharmony_ci WM2000_MODE_ANA_SEQ_INCLUDE | 40062306a36Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE | 40162306a36Sopenharmony_ci WM2000_MODE_MOUSE_ENABLE); 40262306a36Sopenharmony_ci } else { 40362306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 40462306a36Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE | 40562306a36Sopenharmony_ci WM2000_MODE_MOUSE_ENABLE); 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET); 40962306a36Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, 41262306a36Sopenharmony_ci WM2000_STATUS_MOUSE_ACTIVE)) { 41362306a36Sopenharmony_ci dev_err(&i2c->dev, "Timed out waiting for MOUSE\n"); 41462306a36Sopenharmony_ci return -ETIMEDOUT; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci wm2000->anc_mode = ANC_ACTIVE; 41862306a36Sopenharmony_ci dev_dbg(&i2c->dev, "MOUSE active\n"); 41962306a36Sopenharmony_ci if (analogue) 42062306a36Sopenharmony_ci dev_dbg(&i2c->dev, "Analogue enabled\n"); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return 0; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_citypedef int (*wm2000_mode_fn)(struct i2c_client *i2c, int analogue); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic struct { 42862306a36Sopenharmony_ci enum wm2000_anc_mode source; 42962306a36Sopenharmony_ci enum wm2000_anc_mode dest; 43062306a36Sopenharmony_ci int analogue; 43162306a36Sopenharmony_ci wm2000_mode_fn step[2]; 43262306a36Sopenharmony_ci} anc_transitions[] = { 43362306a36Sopenharmony_ci { 43462306a36Sopenharmony_ci .source = ANC_OFF, 43562306a36Sopenharmony_ci .dest = ANC_ACTIVE, 43662306a36Sopenharmony_ci .analogue = 1, 43762306a36Sopenharmony_ci .step = { 43862306a36Sopenharmony_ci wm2000_power_up, 43962306a36Sopenharmony_ci }, 44062306a36Sopenharmony_ci }, 44162306a36Sopenharmony_ci { 44262306a36Sopenharmony_ci .source = ANC_OFF, 44362306a36Sopenharmony_ci .dest = ANC_STANDBY, 44462306a36Sopenharmony_ci .step = { 44562306a36Sopenharmony_ci wm2000_power_up, 44662306a36Sopenharmony_ci wm2000_enter_standby, 44762306a36Sopenharmony_ci }, 44862306a36Sopenharmony_ci }, 44962306a36Sopenharmony_ci { 45062306a36Sopenharmony_ci .source = ANC_OFF, 45162306a36Sopenharmony_ci .dest = ANC_BYPASS, 45262306a36Sopenharmony_ci .analogue = 1, 45362306a36Sopenharmony_ci .step = { 45462306a36Sopenharmony_ci wm2000_power_up, 45562306a36Sopenharmony_ci wm2000_enter_bypass, 45662306a36Sopenharmony_ci }, 45762306a36Sopenharmony_ci }, 45862306a36Sopenharmony_ci { 45962306a36Sopenharmony_ci .source = ANC_ACTIVE, 46062306a36Sopenharmony_ci .dest = ANC_BYPASS, 46162306a36Sopenharmony_ci .analogue = 1, 46262306a36Sopenharmony_ci .step = { 46362306a36Sopenharmony_ci wm2000_enter_bypass, 46462306a36Sopenharmony_ci }, 46562306a36Sopenharmony_ci }, 46662306a36Sopenharmony_ci { 46762306a36Sopenharmony_ci .source = ANC_ACTIVE, 46862306a36Sopenharmony_ci .dest = ANC_STANDBY, 46962306a36Sopenharmony_ci .analogue = 1, 47062306a36Sopenharmony_ci .step = { 47162306a36Sopenharmony_ci wm2000_enter_standby, 47262306a36Sopenharmony_ci }, 47362306a36Sopenharmony_ci }, 47462306a36Sopenharmony_ci { 47562306a36Sopenharmony_ci .source = ANC_ACTIVE, 47662306a36Sopenharmony_ci .dest = ANC_OFF, 47762306a36Sopenharmony_ci .analogue = 1, 47862306a36Sopenharmony_ci .step = { 47962306a36Sopenharmony_ci wm2000_power_down, 48062306a36Sopenharmony_ci }, 48162306a36Sopenharmony_ci }, 48262306a36Sopenharmony_ci { 48362306a36Sopenharmony_ci .source = ANC_BYPASS, 48462306a36Sopenharmony_ci .dest = ANC_ACTIVE, 48562306a36Sopenharmony_ci .analogue = 1, 48662306a36Sopenharmony_ci .step = { 48762306a36Sopenharmony_ci wm2000_exit_bypass, 48862306a36Sopenharmony_ci }, 48962306a36Sopenharmony_ci }, 49062306a36Sopenharmony_ci { 49162306a36Sopenharmony_ci .source = ANC_BYPASS, 49262306a36Sopenharmony_ci .dest = ANC_STANDBY, 49362306a36Sopenharmony_ci .analogue = 1, 49462306a36Sopenharmony_ci .step = { 49562306a36Sopenharmony_ci wm2000_exit_bypass, 49662306a36Sopenharmony_ci wm2000_enter_standby, 49762306a36Sopenharmony_ci }, 49862306a36Sopenharmony_ci }, 49962306a36Sopenharmony_ci { 50062306a36Sopenharmony_ci .source = ANC_BYPASS, 50162306a36Sopenharmony_ci .dest = ANC_OFF, 50262306a36Sopenharmony_ci .step = { 50362306a36Sopenharmony_ci wm2000_exit_bypass, 50462306a36Sopenharmony_ci wm2000_power_down, 50562306a36Sopenharmony_ci }, 50662306a36Sopenharmony_ci }, 50762306a36Sopenharmony_ci { 50862306a36Sopenharmony_ci .source = ANC_STANDBY, 50962306a36Sopenharmony_ci .dest = ANC_ACTIVE, 51062306a36Sopenharmony_ci .analogue = 1, 51162306a36Sopenharmony_ci .step = { 51262306a36Sopenharmony_ci wm2000_exit_standby, 51362306a36Sopenharmony_ci }, 51462306a36Sopenharmony_ci }, 51562306a36Sopenharmony_ci { 51662306a36Sopenharmony_ci .source = ANC_STANDBY, 51762306a36Sopenharmony_ci .dest = ANC_BYPASS, 51862306a36Sopenharmony_ci .analogue = 1, 51962306a36Sopenharmony_ci .step = { 52062306a36Sopenharmony_ci wm2000_exit_standby, 52162306a36Sopenharmony_ci wm2000_enter_bypass, 52262306a36Sopenharmony_ci }, 52362306a36Sopenharmony_ci }, 52462306a36Sopenharmony_ci { 52562306a36Sopenharmony_ci .source = ANC_STANDBY, 52662306a36Sopenharmony_ci .dest = ANC_OFF, 52762306a36Sopenharmony_ci .step = { 52862306a36Sopenharmony_ci wm2000_exit_standby, 52962306a36Sopenharmony_ci wm2000_power_down, 53062306a36Sopenharmony_ci }, 53162306a36Sopenharmony_ci }, 53262306a36Sopenharmony_ci}; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic int wm2000_anc_transition(struct wm2000_priv *wm2000, 53562306a36Sopenharmony_ci enum wm2000_anc_mode mode) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct i2c_client *i2c = wm2000->i2c; 53862306a36Sopenharmony_ci int i, j; 53962306a36Sopenharmony_ci int ret = 0; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (wm2000->anc_mode == mode) 54262306a36Sopenharmony_ci return 0; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(anc_transitions); i++) 54562306a36Sopenharmony_ci if (anc_transitions[i].source == wm2000->anc_mode && 54662306a36Sopenharmony_ci anc_transitions[i].dest == mode) 54762306a36Sopenharmony_ci break; 54862306a36Sopenharmony_ci if (i == ARRAY_SIZE(anc_transitions)) { 54962306a36Sopenharmony_ci dev_err(&i2c->dev, "No transition for %d->%d\n", 55062306a36Sopenharmony_ci wm2000->anc_mode, mode); 55162306a36Sopenharmony_ci return -EINVAL; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* Maintain clock while active */ 55562306a36Sopenharmony_ci if (anc_transitions[i].source == ANC_OFF) { 55662306a36Sopenharmony_ci ret = clk_prepare_enable(wm2000->mclk); 55762306a36Sopenharmony_ci if (ret != 0) { 55862306a36Sopenharmony_ci dev_err(&i2c->dev, "Failed to enable MCLK: %d\n", ret); 55962306a36Sopenharmony_ci return ret; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(anc_transitions[j].step); j++) { 56462306a36Sopenharmony_ci if (!anc_transitions[i].step[j]) 56562306a36Sopenharmony_ci break; 56662306a36Sopenharmony_ci ret = anc_transitions[i].step[j](i2c, 56762306a36Sopenharmony_ci anc_transitions[i].analogue); 56862306a36Sopenharmony_ci if (ret != 0) 56962306a36Sopenharmony_ci break; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (anc_transitions[i].dest == ANC_OFF) 57362306a36Sopenharmony_ci clk_disable_unprepare(wm2000->mclk); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci return ret; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic int wm2000_anc_set_mode(struct wm2000_priv *wm2000) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct i2c_client *i2c = wm2000->i2c; 58162306a36Sopenharmony_ci enum wm2000_anc_mode mode; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (wm2000->anc_eng_ena && wm2000->spk_ena) 58462306a36Sopenharmony_ci if (wm2000->anc_active) 58562306a36Sopenharmony_ci mode = ANC_ACTIVE; 58662306a36Sopenharmony_ci else 58762306a36Sopenharmony_ci mode = ANC_BYPASS; 58862306a36Sopenharmony_ci else 58962306a36Sopenharmony_ci mode = ANC_STANDBY; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci dev_dbg(&i2c->dev, "Set mode %d (enabled %d, mute %d, active %d)\n", 59262306a36Sopenharmony_ci mode, wm2000->anc_eng_ena, !wm2000->spk_ena, 59362306a36Sopenharmony_ci wm2000->anc_active); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return wm2000_anc_transition(wm2000, mode); 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic int wm2000_anc_mode_get(struct snd_kcontrol *kcontrol, 59962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 60262306a36Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci ucontrol->value.integer.value[0] = wm2000->anc_active; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return 0; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, 61062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 61362306a36Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 61462306a36Sopenharmony_ci unsigned int anc_active = ucontrol->value.integer.value[0]; 61562306a36Sopenharmony_ci int ret; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (anc_active > 1) 61862306a36Sopenharmony_ci return -EINVAL; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci mutex_lock(&wm2000->lock); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci wm2000->anc_active = anc_active; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci ret = wm2000_anc_set_mode(wm2000); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci mutex_unlock(&wm2000->lock); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci return ret; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic int wm2000_speaker_get(struct snd_kcontrol *kcontrol, 63262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 63562306a36Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci ucontrol->value.integer.value[0] = wm2000->spk_ena; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return 0; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic int wm2000_speaker_put(struct snd_kcontrol *kcontrol, 64362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 64662306a36Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 64762306a36Sopenharmony_ci unsigned int val = ucontrol->value.integer.value[0]; 64862306a36Sopenharmony_ci int ret; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (val > 1) 65162306a36Sopenharmony_ci return -EINVAL; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci mutex_lock(&wm2000->lock); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci wm2000->spk_ena = val; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci ret = wm2000_anc_set_mode(wm2000); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci mutex_unlock(&wm2000->lock); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci return ret; 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic const struct snd_kcontrol_new wm2000_controls[] = { 66562306a36Sopenharmony_ci SOC_SINGLE("ANC Volume", WM2000_REG_ANC_GAIN_CTRL, 0, 255, 0), 66662306a36Sopenharmony_ci SOC_SINGLE_BOOL_EXT("WM2000 ANC Switch", 0, 66762306a36Sopenharmony_ci wm2000_anc_mode_get, 66862306a36Sopenharmony_ci wm2000_anc_mode_put), 66962306a36Sopenharmony_ci SOC_SINGLE_BOOL_EXT("WM2000 Switch", 0, 67062306a36Sopenharmony_ci wm2000_speaker_get, 67162306a36Sopenharmony_ci wm2000_speaker_put), 67262306a36Sopenharmony_ci}; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, 67562306a36Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 67862306a36Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 67962306a36Sopenharmony_ci int ret; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci mutex_lock(&wm2000->lock); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (SND_SOC_DAPM_EVENT_ON(event)) 68462306a36Sopenharmony_ci wm2000->anc_eng_ena = 1; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (SND_SOC_DAPM_EVENT_OFF(event)) 68762306a36Sopenharmony_ci wm2000->anc_eng_ena = 0; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci ret = wm2000_anc_set_mode(wm2000); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci mutex_unlock(&wm2000->lock); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci return ret; 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = { 69762306a36Sopenharmony_ci/* Externally visible pins */ 69862306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("SPKN"), 69962306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("SPKP"), 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("LINN"), 70262306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("LINP"), 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ciSND_SOC_DAPM_PGA_E("ANC Engine", SND_SOC_NOPM, 0, 0, NULL, 0, 70562306a36Sopenharmony_ci wm2000_anc_power_event, 70662306a36Sopenharmony_ci SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), 70762306a36Sopenharmony_ci}; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci/* Target, Path, Source */ 71062306a36Sopenharmony_cistatic const struct snd_soc_dapm_route wm2000_audio_map[] = { 71162306a36Sopenharmony_ci { "SPKN", NULL, "ANC Engine" }, 71262306a36Sopenharmony_ci { "SPKP", NULL, "ANC Engine" }, 71362306a36Sopenharmony_ci { "ANC Engine", NULL, "LINN" }, 71462306a36Sopenharmony_ci { "ANC Engine", NULL, "LINP" }, 71562306a36Sopenharmony_ci}; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci#ifdef CONFIG_PM 71862306a36Sopenharmony_cistatic int wm2000_suspend(struct snd_soc_component *component) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci return wm2000_anc_transition(wm2000, ANC_OFF); 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic int wm2000_resume(struct snd_soc_component *component) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci return wm2000_anc_set_mode(wm2000); 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci#else 73262306a36Sopenharmony_ci#define wm2000_suspend NULL 73362306a36Sopenharmony_ci#define wm2000_resume NULL 73462306a36Sopenharmony_ci#endif 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic bool wm2000_readable_reg(struct device *dev, unsigned int reg) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci switch (reg) { 73962306a36Sopenharmony_ci case WM2000_REG_SYS_START: 74062306a36Sopenharmony_ci case WM2000_REG_ANC_GAIN_CTRL: 74162306a36Sopenharmony_ci case WM2000_REG_MSE_TH1: 74262306a36Sopenharmony_ci case WM2000_REG_MSE_TH2: 74362306a36Sopenharmony_ci case WM2000_REG_SPEECH_CLARITY: 74462306a36Sopenharmony_ci case WM2000_REG_SYS_WATCHDOG: 74562306a36Sopenharmony_ci case WM2000_REG_ANA_VMID_PD_TIME: 74662306a36Sopenharmony_ci case WM2000_REG_ANA_VMID_PU_TIME: 74762306a36Sopenharmony_ci case WM2000_REG_CAT_FLTR_INDX: 74862306a36Sopenharmony_ci case WM2000_REG_CAT_GAIN_0: 74962306a36Sopenharmony_ci case WM2000_REG_SYS_STATUS: 75062306a36Sopenharmony_ci case WM2000_REG_SYS_MODE_CNTRL: 75162306a36Sopenharmony_ci case WM2000_REG_SYS_START0: 75262306a36Sopenharmony_ci case WM2000_REG_SYS_START1: 75362306a36Sopenharmony_ci case WM2000_REG_ID1: 75462306a36Sopenharmony_ci case WM2000_REG_ID2: 75562306a36Sopenharmony_ci case WM2000_REG_REVISON: 75662306a36Sopenharmony_ci case WM2000_REG_SYS_CTL1: 75762306a36Sopenharmony_ci case WM2000_REG_SYS_CTL2: 75862306a36Sopenharmony_ci case WM2000_REG_ANC_STAT: 75962306a36Sopenharmony_ci case WM2000_REG_IF_CTL: 76062306a36Sopenharmony_ci case WM2000_REG_ANA_MIC_CTL: 76162306a36Sopenharmony_ci case WM2000_REG_SPK_CTL: 76262306a36Sopenharmony_ci return true; 76362306a36Sopenharmony_ci default: 76462306a36Sopenharmony_ci return false; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic const struct regmap_config wm2000_regmap = { 76962306a36Sopenharmony_ci .reg_bits = 16, 77062306a36Sopenharmony_ci .val_bits = 8, 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci .max_register = WM2000_REG_SPK_CTL, 77362306a36Sopenharmony_ci .readable_reg = wm2000_readable_reg, 77462306a36Sopenharmony_ci}; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic int wm2000_probe(struct snd_soc_component *component) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* This will trigger a transition to standby mode by default */ 78162306a36Sopenharmony_ci wm2000_anc_set_mode(wm2000); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci return 0; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic void wm2000_remove(struct snd_soc_component *component) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci wm2000_anc_transition(wm2000, ANC_OFF); 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_wm2000 = { 79462306a36Sopenharmony_ci .probe = wm2000_probe, 79562306a36Sopenharmony_ci .remove = wm2000_remove, 79662306a36Sopenharmony_ci .suspend = wm2000_suspend, 79762306a36Sopenharmony_ci .resume = wm2000_resume, 79862306a36Sopenharmony_ci .controls = wm2000_controls, 79962306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(wm2000_controls), 80062306a36Sopenharmony_ci .dapm_widgets = wm2000_dapm_widgets, 80162306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(wm2000_dapm_widgets), 80262306a36Sopenharmony_ci .dapm_routes = wm2000_audio_map, 80362306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(wm2000_audio_map), 80462306a36Sopenharmony_ci .idle_bias_on = 1, 80562306a36Sopenharmony_ci .use_pmdown_time = 1, 80662306a36Sopenharmony_ci}; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic int wm2000_i2c_probe(struct i2c_client *i2c) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci struct wm2000_priv *wm2000; 81162306a36Sopenharmony_ci struct wm2000_platform_data *pdata; 81262306a36Sopenharmony_ci const char *filename; 81362306a36Sopenharmony_ci const struct firmware *fw = NULL; 81462306a36Sopenharmony_ci int ret, i; 81562306a36Sopenharmony_ci unsigned int reg; 81662306a36Sopenharmony_ci u16 id; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci wm2000 = devm_kzalloc(&i2c->dev, sizeof(*wm2000), GFP_KERNEL); 81962306a36Sopenharmony_ci if (!wm2000) 82062306a36Sopenharmony_ci return -ENOMEM; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci mutex_init(&wm2000->lock); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci dev_set_drvdata(&i2c->dev, wm2000); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci wm2000->regmap = devm_regmap_init_i2c(i2c, &wm2000_regmap); 82762306a36Sopenharmony_ci if (IS_ERR(wm2000->regmap)) { 82862306a36Sopenharmony_ci ret = PTR_ERR(wm2000->regmap); 82962306a36Sopenharmony_ci dev_err(&i2c->dev, "Failed to allocate register map: %d\n", 83062306a36Sopenharmony_ci ret); 83162306a36Sopenharmony_ci goto out; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci for (i = 0; i < WM2000_NUM_SUPPLIES; i++) 83562306a36Sopenharmony_ci wm2000->supplies[i].supply = wm2000_supplies[i]; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci ret = devm_regulator_bulk_get(&i2c->dev, WM2000_NUM_SUPPLIES, 83862306a36Sopenharmony_ci wm2000->supplies); 83962306a36Sopenharmony_ci if (ret != 0) { 84062306a36Sopenharmony_ci dev_err(&i2c->dev, "Failed to get supplies: %d\n", ret); 84162306a36Sopenharmony_ci return ret; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci ret = regulator_bulk_enable(WM2000_NUM_SUPPLIES, wm2000->supplies); 84562306a36Sopenharmony_ci if (ret != 0) { 84662306a36Sopenharmony_ci dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); 84762306a36Sopenharmony_ci return ret; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* Verify that this is a WM2000 */ 85162306a36Sopenharmony_ci ret = regmap_read(wm2000->regmap, WM2000_REG_ID1, ®); 85262306a36Sopenharmony_ci if (ret != 0) { 85362306a36Sopenharmony_ci dev_err(&i2c->dev, "Unable to read ID1: %d\n", ret); 85462306a36Sopenharmony_ci return ret; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci id = reg << 8; 85762306a36Sopenharmony_ci ret = regmap_read(wm2000->regmap, WM2000_REG_ID2, ®); 85862306a36Sopenharmony_ci if (ret != 0) { 85962306a36Sopenharmony_ci dev_err(&i2c->dev, "Unable to read ID2: %d\n", ret); 86062306a36Sopenharmony_ci return ret; 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci id |= reg & 0xff; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (id != 0x2000) { 86562306a36Sopenharmony_ci dev_err(&i2c->dev, "Device is not a WM2000 - ID %x\n", id); 86662306a36Sopenharmony_ci ret = -ENODEV; 86762306a36Sopenharmony_ci goto err_supplies; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci ret = regmap_read(wm2000->regmap, WM2000_REG_REVISON, ®); 87162306a36Sopenharmony_ci if (ret != 0) { 87262306a36Sopenharmony_ci dev_err(&i2c->dev, "Unable to read Revision: %d\n", ret); 87362306a36Sopenharmony_ci return ret; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci dev_info(&i2c->dev, "revision %c\n", reg + 'A'); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci wm2000->mclk = devm_clk_get(&i2c->dev, "MCLK"); 87862306a36Sopenharmony_ci if (IS_ERR(wm2000->mclk)) { 87962306a36Sopenharmony_ci ret = PTR_ERR(wm2000->mclk); 88062306a36Sopenharmony_ci dev_err(&i2c->dev, "Failed to get MCLK: %d\n", ret); 88162306a36Sopenharmony_ci goto err_supplies; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci filename = "wm2000_anc.bin"; 88562306a36Sopenharmony_ci pdata = dev_get_platdata(&i2c->dev); 88662306a36Sopenharmony_ci if (pdata) { 88762306a36Sopenharmony_ci wm2000->speech_clarity = !pdata->speech_enh_disable; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci if (pdata->download_file) 89062306a36Sopenharmony_ci filename = pdata->download_file; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci ret = request_firmware(&fw, filename, &i2c->dev); 89462306a36Sopenharmony_ci if (ret != 0) { 89562306a36Sopenharmony_ci dev_err(&i2c->dev, "Failed to acquire ANC data: %d\n", ret); 89662306a36Sopenharmony_ci goto err_supplies; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci /* Pre-cook the concatenation of the register address onto the image */ 90062306a36Sopenharmony_ci wm2000->anc_download_size = fw->size + 2; 90162306a36Sopenharmony_ci wm2000->anc_download = devm_kzalloc(&i2c->dev, 90262306a36Sopenharmony_ci wm2000->anc_download_size, 90362306a36Sopenharmony_ci GFP_KERNEL); 90462306a36Sopenharmony_ci if (wm2000->anc_download == NULL) { 90562306a36Sopenharmony_ci ret = -ENOMEM; 90662306a36Sopenharmony_ci goto err_supplies; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci wm2000->anc_download[0] = 0x80; 91062306a36Sopenharmony_ci wm2000->anc_download[1] = 0x00; 91162306a36Sopenharmony_ci memcpy(wm2000->anc_download + 2, fw->data, fw->size); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci wm2000->anc_eng_ena = 1; 91462306a36Sopenharmony_ci wm2000->anc_active = 1; 91562306a36Sopenharmony_ci wm2000->spk_ena = 1; 91662306a36Sopenharmony_ci wm2000->i2c = i2c; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci wm2000_reset(wm2000); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&i2c->dev, 92162306a36Sopenharmony_ci &soc_component_dev_wm2000, NULL, 0); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_cierr_supplies: 92462306a36Sopenharmony_ci regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ciout: 92762306a36Sopenharmony_ci release_firmware(fw); 92862306a36Sopenharmony_ci return ret; 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic const struct i2c_device_id wm2000_i2c_id[] = { 93262306a36Sopenharmony_ci { "wm2000", 0 }, 93362306a36Sopenharmony_ci { } 93462306a36Sopenharmony_ci}; 93562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, wm2000_i2c_id); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_cistatic struct i2c_driver wm2000_i2c_driver = { 93862306a36Sopenharmony_ci .driver = { 93962306a36Sopenharmony_ci .name = "wm2000", 94062306a36Sopenharmony_ci }, 94162306a36Sopenharmony_ci .probe = wm2000_i2c_probe, 94262306a36Sopenharmony_ci .id_table = wm2000_i2c_id, 94362306a36Sopenharmony_ci}; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_cimodule_i2c_driver(wm2000_i2c_driver); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC WM2000 driver"); 94862306a36Sopenharmony_ciMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfonmicro.com>"); 94962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 950