18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * wm2000.c -- WM2000 ALSA Soc Audio driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2008-2011 Wolfson Microelectronics PLC. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * The download image for the WM2000 will be requested as 108c2ecf20Sopenharmony_ci * 'wm2000_anc.bin' by default (overridable via platform data) at 118c2ecf20Sopenharmony_ci * runtime and is expected to be in flat binary format. This is 128c2ecf20Sopenharmony_ci * generated by Wolfson configuration tools and includes 138c2ecf20Sopenharmony_ci * system-specific calibration information. If supplied as a 148c2ecf20Sopenharmony_ci * sequence of ASCII-encoded hexidecimal bytes this can be converted 158c2ecf20Sopenharmony_ci * into a flat binary with a command such as this on the command line: 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * perl -e 'while (<>) { s/[\r\n]+// ; printf("%c", hex($_)); }' 188c2ecf20Sopenharmony_ci * < file > wm2000_anc.bin 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 238c2ecf20Sopenharmony_ci#include <linux/kernel.h> 248c2ecf20Sopenharmony_ci#include <linux/init.h> 258c2ecf20Sopenharmony_ci#include <linux/firmware.h> 268c2ecf20Sopenharmony_ci#include <linux/clk.h> 278c2ecf20Sopenharmony_ci#include <linux/delay.h> 288c2ecf20Sopenharmony_ci#include <linux/pm.h> 298c2ecf20Sopenharmony_ci#include <linux/i2c.h> 308c2ecf20Sopenharmony_ci#include <linux/regmap.h> 318c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 328c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 338c2ecf20Sopenharmony_ci#include <linux/slab.h> 348c2ecf20Sopenharmony_ci#include <sound/core.h> 358c2ecf20Sopenharmony_ci#include <sound/pcm.h> 368c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 378c2ecf20Sopenharmony_ci#include <sound/soc.h> 388c2ecf20Sopenharmony_ci#include <sound/initval.h> 398c2ecf20Sopenharmony_ci#include <sound/tlv.h> 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#include <sound/wm2000.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#include "wm2000.h" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define WM2000_NUM_SUPPLIES 3 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic const char *wm2000_supplies[WM2000_NUM_SUPPLIES] = { 488c2ecf20Sopenharmony_ci "SPKVDD", 498c2ecf20Sopenharmony_ci "DBVDD", 508c2ecf20Sopenharmony_ci "DCVDD", 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cienum wm2000_anc_mode { 548c2ecf20Sopenharmony_ci ANC_ACTIVE = 0, 558c2ecf20Sopenharmony_ci ANC_BYPASS = 1, 568c2ecf20Sopenharmony_ci ANC_STANDBY = 2, 578c2ecf20Sopenharmony_ci ANC_OFF = 3, 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct wm2000_priv { 618c2ecf20Sopenharmony_ci struct i2c_client *i2c; 628c2ecf20Sopenharmony_ci struct regmap *regmap; 638c2ecf20Sopenharmony_ci struct clk *mclk; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci struct regulator_bulk_data supplies[WM2000_NUM_SUPPLIES]; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci enum wm2000_anc_mode anc_mode; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci unsigned int anc_active:1; 708c2ecf20Sopenharmony_ci unsigned int anc_eng_ena:1; 718c2ecf20Sopenharmony_ci unsigned int spk_ena:1; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci unsigned int speech_clarity:1; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci int anc_download_size; 768c2ecf20Sopenharmony_ci char *anc_download; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci struct mutex lock; 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int wm2000_write(struct i2c_client *i2c, unsigned int reg, 828c2ecf20Sopenharmony_ci unsigned int value) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = i2c_get_clientdata(i2c); 858c2ecf20Sopenharmony_ci return regmap_write(wm2000->regmap, reg, value); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic void wm2000_reset(struct wm2000_priv *wm2000) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct i2c_client *i2c = wm2000->i2c; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_CLR); 938c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR); 948c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_ID1, 0); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci wm2000->anc_mode = ANC_OFF; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int wm2000_poll_bit(struct i2c_client *i2c, 1008c2ecf20Sopenharmony_ci unsigned int reg, u8 mask) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = i2c_get_clientdata(i2c); 1038c2ecf20Sopenharmony_ci int timeout = 4000; 1048c2ecf20Sopenharmony_ci unsigned int val; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci regmap_read(wm2000->regmap, reg, &val); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci while (!(val & mask) && --timeout) { 1098c2ecf20Sopenharmony_ci msleep(1); 1108c2ecf20Sopenharmony_ci regmap_read(wm2000->regmap, reg, &val); 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (timeout == 0) 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci else 1168c2ecf20Sopenharmony_ci return 1; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int wm2000_power_up(struct i2c_client *i2c, int analogue) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); 1228c2ecf20Sopenharmony_ci unsigned long rate; 1238c2ecf20Sopenharmony_ci unsigned int val; 1248c2ecf20Sopenharmony_ci int ret; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (WARN_ON(wm2000->anc_mode != ANC_OFF)) 1278c2ecf20Sopenharmony_ci return -EINVAL; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "Beginning power up\n"); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(WM2000_NUM_SUPPLIES, wm2000->supplies); 1328c2ecf20Sopenharmony_ci if (ret != 0) { 1338c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); 1348c2ecf20Sopenharmony_ci return ret; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci rate = clk_get_rate(wm2000->mclk); 1388c2ecf20Sopenharmony_ci if (rate <= 13500000) { 1398c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "Disabling MCLK divider\n"); 1408c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, 1418c2ecf20Sopenharmony_ci WM2000_MCLK_DIV2_ENA_CLR); 1428c2ecf20Sopenharmony_ci } else { 1438c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "Enabling MCLK divider\n"); 1448c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, 1458c2ecf20Sopenharmony_ci WM2000_MCLK_DIV2_ENA_SET); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_CLR); 1498c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_SET); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Wait for ANC engine to become ready */ 1528c2ecf20Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT, 1538c2ecf20Sopenharmony_ci WM2000_ANC_ENG_IDLE)) { 1548c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "ANC engine failed to reset\n"); 1558c2ecf20Sopenharmony_ci regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); 1568c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, 1608c2ecf20Sopenharmony_ci WM2000_STATUS_BOOT_COMPLETE)) { 1618c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "ANC engine failed to initialise\n"); 1628c2ecf20Sopenharmony_ci regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); 1638c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* Open code download of the data since it is the only bulk 1698c2ecf20Sopenharmony_ci * write we do. */ 1708c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "Downloading %d bytes\n", 1718c2ecf20Sopenharmony_ci wm2000->anc_download_size - 2); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci ret = i2c_master_send(i2c, wm2000->anc_download, 1748c2ecf20Sopenharmony_ci wm2000->anc_download_size); 1758c2ecf20Sopenharmony_ci if (ret < 0) { 1768c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "i2c_transfer() failed: %d\n", ret); 1778c2ecf20Sopenharmony_ci regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); 1788c2ecf20Sopenharmony_ci return ret; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci if (ret != wm2000->anc_download_size) { 1818c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "i2c_transfer() failed, %d != %d\n", 1828c2ecf20Sopenharmony_ci ret, wm2000->anc_download_size); 1838c2ecf20Sopenharmony_ci regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); 1848c2ecf20Sopenharmony_ci return -EIO; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "Download complete\n"); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (analogue) { 1908c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_ANA_VMID_PU_TIME, 248 / 4); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 1938c2ecf20Sopenharmony_ci WM2000_MODE_ANA_SEQ_INCLUDE | 1948c2ecf20Sopenharmony_ci WM2000_MODE_MOUSE_ENABLE | 1958c2ecf20Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE); 1968c2ecf20Sopenharmony_ci } else { 1978c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 1988c2ecf20Sopenharmony_ci WM2000_MODE_MOUSE_ENABLE | 1998c2ecf20Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE); 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci ret = regmap_read(wm2000->regmap, WM2000_REG_SPEECH_CLARITY, &val); 2038c2ecf20Sopenharmony_ci if (ret != 0) { 2048c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Unable to read Speech Clarity: %d\n", ret); 2058c2ecf20Sopenharmony_ci regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); 2068c2ecf20Sopenharmony_ci return ret; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci if (wm2000->speech_clarity) 2098c2ecf20Sopenharmony_ci val |= WM2000_SPEECH_CLARITY; 2108c2ecf20Sopenharmony_ci else 2118c2ecf20Sopenharmony_ci val &= ~WM2000_SPEECH_CLARITY; 2128c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SPEECH_CLARITY, val); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_START0, 0x33); 2158c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_START1, 0x02); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, 2208c2ecf20Sopenharmony_ci WM2000_STATUS_MOUSE_ACTIVE)) { 2218c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Timed out waiting for device\n"); 2228c2ecf20Sopenharmony_ci regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); 2238c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "ANC active\n"); 2278c2ecf20Sopenharmony_ci if (analogue) 2288c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "Analogue active\n"); 2298c2ecf20Sopenharmony_ci wm2000->anc_mode = ANC_ACTIVE; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int wm2000_power_down(struct i2c_client *i2c, int analogue) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (analogue) { 2398c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_ANA_VMID_PD_TIME, 248 / 4); 2408c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 2418c2ecf20Sopenharmony_ci WM2000_MODE_ANA_SEQ_INCLUDE | 2428c2ecf20Sopenharmony_ci WM2000_MODE_POWER_DOWN); 2438c2ecf20Sopenharmony_ci } else { 2448c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 2458c2ecf20Sopenharmony_ci WM2000_MODE_POWER_DOWN); 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, 2498c2ecf20Sopenharmony_ci WM2000_STATUS_POWER_DOWN_COMPLETE)) { 2508c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Timeout waiting for ANC power down\n"); 2518c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT, 2558c2ecf20Sopenharmony_ci WM2000_ANC_ENG_IDLE)) { 2568c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Timeout waiting for ANC engine idle\n"); 2578c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "powered off\n"); 2638c2ecf20Sopenharmony_ci wm2000->anc_mode = ANC_OFF; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int wm2000_enter_bypass(struct i2c_client *i2c, int analogue) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (WARN_ON(wm2000->anc_mode != ANC_ACTIVE)) 2738c2ecf20Sopenharmony_ci return -EINVAL; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (analogue) { 2768c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 2778c2ecf20Sopenharmony_ci WM2000_MODE_ANA_SEQ_INCLUDE | 2788c2ecf20Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE | 2798c2ecf20Sopenharmony_ci WM2000_MODE_BYPASS_ENTRY); 2808c2ecf20Sopenharmony_ci } else { 2818c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 2828c2ecf20Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE | 2838c2ecf20Sopenharmony_ci WM2000_MODE_BYPASS_ENTRY); 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, 2878c2ecf20Sopenharmony_ci WM2000_STATUS_ANC_DISABLED)) { 2888c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Timeout waiting for ANC disable\n"); 2898c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT, 2938c2ecf20Sopenharmony_ci WM2000_ANC_ENG_IDLE)) { 2948c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Timeout waiting for ANC engine idle\n"); 2958c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL1, WM2000_SYS_STBY); 2998c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci wm2000->anc_mode = ANC_BYPASS; 3028c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "bypass enabled\n"); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int wm2000_exit_bypass(struct i2c_client *i2c, int analogue) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (WARN_ON(wm2000->anc_mode != ANC_BYPASS)) 3128c2ecf20Sopenharmony_ci return -EINVAL; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL1, 0); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (analogue) { 3178c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 3188c2ecf20Sopenharmony_ci WM2000_MODE_ANA_SEQ_INCLUDE | 3198c2ecf20Sopenharmony_ci WM2000_MODE_MOUSE_ENABLE | 3208c2ecf20Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE); 3218c2ecf20Sopenharmony_ci } else { 3228c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 3238c2ecf20Sopenharmony_ci WM2000_MODE_MOUSE_ENABLE | 3248c2ecf20Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE); 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET); 3288c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, 3318c2ecf20Sopenharmony_ci WM2000_STATUS_MOUSE_ACTIVE)) { 3328c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Timed out waiting for MOUSE\n"); 3338c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci wm2000->anc_mode = ANC_ACTIVE; 3378c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "MOUSE active\n"); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic int wm2000_enter_standby(struct i2c_client *i2c, int analogue) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (WARN_ON(wm2000->anc_mode != ANC_ACTIVE)) 3478c2ecf20Sopenharmony_ci return -EINVAL; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (analogue) { 3508c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_ANA_VMID_PD_TIME, 248 / 4); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 3538c2ecf20Sopenharmony_ci WM2000_MODE_ANA_SEQ_INCLUDE | 3548c2ecf20Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE | 3558c2ecf20Sopenharmony_ci WM2000_MODE_STANDBY_ENTRY); 3568c2ecf20Sopenharmony_ci } else { 3578c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 3588c2ecf20Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE | 3598c2ecf20Sopenharmony_ci WM2000_MODE_STANDBY_ENTRY); 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, 3638c2ecf20Sopenharmony_ci WM2000_STATUS_ANC_DISABLED)) { 3648c2ecf20Sopenharmony_ci dev_err(&i2c->dev, 3658c2ecf20Sopenharmony_ci "Timed out waiting for ANC disable after 1ms\n"); 3668c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT, WM2000_ANC_ENG_IDLE)) { 3708c2ecf20Sopenharmony_ci dev_err(&i2c->dev, 3718c2ecf20Sopenharmony_ci "Timed out waiting for standby\n"); 3728c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL1, WM2000_SYS_STBY); 3768c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci wm2000->anc_mode = ANC_STANDBY; 3798c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "standby\n"); 3808c2ecf20Sopenharmony_ci if (analogue) 3818c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "Analogue disabled\n"); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic int wm2000_exit_standby(struct i2c_client *i2c, int analogue) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (WARN_ON(wm2000->anc_mode != ANC_STANDBY)) 3918c2ecf20Sopenharmony_ci return -EINVAL; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL1, 0); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (analogue) { 3968c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_ANA_VMID_PU_TIME, 248 / 4); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 3998c2ecf20Sopenharmony_ci WM2000_MODE_ANA_SEQ_INCLUDE | 4008c2ecf20Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE | 4018c2ecf20Sopenharmony_ci WM2000_MODE_MOUSE_ENABLE); 4028c2ecf20Sopenharmony_ci } else { 4038c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, 4048c2ecf20Sopenharmony_ci WM2000_MODE_THERMAL_ENABLE | 4058c2ecf20Sopenharmony_ci WM2000_MODE_MOUSE_ENABLE); 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET); 4098c2ecf20Sopenharmony_ci wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, 4128c2ecf20Sopenharmony_ci WM2000_STATUS_MOUSE_ACTIVE)) { 4138c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Timed out waiting for MOUSE\n"); 4148c2ecf20Sopenharmony_ci return -ETIMEDOUT; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci wm2000->anc_mode = ANC_ACTIVE; 4188c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "MOUSE active\n"); 4198c2ecf20Sopenharmony_ci if (analogue) 4208c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "Analogue enabled\n"); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_citypedef int (*wm2000_mode_fn)(struct i2c_client *i2c, int analogue); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic struct { 4288c2ecf20Sopenharmony_ci enum wm2000_anc_mode source; 4298c2ecf20Sopenharmony_ci enum wm2000_anc_mode dest; 4308c2ecf20Sopenharmony_ci int analogue; 4318c2ecf20Sopenharmony_ci wm2000_mode_fn step[2]; 4328c2ecf20Sopenharmony_ci} anc_transitions[] = { 4338c2ecf20Sopenharmony_ci { 4348c2ecf20Sopenharmony_ci .source = ANC_OFF, 4358c2ecf20Sopenharmony_ci .dest = ANC_ACTIVE, 4368c2ecf20Sopenharmony_ci .analogue = 1, 4378c2ecf20Sopenharmony_ci .step = { 4388c2ecf20Sopenharmony_ci wm2000_power_up, 4398c2ecf20Sopenharmony_ci }, 4408c2ecf20Sopenharmony_ci }, 4418c2ecf20Sopenharmony_ci { 4428c2ecf20Sopenharmony_ci .source = ANC_OFF, 4438c2ecf20Sopenharmony_ci .dest = ANC_STANDBY, 4448c2ecf20Sopenharmony_ci .step = { 4458c2ecf20Sopenharmony_ci wm2000_power_up, 4468c2ecf20Sopenharmony_ci wm2000_enter_standby, 4478c2ecf20Sopenharmony_ci }, 4488c2ecf20Sopenharmony_ci }, 4498c2ecf20Sopenharmony_ci { 4508c2ecf20Sopenharmony_ci .source = ANC_OFF, 4518c2ecf20Sopenharmony_ci .dest = ANC_BYPASS, 4528c2ecf20Sopenharmony_ci .analogue = 1, 4538c2ecf20Sopenharmony_ci .step = { 4548c2ecf20Sopenharmony_ci wm2000_power_up, 4558c2ecf20Sopenharmony_ci wm2000_enter_bypass, 4568c2ecf20Sopenharmony_ci }, 4578c2ecf20Sopenharmony_ci }, 4588c2ecf20Sopenharmony_ci { 4598c2ecf20Sopenharmony_ci .source = ANC_ACTIVE, 4608c2ecf20Sopenharmony_ci .dest = ANC_BYPASS, 4618c2ecf20Sopenharmony_ci .analogue = 1, 4628c2ecf20Sopenharmony_ci .step = { 4638c2ecf20Sopenharmony_ci wm2000_enter_bypass, 4648c2ecf20Sopenharmony_ci }, 4658c2ecf20Sopenharmony_ci }, 4668c2ecf20Sopenharmony_ci { 4678c2ecf20Sopenharmony_ci .source = ANC_ACTIVE, 4688c2ecf20Sopenharmony_ci .dest = ANC_STANDBY, 4698c2ecf20Sopenharmony_ci .analogue = 1, 4708c2ecf20Sopenharmony_ci .step = { 4718c2ecf20Sopenharmony_ci wm2000_enter_standby, 4728c2ecf20Sopenharmony_ci }, 4738c2ecf20Sopenharmony_ci }, 4748c2ecf20Sopenharmony_ci { 4758c2ecf20Sopenharmony_ci .source = ANC_ACTIVE, 4768c2ecf20Sopenharmony_ci .dest = ANC_OFF, 4778c2ecf20Sopenharmony_ci .analogue = 1, 4788c2ecf20Sopenharmony_ci .step = { 4798c2ecf20Sopenharmony_ci wm2000_power_down, 4808c2ecf20Sopenharmony_ci }, 4818c2ecf20Sopenharmony_ci }, 4828c2ecf20Sopenharmony_ci { 4838c2ecf20Sopenharmony_ci .source = ANC_BYPASS, 4848c2ecf20Sopenharmony_ci .dest = ANC_ACTIVE, 4858c2ecf20Sopenharmony_ci .analogue = 1, 4868c2ecf20Sopenharmony_ci .step = { 4878c2ecf20Sopenharmony_ci wm2000_exit_bypass, 4888c2ecf20Sopenharmony_ci }, 4898c2ecf20Sopenharmony_ci }, 4908c2ecf20Sopenharmony_ci { 4918c2ecf20Sopenharmony_ci .source = ANC_BYPASS, 4928c2ecf20Sopenharmony_ci .dest = ANC_STANDBY, 4938c2ecf20Sopenharmony_ci .analogue = 1, 4948c2ecf20Sopenharmony_ci .step = { 4958c2ecf20Sopenharmony_ci wm2000_exit_bypass, 4968c2ecf20Sopenharmony_ci wm2000_enter_standby, 4978c2ecf20Sopenharmony_ci }, 4988c2ecf20Sopenharmony_ci }, 4998c2ecf20Sopenharmony_ci { 5008c2ecf20Sopenharmony_ci .source = ANC_BYPASS, 5018c2ecf20Sopenharmony_ci .dest = ANC_OFF, 5028c2ecf20Sopenharmony_ci .step = { 5038c2ecf20Sopenharmony_ci wm2000_exit_bypass, 5048c2ecf20Sopenharmony_ci wm2000_power_down, 5058c2ecf20Sopenharmony_ci }, 5068c2ecf20Sopenharmony_ci }, 5078c2ecf20Sopenharmony_ci { 5088c2ecf20Sopenharmony_ci .source = ANC_STANDBY, 5098c2ecf20Sopenharmony_ci .dest = ANC_ACTIVE, 5108c2ecf20Sopenharmony_ci .analogue = 1, 5118c2ecf20Sopenharmony_ci .step = { 5128c2ecf20Sopenharmony_ci wm2000_exit_standby, 5138c2ecf20Sopenharmony_ci }, 5148c2ecf20Sopenharmony_ci }, 5158c2ecf20Sopenharmony_ci { 5168c2ecf20Sopenharmony_ci .source = ANC_STANDBY, 5178c2ecf20Sopenharmony_ci .dest = ANC_BYPASS, 5188c2ecf20Sopenharmony_ci .analogue = 1, 5198c2ecf20Sopenharmony_ci .step = { 5208c2ecf20Sopenharmony_ci wm2000_exit_standby, 5218c2ecf20Sopenharmony_ci wm2000_enter_bypass, 5228c2ecf20Sopenharmony_ci }, 5238c2ecf20Sopenharmony_ci }, 5248c2ecf20Sopenharmony_ci { 5258c2ecf20Sopenharmony_ci .source = ANC_STANDBY, 5268c2ecf20Sopenharmony_ci .dest = ANC_OFF, 5278c2ecf20Sopenharmony_ci .step = { 5288c2ecf20Sopenharmony_ci wm2000_exit_standby, 5298c2ecf20Sopenharmony_ci wm2000_power_down, 5308c2ecf20Sopenharmony_ci }, 5318c2ecf20Sopenharmony_ci }, 5328c2ecf20Sopenharmony_ci}; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int wm2000_anc_transition(struct wm2000_priv *wm2000, 5358c2ecf20Sopenharmony_ci enum wm2000_anc_mode mode) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct i2c_client *i2c = wm2000->i2c; 5388c2ecf20Sopenharmony_ci int i, j; 5398c2ecf20Sopenharmony_ci int ret = 0; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (wm2000->anc_mode == mode) 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(anc_transitions); i++) 5458c2ecf20Sopenharmony_ci if (anc_transitions[i].source == wm2000->anc_mode && 5468c2ecf20Sopenharmony_ci anc_transitions[i].dest == mode) 5478c2ecf20Sopenharmony_ci break; 5488c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(anc_transitions)) { 5498c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "No transition for %d->%d\n", 5508c2ecf20Sopenharmony_ci wm2000->anc_mode, mode); 5518c2ecf20Sopenharmony_ci return -EINVAL; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* Maintain clock while active */ 5558c2ecf20Sopenharmony_ci if (anc_transitions[i].source == ANC_OFF) { 5568c2ecf20Sopenharmony_ci ret = clk_prepare_enable(wm2000->mclk); 5578c2ecf20Sopenharmony_ci if (ret != 0) { 5588c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Failed to enable MCLK: %d\n", ret); 5598c2ecf20Sopenharmony_ci return ret; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(anc_transitions[j].step); j++) { 5648c2ecf20Sopenharmony_ci if (!anc_transitions[i].step[j]) 5658c2ecf20Sopenharmony_ci break; 5668c2ecf20Sopenharmony_ci ret = anc_transitions[i].step[j](i2c, 5678c2ecf20Sopenharmony_ci anc_transitions[i].analogue); 5688c2ecf20Sopenharmony_ci if (ret != 0) 5698c2ecf20Sopenharmony_ci break; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (anc_transitions[i].dest == ANC_OFF) 5738c2ecf20Sopenharmony_ci clk_disable_unprepare(wm2000->mclk); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci return ret; 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic int wm2000_anc_set_mode(struct wm2000_priv *wm2000) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci struct i2c_client *i2c = wm2000->i2c; 5818c2ecf20Sopenharmony_ci enum wm2000_anc_mode mode; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (wm2000->anc_eng_ena && wm2000->spk_ena) 5848c2ecf20Sopenharmony_ci if (wm2000->anc_active) 5858c2ecf20Sopenharmony_ci mode = ANC_ACTIVE; 5868c2ecf20Sopenharmony_ci else 5878c2ecf20Sopenharmony_ci mode = ANC_BYPASS; 5888c2ecf20Sopenharmony_ci else 5898c2ecf20Sopenharmony_ci mode = ANC_STANDBY; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "Set mode %d (enabled %d, mute %d, active %d)\n", 5928c2ecf20Sopenharmony_ci mode, wm2000->anc_eng_ena, !wm2000->spk_ena, 5938c2ecf20Sopenharmony_ci wm2000->anc_active); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci return wm2000_anc_transition(wm2000, mode); 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic int wm2000_anc_mode_get(struct snd_kcontrol *kcontrol, 5998c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 6028c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = wm2000->anc_active; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci return 0; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, 6108c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 6138c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 6148c2ecf20Sopenharmony_ci unsigned int anc_active = ucontrol->value.integer.value[0]; 6158c2ecf20Sopenharmony_ci int ret; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (anc_active > 1) 6188c2ecf20Sopenharmony_ci return -EINVAL; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci mutex_lock(&wm2000->lock); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci wm2000->anc_active = anc_active; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci ret = wm2000_anc_set_mode(wm2000); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci mutex_unlock(&wm2000->lock); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci return ret; 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic int wm2000_speaker_get(struct snd_kcontrol *kcontrol, 6328c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 6358c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = wm2000->spk_ena; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci return 0; 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic int wm2000_speaker_put(struct snd_kcontrol *kcontrol, 6438c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 6468c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 6478c2ecf20Sopenharmony_ci unsigned int val = ucontrol->value.integer.value[0]; 6488c2ecf20Sopenharmony_ci int ret; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (val > 1) 6518c2ecf20Sopenharmony_ci return -EINVAL; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci mutex_lock(&wm2000->lock); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci wm2000->spk_ena = val; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci ret = wm2000_anc_set_mode(wm2000); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci mutex_unlock(&wm2000->lock); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci return ret; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm2000_controls[] = { 6658c2ecf20Sopenharmony_ci SOC_SINGLE("ANC Volume", WM2000_REG_ANC_GAIN_CTRL, 0, 255, 0), 6668c2ecf20Sopenharmony_ci SOC_SINGLE_BOOL_EXT("WM2000 ANC Switch", 0, 6678c2ecf20Sopenharmony_ci wm2000_anc_mode_get, 6688c2ecf20Sopenharmony_ci wm2000_anc_mode_put), 6698c2ecf20Sopenharmony_ci SOC_SINGLE_BOOL_EXT("WM2000 Switch", 0, 6708c2ecf20Sopenharmony_ci wm2000_speaker_get, 6718c2ecf20Sopenharmony_ci wm2000_speaker_put), 6728c2ecf20Sopenharmony_ci}; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, 6758c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol, int event) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 6788c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 6798c2ecf20Sopenharmony_ci int ret; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci mutex_lock(&wm2000->lock); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (SND_SOC_DAPM_EVENT_ON(event)) 6848c2ecf20Sopenharmony_ci wm2000->anc_eng_ena = 1; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (SND_SOC_DAPM_EVENT_OFF(event)) 6878c2ecf20Sopenharmony_ci wm2000->anc_eng_ena = 0; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci ret = wm2000_anc_set_mode(wm2000); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci mutex_unlock(&wm2000->lock); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci return ret; 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = { 6978c2ecf20Sopenharmony_ci/* Externally visible pins */ 6988c2ecf20Sopenharmony_ciSND_SOC_DAPM_OUTPUT("SPKN"), 6998c2ecf20Sopenharmony_ciSND_SOC_DAPM_OUTPUT("SPKP"), 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ciSND_SOC_DAPM_INPUT("LINN"), 7028c2ecf20Sopenharmony_ciSND_SOC_DAPM_INPUT("LINP"), 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ciSND_SOC_DAPM_PGA_E("ANC Engine", SND_SOC_NOPM, 0, 0, NULL, 0, 7058c2ecf20Sopenharmony_ci wm2000_anc_power_event, 7068c2ecf20Sopenharmony_ci SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), 7078c2ecf20Sopenharmony_ci}; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci/* Target, Path, Source */ 7108c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route wm2000_audio_map[] = { 7118c2ecf20Sopenharmony_ci { "SPKN", NULL, "ANC Engine" }, 7128c2ecf20Sopenharmony_ci { "SPKP", NULL, "ANC Engine" }, 7138c2ecf20Sopenharmony_ci { "ANC Engine", NULL, "LINN" }, 7148c2ecf20Sopenharmony_ci { "ANC Engine", NULL, "LINP" }, 7158c2ecf20Sopenharmony_ci}; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 7188c2ecf20Sopenharmony_cistatic int wm2000_suspend(struct snd_soc_component *component) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci return wm2000_anc_transition(wm2000, ANC_OFF); 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_cistatic int wm2000_resume(struct snd_soc_component *component) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci return wm2000_anc_set_mode(wm2000); 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci#else 7328c2ecf20Sopenharmony_ci#define wm2000_suspend NULL 7338c2ecf20Sopenharmony_ci#define wm2000_resume NULL 7348c2ecf20Sopenharmony_ci#endif 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic bool wm2000_readable_reg(struct device *dev, unsigned int reg) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci switch (reg) { 7398c2ecf20Sopenharmony_ci case WM2000_REG_SYS_START: 7408c2ecf20Sopenharmony_ci case WM2000_REG_ANC_GAIN_CTRL: 7418c2ecf20Sopenharmony_ci case WM2000_REG_MSE_TH1: 7428c2ecf20Sopenharmony_ci case WM2000_REG_MSE_TH2: 7438c2ecf20Sopenharmony_ci case WM2000_REG_SPEECH_CLARITY: 7448c2ecf20Sopenharmony_ci case WM2000_REG_SYS_WATCHDOG: 7458c2ecf20Sopenharmony_ci case WM2000_REG_ANA_VMID_PD_TIME: 7468c2ecf20Sopenharmony_ci case WM2000_REG_ANA_VMID_PU_TIME: 7478c2ecf20Sopenharmony_ci case WM2000_REG_CAT_FLTR_INDX: 7488c2ecf20Sopenharmony_ci case WM2000_REG_CAT_GAIN_0: 7498c2ecf20Sopenharmony_ci case WM2000_REG_SYS_STATUS: 7508c2ecf20Sopenharmony_ci case WM2000_REG_SYS_MODE_CNTRL: 7518c2ecf20Sopenharmony_ci case WM2000_REG_SYS_START0: 7528c2ecf20Sopenharmony_ci case WM2000_REG_SYS_START1: 7538c2ecf20Sopenharmony_ci case WM2000_REG_ID1: 7548c2ecf20Sopenharmony_ci case WM2000_REG_ID2: 7558c2ecf20Sopenharmony_ci case WM2000_REG_REVISON: 7568c2ecf20Sopenharmony_ci case WM2000_REG_SYS_CTL1: 7578c2ecf20Sopenharmony_ci case WM2000_REG_SYS_CTL2: 7588c2ecf20Sopenharmony_ci case WM2000_REG_ANC_STAT: 7598c2ecf20Sopenharmony_ci case WM2000_REG_IF_CTL: 7608c2ecf20Sopenharmony_ci case WM2000_REG_ANA_MIC_CTL: 7618c2ecf20Sopenharmony_ci case WM2000_REG_SPK_CTL: 7628c2ecf20Sopenharmony_ci return true; 7638c2ecf20Sopenharmony_ci default: 7648c2ecf20Sopenharmony_ci return false; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_cistatic const struct regmap_config wm2000_regmap = { 7698c2ecf20Sopenharmony_ci .reg_bits = 16, 7708c2ecf20Sopenharmony_ci .val_bits = 8, 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci .max_register = WM2000_REG_SPK_CTL, 7738c2ecf20Sopenharmony_ci .readable_reg = wm2000_readable_reg, 7748c2ecf20Sopenharmony_ci}; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_cistatic int wm2000_probe(struct snd_soc_component *component) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci /* This will trigger a transition to standby mode by default */ 7818c2ecf20Sopenharmony_ci wm2000_anc_set_mode(wm2000); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci return 0; 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_cistatic void wm2000_remove(struct snd_soc_component *component) 7878c2ecf20Sopenharmony_ci{ 7888c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci wm2000_anc_transition(wm2000, ANC_OFF); 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_wm2000 = { 7948c2ecf20Sopenharmony_ci .probe = wm2000_probe, 7958c2ecf20Sopenharmony_ci .remove = wm2000_remove, 7968c2ecf20Sopenharmony_ci .suspend = wm2000_suspend, 7978c2ecf20Sopenharmony_ci .resume = wm2000_resume, 7988c2ecf20Sopenharmony_ci .controls = wm2000_controls, 7998c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(wm2000_controls), 8008c2ecf20Sopenharmony_ci .dapm_widgets = wm2000_dapm_widgets, 8018c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(wm2000_dapm_widgets), 8028c2ecf20Sopenharmony_ci .dapm_routes = wm2000_audio_map, 8038c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(wm2000_audio_map), 8048c2ecf20Sopenharmony_ci .idle_bias_on = 1, 8058c2ecf20Sopenharmony_ci .use_pmdown_time = 1, 8068c2ecf20Sopenharmony_ci .endianness = 1, 8078c2ecf20Sopenharmony_ci .non_legacy_dai_naming = 1, 8088c2ecf20Sopenharmony_ci}; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cistatic int wm2000_i2c_probe(struct i2c_client *i2c, 8118c2ecf20Sopenharmony_ci const struct i2c_device_id *i2c_id) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci struct wm2000_priv *wm2000; 8148c2ecf20Sopenharmony_ci struct wm2000_platform_data *pdata; 8158c2ecf20Sopenharmony_ci const char *filename; 8168c2ecf20Sopenharmony_ci const struct firmware *fw = NULL; 8178c2ecf20Sopenharmony_ci int ret, i; 8188c2ecf20Sopenharmony_ci unsigned int reg; 8198c2ecf20Sopenharmony_ci u16 id; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci wm2000 = devm_kzalloc(&i2c->dev, sizeof(*wm2000), GFP_KERNEL); 8228c2ecf20Sopenharmony_ci if (!wm2000) 8238c2ecf20Sopenharmony_ci return -ENOMEM; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci mutex_init(&wm2000->lock); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci dev_set_drvdata(&i2c->dev, wm2000); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci wm2000->regmap = devm_regmap_init_i2c(i2c, &wm2000_regmap); 8308c2ecf20Sopenharmony_ci if (IS_ERR(wm2000->regmap)) { 8318c2ecf20Sopenharmony_ci ret = PTR_ERR(wm2000->regmap); 8328c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Failed to allocate register map: %d\n", 8338c2ecf20Sopenharmony_ci ret); 8348c2ecf20Sopenharmony_ci goto out; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci for (i = 0; i < WM2000_NUM_SUPPLIES; i++) 8388c2ecf20Sopenharmony_ci wm2000->supplies[i].supply = wm2000_supplies[i]; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci ret = devm_regulator_bulk_get(&i2c->dev, WM2000_NUM_SUPPLIES, 8418c2ecf20Sopenharmony_ci wm2000->supplies); 8428c2ecf20Sopenharmony_ci if (ret != 0) { 8438c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Failed to get supplies: %d\n", ret); 8448c2ecf20Sopenharmony_ci return ret; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(WM2000_NUM_SUPPLIES, wm2000->supplies); 8488c2ecf20Sopenharmony_ci if (ret != 0) { 8498c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); 8508c2ecf20Sopenharmony_ci return ret; 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci /* Verify that this is a WM2000 */ 8548c2ecf20Sopenharmony_ci ret = regmap_read(wm2000->regmap, WM2000_REG_ID1, ®); 8558c2ecf20Sopenharmony_ci if (ret != 0) { 8568c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Unable to read ID1: %d\n", ret); 8578c2ecf20Sopenharmony_ci return ret; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci id = reg << 8; 8608c2ecf20Sopenharmony_ci ret = regmap_read(wm2000->regmap, WM2000_REG_ID2, ®); 8618c2ecf20Sopenharmony_ci if (ret != 0) { 8628c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Unable to read ID2: %d\n", ret); 8638c2ecf20Sopenharmony_ci return ret; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci id |= reg & 0xff; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (id != 0x2000) { 8688c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Device is not a WM2000 - ID %x\n", id); 8698c2ecf20Sopenharmony_ci ret = -ENODEV; 8708c2ecf20Sopenharmony_ci goto err_supplies; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci ret = regmap_read(wm2000->regmap, WM2000_REG_REVISON, ®); 8748c2ecf20Sopenharmony_ci if (ret != 0) { 8758c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Unable to read Revision: %d\n", ret); 8768c2ecf20Sopenharmony_ci return ret; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci dev_info(&i2c->dev, "revision %c\n", reg + 'A'); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci wm2000->mclk = devm_clk_get(&i2c->dev, "MCLK"); 8818c2ecf20Sopenharmony_ci if (IS_ERR(wm2000->mclk)) { 8828c2ecf20Sopenharmony_ci ret = PTR_ERR(wm2000->mclk); 8838c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Failed to get MCLK: %d\n", ret); 8848c2ecf20Sopenharmony_ci goto err_supplies; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci filename = "wm2000_anc.bin"; 8888c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&i2c->dev); 8898c2ecf20Sopenharmony_ci if (pdata) { 8908c2ecf20Sopenharmony_ci wm2000->speech_clarity = !pdata->speech_enh_disable; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if (pdata->download_file) 8938c2ecf20Sopenharmony_ci filename = pdata->download_file; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci ret = request_firmware(&fw, filename, &i2c->dev); 8978c2ecf20Sopenharmony_ci if (ret != 0) { 8988c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "Failed to acquire ANC data: %d\n", ret); 8998c2ecf20Sopenharmony_ci goto err_supplies; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci /* Pre-cook the concatenation of the register address onto the image */ 9038c2ecf20Sopenharmony_ci wm2000->anc_download_size = fw->size + 2; 9048c2ecf20Sopenharmony_ci wm2000->anc_download = devm_kzalloc(&i2c->dev, 9058c2ecf20Sopenharmony_ci wm2000->anc_download_size, 9068c2ecf20Sopenharmony_ci GFP_KERNEL); 9078c2ecf20Sopenharmony_ci if (wm2000->anc_download == NULL) { 9088c2ecf20Sopenharmony_ci ret = -ENOMEM; 9098c2ecf20Sopenharmony_ci goto err_supplies; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci wm2000->anc_download[0] = 0x80; 9138c2ecf20Sopenharmony_ci wm2000->anc_download[1] = 0x00; 9148c2ecf20Sopenharmony_ci memcpy(wm2000->anc_download + 2, fw->data, fw->size); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci wm2000->anc_eng_ena = 1; 9178c2ecf20Sopenharmony_ci wm2000->anc_active = 1; 9188c2ecf20Sopenharmony_ci wm2000->spk_ena = 1; 9198c2ecf20Sopenharmony_ci wm2000->i2c = i2c; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci wm2000_reset(wm2000); 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&i2c->dev, 9248c2ecf20Sopenharmony_ci &soc_component_dev_wm2000, NULL, 0); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_cierr_supplies: 9278c2ecf20Sopenharmony_ci regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ciout: 9308c2ecf20Sopenharmony_ci release_firmware(fw); 9318c2ecf20Sopenharmony_ci return ret; 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_cistatic const struct i2c_device_id wm2000_i2c_id[] = { 9358c2ecf20Sopenharmony_ci { "wm2000", 0 }, 9368c2ecf20Sopenharmony_ci { } 9378c2ecf20Sopenharmony_ci}; 9388c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, wm2000_i2c_id); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_cistatic struct i2c_driver wm2000_i2c_driver = { 9418c2ecf20Sopenharmony_ci .driver = { 9428c2ecf20Sopenharmony_ci .name = "wm2000", 9438c2ecf20Sopenharmony_ci }, 9448c2ecf20Sopenharmony_ci .probe = wm2000_i2c_probe, 9458c2ecf20Sopenharmony_ci .id_table = wm2000_i2c_id, 9468c2ecf20Sopenharmony_ci}; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cimodule_i2c_driver(wm2000_i2c_driver); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ASoC WM2000 driver"); 9518c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfonmicro.com>"); 9528c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 953