18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * wm8711.c -- WM8711 ALSA SoC Audio driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2006 Wolfson Microelectronics 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Mike Arthur <Mike.Arthur@wolfsonmicro.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on wm8731.c by Richard Purdie 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/pm.h> 178c2ecf20Sopenharmony_ci#include <linux/i2c.h> 188c2ecf20Sopenharmony_ci#include <linux/regmap.h> 198c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/of_device.h> 228c2ecf20Sopenharmony_ci#include <sound/core.h> 238c2ecf20Sopenharmony_ci#include <sound/pcm.h> 248c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 258c2ecf20Sopenharmony_ci#include <sound/soc.h> 268c2ecf20Sopenharmony_ci#include <sound/tlv.h> 278c2ecf20Sopenharmony_ci#include <sound/initval.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "wm8711.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* codec private data */ 328c2ecf20Sopenharmony_cistruct wm8711_priv { 338c2ecf20Sopenharmony_ci struct regmap *regmap; 348c2ecf20Sopenharmony_ci unsigned int sysclk; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * wm8711 register cache 398c2ecf20Sopenharmony_ci * We can't read the WM8711 register space when we are 408c2ecf20Sopenharmony_ci * using 2 wire for device control, so we cache them instead. 418c2ecf20Sopenharmony_ci * There is no point in caching the reset register 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_cistatic const struct reg_default wm8711_reg_defaults[] = { 448c2ecf20Sopenharmony_ci { 0, 0x0079 }, { 1, 0x0079 }, { 2, 0x000a }, { 3, 0x0008 }, 458c2ecf20Sopenharmony_ci { 4, 0x009f }, { 5, 0x000a }, { 6, 0x0000 }, { 7, 0x0000 }, 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic bool wm8711_volatile(struct device *dev, unsigned int reg) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci switch (reg) { 518c2ecf20Sopenharmony_ci case WM8711_RESET: 528c2ecf20Sopenharmony_ci return true; 538c2ecf20Sopenharmony_ci default: 548c2ecf20Sopenharmony_ci return false; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define wm8711_reset(c) snd_soc_component_write(c, WM8711_RESET, 0) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm8711_snd_controls[] = { 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ciSOC_DOUBLE_R_TLV("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V, 658c2ecf20Sopenharmony_ci 0, 127, 0, out_tlv), 668c2ecf20Sopenharmony_ciSOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V, 678c2ecf20Sopenharmony_ci 7, 1, 0), 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* Output Mixer */ 728c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm8711_output_mixer_controls[] = { 738c2ecf20Sopenharmony_ciSOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0), 748c2ecf20Sopenharmony_ciSOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0), 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = { 788c2ecf20Sopenharmony_ciSND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1, 798c2ecf20Sopenharmony_ci &wm8711_output_mixer_controls[0], 808c2ecf20Sopenharmony_ci ARRAY_SIZE(wm8711_output_mixer_controls)), 818c2ecf20Sopenharmony_ciSND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1), 828c2ecf20Sopenharmony_ciSND_SOC_DAPM_OUTPUT("LOUT"), 838c2ecf20Sopenharmony_ciSND_SOC_DAPM_OUTPUT("LHPOUT"), 848c2ecf20Sopenharmony_ciSND_SOC_DAPM_OUTPUT("ROUT"), 858c2ecf20Sopenharmony_ciSND_SOC_DAPM_OUTPUT("RHPOUT"), 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route wm8711_intercon[] = { 898c2ecf20Sopenharmony_ci /* output mixer */ 908c2ecf20Sopenharmony_ci {"Output Mixer", "Line Bypass Switch", "Line Input"}, 918c2ecf20Sopenharmony_ci {"Output Mixer", "HiFi Playback Switch", "DAC"}, 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* outputs */ 948c2ecf20Sopenharmony_ci {"RHPOUT", NULL, "Output Mixer"}, 958c2ecf20Sopenharmony_ci {"ROUT", NULL, "Output Mixer"}, 968c2ecf20Sopenharmony_ci {"LHPOUT", NULL, "Output Mixer"}, 978c2ecf20Sopenharmony_ci {"LOUT", NULL, "Output Mixer"}, 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistruct _coeff_div { 1018c2ecf20Sopenharmony_ci u32 mclk; 1028c2ecf20Sopenharmony_ci u32 rate; 1038c2ecf20Sopenharmony_ci u16 fs; 1048c2ecf20Sopenharmony_ci u8 sr:4; 1058c2ecf20Sopenharmony_ci u8 bosr:1; 1068c2ecf20Sopenharmony_ci u8 usb:1; 1078c2ecf20Sopenharmony_ci}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* codec mclk clock divider coefficients */ 1108c2ecf20Sopenharmony_cistatic const struct _coeff_div coeff_div[] = { 1118c2ecf20Sopenharmony_ci /* 48k */ 1128c2ecf20Sopenharmony_ci {12288000, 48000, 256, 0x0, 0x0, 0x0}, 1138c2ecf20Sopenharmony_ci {18432000, 48000, 384, 0x0, 0x1, 0x0}, 1148c2ecf20Sopenharmony_ci {12000000, 48000, 250, 0x0, 0x0, 0x1}, 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* 32k */ 1178c2ecf20Sopenharmony_ci {12288000, 32000, 384, 0x6, 0x0, 0x0}, 1188c2ecf20Sopenharmony_ci {18432000, 32000, 576, 0x6, 0x1, 0x0}, 1198c2ecf20Sopenharmony_ci {12000000, 32000, 375, 0x6, 0x0, 0x1}, 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* 8k */ 1228c2ecf20Sopenharmony_ci {12288000, 8000, 1536, 0x3, 0x0, 0x0}, 1238c2ecf20Sopenharmony_ci {18432000, 8000, 2304, 0x3, 0x1, 0x0}, 1248c2ecf20Sopenharmony_ci {11289600, 8000, 1408, 0xb, 0x0, 0x0}, 1258c2ecf20Sopenharmony_ci {16934400, 8000, 2112, 0xb, 0x1, 0x0}, 1268c2ecf20Sopenharmony_ci {12000000, 8000, 1500, 0x3, 0x0, 0x1}, 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* 96k */ 1298c2ecf20Sopenharmony_ci {12288000, 96000, 128, 0x7, 0x0, 0x0}, 1308c2ecf20Sopenharmony_ci {18432000, 96000, 192, 0x7, 0x1, 0x0}, 1318c2ecf20Sopenharmony_ci {12000000, 96000, 125, 0x7, 0x0, 0x1}, 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* 44.1k */ 1348c2ecf20Sopenharmony_ci {11289600, 44100, 256, 0x8, 0x0, 0x0}, 1358c2ecf20Sopenharmony_ci {16934400, 44100, 384, 0x8, 0x1, 0x0}, 1368c2ecf20Sopenharmony_ci {12000000, 44100, 272, 0x8, 0x1, 0x1}, 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* 88.2k */ 1398c2ecf20Sopenharmony_ci {11289600, 88200, 128, 0xf, 0x0, 0x0}, 1408c2ecf20Sopenharmony_ci {16934400, 88200, 192, 0xf, 0x1, 0x0}, 1418c2ecf20Sopenharmony_ci {12000000, 88200, 136, 0xf, 0x1, 0x1}, 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic inline int get_coeff(int mclk, int rate) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci int i; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { 1498c2ecf20Sopenharmony_ci if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) 1508c2ecf20Sopenharmony_ci return i; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int wm8711_hw_params(struct snd_pcm_substream *substream, 1568c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 1578c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 1608c2ecf20Sopenharmony_ci struct wm8711_priv *wm8711 = snd_soc_component_get_drvdata(component); 1618c2ecf20Sopenharmony_ci u16 iface = snd_soc_component_read(component, WM8711_IFACE) & 0xfff3; 1628c2ecf20Sopenharmony_ci int i = get_coeff(wm8711->sysclk, params_rate(params)); 1638c2ecf20Sopenharmony_ci u16 srate = (coeff_div[i].sr << 2) | 1648c2ecf20Sopenharmony_ci (coeff_div[i].bosr << 1) | coeff_div[i].usb; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci snd_soc_component_write(component, WM8711_SRATE, srate); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* bit size */ 1698c2ecf20Sopenharmony_ci switch (params_width(params)) { 1708c2ecf20Sopenharmony_ci case 16: 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci case 20: 1738c2ecf20Sopenharmony_ci iface |= 0x0004; 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci case 24: 1768c2ecf20Sopenharmony_ci iface |= 0x0008; 1778c2ecf20Sopenharmony_ci break; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci snd_soc_component_write(component, WM8711_IFACE, iface); 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int wm8711_pcm_prepare(struct snd_pcm_substream *substream, 1858c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* set active */ 1908c2ecf20Sopenharmony_ci snd_soc_component_write(component, WM8711_ACTIVE, 0x0001); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void wm8711_shutdown(struct snd_pcm_substream *substream, 1968c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* deactivate */ 2018c2ecf20Sopenharmony_ci if (!snd_soc_component_active(component)) { 2028c2ecf20Sopenharmony_ci udelay(50); 2038c2ecf20Sopenharmony_ci snd_soc_component_write(component, WM8711_ACTIVE, 0x0); 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int wm8711_mute(struct snd_soc_dai *dai, int mute, int direction) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 2108c2ecf20Sopenharmony_ci u16 mute_reg = snd_soc_component_read(component, WM8711_APDIGI) & 0xfff7; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (mute) 2138c2ecf20Sopenharmony_ci snd_soc_component_write(component, WM8711_APDIGI, mute_reg | 0x8); 2148c2ecf20Sopenharmony_ci else 2158c2ecf20Sopenharmony_ci snd_soc_component_write(component, WM8711_APDIGI, mute_reg); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int wm8711_set_dai_sysclk(struct snd_soc_dai *codec_dai, 2218c2ecf20Sopenharmony_ci int clk_id, unsigned int freq, int dir) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 2248c2ecf20Sopenharmony_ci struct wm8711_priv *wm8711 = snd_soc_component_get_drvdata(component); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci switch (freq) { 2278c2ecf20Sopenharmony_ci case 11289600: 2288c2ecf20Sopenharmony_ci case 12000000: 2298c2ecf20Sopenharmony_ci case 12288000: 2308c2ecf20Sopenharmony_ci case 16934400: 2318c2ecf20Sopenharmony_ci case 18432000: 2328c2ecf20Sopenharmony_ci wm8711->sysclk = freq; 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci return -EINVAL; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai, 2398c2ecf20Sopenharmony_ci unsigned int fmt) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 2428c2ecf20Sopenharmony_ci u16 iface = snd_soc_component_read(component, WM8711_IFACE) & 0x000c; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* set master/slave audio interface */ 2458c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 2468c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 2478c2ecf20Sopenharmony_ci iface |= 0x0040; 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci default: 2528c2ecf20Sopenharmony_ci return -EINVAL; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* interface format */ 2568c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 2578c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 2588c2ecf20Sopenharmony_ci iface |= 0x0002; 2598c2ecf20Sopenharmony_ci break; 2608c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 2618c2ecf20Sopenharmony_ci break; 2628c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 2638c2ecf20Sopenharmony_ci iface |= 0x0001; 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 2668c2ecf20Sopenharmony_ci iface |= 0x0003; 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 2698c2ecf20Sopenharmony_ci iface |= 0x0013; 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci default: 2728c2ecf20Sopenharmony_ci return -EINVAL; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* clock inversion */ 2768c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 2778c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 2808c2ecf20Sopenharmony_ci iface |= 0x0090; 2818c2ecf20Sopenharmony_ci break; 2828c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 2838c2ecf20Sopenharmony_ci iface |= 0x0080; 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 2868c2ecf20Sopenharmony_ci iface |= 0x0010; 2878c2ecf20Sopenharmony_ci break; 2888c2ecf20Sopenharmony_ci default: 2898c2ecf20Sopenharmony_ci return -EINVAL; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* set iface */ 2938c2ecf20Sopenharmony_ci snd_soc_component_write(component, WM8711_IFACE, iface); 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int wm8711_set_bias_level(struct snd_soc_component *component, 2988c2ecf20Sopenharmony_ci enum snd_soc_bias_level level) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct wm8711_priv *wm8711 = snd_soc_component_get_drvdata(component); 3018c2ecf20Sopenharmony_ci u16 reg = snd_soc_component_read(component, WM8711_PWR) & 0xff7f; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci switch (level) { 3048c2ecf20Sopenharmony_ci case SND_SOC_BIAS_ON: 3058c2ecf20Sopenharmony_ci snd_soc_component_write(component, WM8711_PWR, reg); 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 3108c2ecf20Sopenharmony_ci if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) 3118c2ecf20Sopenharmony_ci regcache_sync(wm8711->regmap); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci snd_soc_component_write(component, WM8711_PWR, reg | 0x0040); 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci case SND_SOC_BIAS_OFF: 3168c2ecf20Sopenharmony_ci snd_soc_component_write(component, WM8711_ACTIVE, 0x0); 3178c2ecf20Sopenharmony_ci snd_soc_component_write(component, WM8711_PWR, 0xffff); 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci#define WM8711_RATES SNDRV_PCM_RATE_8000_96000 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci#define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 3268c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE) 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops wm8711_ops = { 3298c2ecf20Sopenharmony_ci .prepare = wm8711_pcm_prepare, 3308c2ecf20Sopenharmony_ci .hw_params = wm8711_hw_params, 3318c2ecf20Sopenharmony_ci .shutdown = wm8711_shutdown, 3328c2ecf20Sopenharmony_ci .mute_stream = wm8711_mute, 3338c2ecf20Sopenharmony_ci .set_sysclk = wm8711_set_dai_sysclk, 3348c2ecf20Sopenharmony_ci .set_fmt = wm8711_set_dai_fmt, 3358c2ecf20Sopenharmony_ci .no_capture_mute = 1, 3368c2ecf20Sopenharmony_ci}; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver wm8711_dai = { 3398c2ecf20Sopenharmony_ci .name = "wm8711-hifi", 3408c2ecf20Sopenharmony_ci .playback = { 3418c2ecf20Sopenharmony_ci .stream_name = "Playback", 3428c2ecf20Sopenharmony_ci .channels_min = 1, 3438c2ecf20Sopenharmony_ci .channels_max = 2, 3448c2ecf20Sopenharmony_ci .rates = WM8711_RATES, 3458c2ecf20Sopenharmony_ci .formats = WM8711_FORMATS, 3468c2ecf20Sopenharmony_ci }, 3478c2ecf20Sopenharmony_ci .ops = &wm8711_ops, 3488c2ecf20Sopenharmony_ci}; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic int wm8711_probe(struct snd_soc_component *component) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci int ret; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci ret = wm8711_reset(component); 3558c2ecf20Sopenharmony_ci if (ret < 0) { 3568c2ecf20Sopenharmony_ci dev_err(component->dev, "Failed to issue reset\n"); 3578c2ecf20Sopenharmony_ci return ret; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* Latch the update bits */ 3618c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, WM8711_LOUT1V, 0x0100, 0x0100); 3628c2ecf20Sopenharmony_ci snd_soc_component_update_bits(component, WM8711_ROUT1V, 0x0100, 0x0100); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return ret; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_wm8711 = { 3698c2ecf20Sopenharmony_ci .probe = wm8711_probe, 3708c2ecf20Sopenharmony_ci .set_bias_level = wm8711_set_bias_level, 3718c2ecf20Sopenharmony_ci .controls = wm8711_snd_controls, 3728c2ecf20Sopenharmony_ci .num_controls = ARRAY_SIZE(wm8711_snd_controls), 3738c2ecf20Sopenharmony_ci .dapm_widgets = wm8711_dapm_widgets, 3748c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(wm8711_dapm_widgets), 3758c2ecf20Sopenharmony_ci .dapm_routes = wm8711_intercon, 3768c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(wm8711_intercon), 3778c2ecf20Sopenharmony_ci .suspend_bias_off = 1, 3788c2ecf20Sopenharmony_ci .idle_bias_on = 1, 3798c2ecf20Sopenharmony_ci .use_pmdown_time = 1, 3808c2ecf20Sopenharmony_ci .endianness = 1, 3818c2ecf20Sopenharmony_ci .non_legacy_dai_naming = 1, 3828c2ecf20Sopenharmony_ci}; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic const struct of_device_id wm8711_of_match[] = { 3858c2ecf20Sopenharmony_ci { .compatible = "wlf,wm8711", }, 3868c2ecf20Sopenharmony_ci { } 3878c2ecf20Sopenharmony_ci}; 3888c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, wm8711_of_match); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic const struct regmap_config wm8711_regmap = { 3918c2ecf20Sopenharmony_ci .reg_bits = 7, 3928c2ecf20Sopenharmony_ci .val_bits = 9, 3938c2ecf20Sopenharmony_ci .max_register = WM8711_RESET, 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci .reg_defaults = wm8711_reg_defaults, 3968c2ecf20Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(wm8711_reg_defaults), 3978c2ecf20Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci .volatile_reg = wm8711_volatile, 4008c2ecf20Sopenharmony_ci}; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci#if defined(CONFIG_SPI_MASTER) 4038c2ecf20Sopenharmony_cistatic int wm8711_spi_probe(struct spi_device *spi) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct wm8711_priv *wm8711; 4068c2ecf20Sopenharmony_ci int ret; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci wm8711 = devm_kzalloc(&spi->dev, sizeof(struct wm8711_priv), 4098c2ecf20Sopenharmony_ci GFP_KERNEL); 4108c2ecf20Sopenharmony_ci if (wm8711 == NULL) 4118c2ecf20Sopenharmony_ci return -ENOMEM; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci wm8711->regmap = devm_regmap_init_spi(spi, &wm8711_regmap); 4148c2ecf20Sopenharmony_ci if (IS_ERR(wm8711->regmap)) 4158c2ecf20Sopenharmony_ci return PTR_ERR(wm8711->regmap); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci spi_set_drvdata(spi, wm8711); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&spi->dev, 4208c2ecf20Sopenharmony_ci &soc_component_dev_wm8711, &wm8711_dai, 1); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return ret; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic struct spi_driver wm8711_spi_driver = { 4268c2ecf20Sopenharmony_ci .driver = { 4278c2ecf20Sopenharmony_ci .name = "wm8711", 4288c2ecf20Sopenharmony_ci .of_match_table = wm8711_of_match, 4298c2ecf20Sopenharmony_ci }, 4308c2ecf20Sopenharmony_ci .probe = wm8711_spi_probe, 4318c2ecf20Sopenharmony_ci}; 4328c2ecf20Sopenharmony_ci#endif /* CONFIG_SPI_MASTER */ 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C) 4358c2ecf20Sopenharmony_cistatic int wm8711_i2c_probe(struct i2c_client *client, 4368c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct wm8711_priv *wm8711; 4398c2ecf20Sopenharmony_ci int ret; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci wm8711 = devm_kzalloc(&client->dev, sizeof(struct wm8711_priv), 4428c2ecf20Sopenharmony_ci GFP_KERNEL); 4438c2ecf20Sopenharmony_ci if (wm8711 == NULL) 4448c2ecf20Sopenharmony_ci return -ENOMEM; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci wm8711->regmap = devm_regmap_init_i2c(client, &wm8711_regmap); 4478c2ecf20Sopenharmony_ci if (IS_ERR(wm8711->regmap)) 4488c2ecf20Sopenharmony_ci return PTR_ERR(wm8711->regmap); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci i2c_set_clientdata(client, wm8711); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&client->dev, 4538c2ecf20Sopenharmony_ci &soc_component_dev_wm8711, &wm8711_dai, 1); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci return ret; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic const struct i2c_device_id wm8711_i2c_id[] = { 4598c2ecf20Sopenharmony_ci { "wm8711", 0 }, 4608c2ecf20Sopenharmony_ci { } 4618c2ecf20Sopenharmony_ci}; 4628c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, wm8711_i2c_id); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic struct i2c_driver wm8711_i2c_driver = { 4658c2ecf20Sopenharmony_ci .driver = { 4668c2ecf20Sopenharmony_ci .name = "wm8711", 4678c2ecf20Sopenharmony_ci .of_match_table = wm8711_of_match, 4688c2ecf20Sopenharmony_ci }, 4698c2ecf20Sopenharmony_ci .probe = wm8711_i2c_probe, 4708c2ecf20Sopenharmony_ci .id_table = wm8711_i2c_id, 4718c2ecf20Sopenharmony_ci}; 4728c2ecf20Sopenharmony_ci#endif 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic int __init wm8711_modinit(void) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci int ret; 4778c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C) 4788c2ecf20Sopenharmony_ci ret = i2c_add_driver(&wm8711_i2c_driver); 4798c2ecf20Sopenharmony_ci if (ret != 0) { 4808c2ecf20Sopenharmony_ci printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n", 4818c2ecf20Sopenharmony_ci ret); 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci#endif 4848c2ecf20Sopenharmony_ci#if defined(CONFIG_SPI_MASTER) 4858c2ecf20Sopenharmony_ci ret = spi_register_driver(&wm8711_spi_driver); 4868c2ecf20Sopenharmony_ci if (ret != 0) { 4878c2ecf20Sopenharmony_ci printk(KERN_ERR "Failed to register WM8711 SPI driver: %d\n", 4888c2ecf20Sopenharmony_ci ret); 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci#endif 4918c2ecf20Sopenharmony_ci return 0; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_cimodule_init(wm8711_modinit); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic void __exit wm8711_exit(void) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C) 4988c2ecf20Sopenharmony_ci i2c_del_driver(&wm8711_i2c_driver); 4998c2ecf20Sopenharmony_ci#endif 5008c2ecf20Sopenharmony_ci#if defined(CONFIG_SPI_MASTER) 5018c2ecf20Sopenharmony_ci spi_unregister_driver(&wm8711_spi_driver); 5028c2ecf20Sopenharmony_ci#endif 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_cimodule_exit(wm8711_exit); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ASoC WM8711 driver"); 5078c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mike Arthur"); 5088c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 509