cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

wm8711.c (12211B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * wm8711.c  --  WM8711 ALSA SoC Audio driver
      4 *
      5 * Copyright 2006 Wolfson Microelectronics
      6 *
      7 * Author: Mike Arthur <Mike.Arthur@wolfsonmicro.com>
      8 *
      9 * Based on wm8731.c by Richard Purdie
     10 */
     11
     12#include <linux/module.h>
     13#include <linux/moduleparam.h>
     14#include <linux/init.h>
     15#include <linux/delay.h>
     16#include <linux/pm.h>
     17#include <linux/i2c.h>
     18#include <linux/regmap.h>
     19#include <linux/spi/spi.h>
     20#include <linux/slab.h>
     21#include <linux/of_device.h>
     22#include <sound/core.h>
     23#include <sound/pcm.h>
     24#include <sound/pcm_params.h>
     25#include <sound/soc.h>
     26#include <sound/tlv.h>
     27#include <sound/initval.h>
     28
     29#include "wm8711.h"
     30
     31/* codec private data */
     32struct wm8711_priv {
     33	struct regmap *regmap;
     34	unsigned int sysclk;
     35};
     36
     37/*
     38 * wm8711 register cache
     39 * We can't read the WM8711 register space when we are
     40 * using 2 wire for device control, so we cache them instead.
     41 * There is no point in caching the reset register
     42 */
     43static const struct reg_default wm8711_reg_defaults[] = {
     44	{ 0, 0x0079 }, { 1, 0x0079 }, { 2, 0x000a }, { 3, 0x0008 },
     45	{ 4, 0x009f }, { 5, 0x000a }, { 6, 0x0000 }, { 7, 0x0000 },
     46};
     47
     48static bool wm8711_volatile(struct device *dev, unsigned int reg)
     49{
     50	switch (reg) {
     51	case WM8711_RESET:
     52		return true;
     53	default:
     54		return false;
     55	}
     56}
     57
     58#define wm8711_reset(c)	snd_soc_component_write(c, WM8711_RESET, 0)
     59
     60static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
     61
     62static const struct snd_kcontrol_new wm8711_snd_controls[] = {
     63
     64SOC_DOUBLE_R_TLV("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V,
     65		 0, 127, 0, out_tlv),
     66SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V,
     67	7, 1, 0),
     68
     69};
     70
     71/* Output Mixer */
     72static const struct snd_kcontrol_new wm8711_output_mixer_controls[] = {
     73SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0),
     74SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0),
     75};
     76
     77static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = {
     78SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1,
     79	&wm8711_output_mixer_controls[0],
     80	ARRAY_SIZE(wm8711_output_mixer_controls)),
     81SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1),
     82SND_SOC_DAPM_OUTPUT("LOUT"),
     83SND_SOC_DAPM_OUTPUT("LHPOUT"),
     84SND_SOC_DAPM_OUTPUT("ROUT"),
     85SND_SOC_DAPM_OUTPUT("RHPOUT"),
     86};
     87
     88static const struct snd_soc_dapm_route wm8711_intercon[] = {
     89	/* output mixer */
     90	{"Output Mixer", "Line Bypass Switch", "Line Input"},
     91	{"Output Mixer", "HiFi Playback Switch", "DAC"},
     92
     93	/* outputs */
     94	{"RHPOUT", NULL, "Output Mixer"},
     95	{"ROUT", NULL, "Output Mixer"},
     96	{"LHPOUT", NULL, "Output Mixer"},
     97	{"LOUT", NULL, "Output Mixer"},
     98};
     99
    100struct _coeff_div {
    101	u32 mclk;
    102	u32 rate;
    103	u16 fs;
    104	u8 sr:4;
    105	u8 bosr:1;
    106	u8 usb:1;
    107};
    108
    109/* codec mclk clock divider coefficients */
    110static const struct _coeff_div coeff_div[] = {
    111	/* 48k */
    112	{12288000, 48000, 256, 0x0, 0x0, 0x0},
    113	{18432000, 48000, 384, 0x0, 0x1, 0x0},
    114	{12000000, 48000, 250, 0x0, 0x0, 0x1},
    115
    116	/* 32k */
    117	{12288000, 32000, 384, 0x6, 0x0, 0x0},
    118	{18432000, 32000, 576, 0x6, 0x1, 0x0},
    119	{12000000, 32000, 375, 0x6, 0x0, 0x1},
    120
    121	/* 8k */
    122	{12288000, 8000, 1536, 0x3, 0x0, 0x0},
    123	{18432000, 8000, 2304, 0x3, 0x1, 0x0},
    124	{11289600, 8000, 1408, 0xb, 0x0, 0x0},
    125	{16934400, 8000, 2112, 0xb, 0x1, 0x0},
    126	{12000000, 8000, 1500, 0x3, 0x0, 0x1},
    127
    128	/* 96k */
    129	{12288000, 96000, 128, 0x7, 0x0, 0x0},
    130	{18432000, 96000, 192, 0x7, 0x1, 0x0},
    131	{12000000, 96000, 125, 0x7, 0x0, 0x1},
    132
    133	/* 44.1k */
    134	{11289600, 44100, 256, 0x8, 0x0, 0x0},
    135	{16934400, 44100, 384, 0x8, 0x1, 0x0},
    136	{12000000, 44100, 272, 0x8, 0x1, 0x1},
    137
    138	/* 88.2k */
    139	{11289600, 88200, 128, 0xf, 0x0, 0x0},
    140	{16934400, 88200, 192, 0xf, 0x1, 0x0},
    141	{12000000, 88200, 136, 0xf, 0x1, 0x1},
    142};
    143
    144static inline int get_coeff(int mclk, int rate)
    145{
    146	int i;
    147
    148	for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
    149		if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
    150			return i;
    151	}
    152	return 0;
    153}
    154
    155static int wm8711_hw_params(struct snd_pcm_substream *substream,
    156	struct snd_pcm_hw_params *params,
    157	struct snd_soc_dai *dai)
    158{
    159	struct snd_soc_component *component = dai->component;
    160	struct wm8711_priv *wm8711 =  snd_soc_component_get_drvdata(component);
    161	u16 iface = snd_soc_component_read(component, WM8711_IFACE) & 0xfff3;
    162	int i = get_coeff(wm8711->sysclk, params_rate(params));
    163	u16 srate = (coeff_div[i].sr << 2) |
    164		(coeff_div[i].bosr << 1) | coeff_div[i].usb;
    165
    166	snd_soc_component_write(component, WM8711_SRATE, srate);
    167
    168	/* bit size */
    169	switch (params_width(params)) {
    170	case 16:
    171		break;
    172	case 20:
    173		iface |= 0x0004;
    174		break;
    175	case 24:
    176		iface |= 0x0008;
    177		break;
    178	}
    179
    180	snd_soc_component_write(component, WM8711_IFACE, iface);
    181	return 0;
    182}
    183
    184static int wm8711_pcm_prepare(struct snd_pcm_substream *substream,
    185			      struct snd_soc_dai *dai)
    186{
    187	struct snd_soc_component *component = dai->component;
    188
    189	/* set active */
    190	snd_soc_component_write(component, WM8711_ACTIVE, 0x0001);
    191
    192	return 0;
    193}
    194
    195static void wm8711_shutdown(struct snd_pcm_substream *substream,
    196			    struct snd_soc_dai *dai)
    197{
    198	struct snd_soc_component *component = dai->component;
    199
    200	/* deactivate */
    201	if (!snd_soc_component_active(component)) {
    202		udelay(50);
    203		snd_soc_component_write(component, WM8711_ACTIVE, 0x0);
    204	}
    205}
    206
    207static int wm8711_mute(struct snd_soc_dai *dai, int mute, int direction)
    208{
    209	struct snd_soc_component *component = dai->component;
    210	u16 mute_reg = snd_soc_component_read(component, WM8711_APDIGI) & 0xfff7;
    211
    212	if (mute)
    213		snd_soc_component_write(component, WM8711_APDIGI, mute_reg | 0x8);
    214	else
    215		snd_soc_component_write(component, WM8711_APDIGI, mute_reg);
    216
    217	return 0;
    218}
    219
    220static int wm8711_set_dai_sysclk(struct snd_soc_dai *codec_dai,
    221		int clk_id, unsigned int freq, int dir)
    222{
    223	struct snd_soc_component *component = codec_dai->component;
    224	struct wm8711_priv *wm8711 =  snd_soc_component_get_drvdata(component);
    225
    226	switch (freq) {
    227	case 11289600:
    228	case 12000000:
    229	case 12288000:
    230	case 16934400:
    231	case 18432000:
    232		wm8711->sysclk = freq;
    233		return 0;
    234	}
    235	return -EINVAL;
    236}
    237
    238static int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai,
    239		unsigned int fmt)
    240{
    241	struct snd_soc_component *component = codec_dai->component;
    242	u16 iface = snd_soc_component_read(component, WM8711_IFACE) & 0x000c;
    243
    244	/* set master/slave audio interface */
    245	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
    246	case SND_SOC_DAIFMT_CBM_CFM:
    247		iface |= 0x0040;
    248		break;
    249	case SND_SOC_DAIFMT_CBS_CFS:
    250		break;
    251	default:
    252		return -EINVAL;
    253	}
    254
    255	/* interface format */
    256	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
    257	case SND_SOC_DAIFMT_I2S:
    258		iface |= 0x0002;
    259		break;
    260	case SND_SOC_DAIFMT_RIGHT_J:
    261		break;
    262	case SND_SOC_DAIFMT_LEFT_J:
    263		iface |= 0x0001;
    264		break;
    265	case SND_SOC_DAIFMT_DSP_A:
    266		iface |= 0x0003;
    267		break;
    268	case SND_SOC_DAIFMT_DSP_B:
    269		iface |= 0x0013;
    270		break;
    271	default:
    272		return -EINVAL;
    273	}
    274
    275	/* clock inversion */
    276	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
    277	case SND_SOC_DAIFMT_NB_NF:
    278		break;
    279	case SND_SOC_DAIFMT_IB_IF:
    280		iface |= 0x0090;
    281		break;
    282	case SND_SOC_DAIFMT_IB_NF:
    283		iface |= 0x0080;
    284		break;
    285	case SND_SOC_DAIFMT_NB_IF:
    286		iface |= 0x0010;
    287		break;
    288	default:
    289		return -EINVAL;
    290	}
    291
    292	/* set iface */
    293	snd_soc_component_write(component, WM8711_IFACE, iface);
    294	return 0;
    295}
    296
    297static int wm8711_set_bias_level(struct snd_soc_component *component,
    298	enum snd_soc_bias_level level)
    299{
    300	struct wm8711_priv *wm8711 = snd_soc_component_get_drvdata(component);
    301	u16 reg = snd_soc_component_read(component, WM8711_PWR) & 0xff7f;
    302
    303	switch (level) {
    304	case SND_SOC_BIAS_ON:
    305		snd_soc_component_write(component, WM8711_PWR, reg);
    306		break;
    307	case SND_SOC_BIAS_PREPARE:
    308		break;
    309	case SND_SOC_BIAS_STANDBY:
    310		if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
    311			regcache_sync(wm8711->regmap);
    312
    313		snd_soc_component_write(component, WM8711_PWR, reg | 0x0040);
    314		break;
    315	case SND_SOC_BIAS_OFF:
    316		snd_soc_component_write(component, WM8711_ACTIVE, 0x0);
    317		snd_soc_component_write(component, WM8711_PWR, 0xffff);
    318		break;
    319	}
    320	return 0;
    321}
    322
    323#define WM8711_RATES SNDRV_PCM_RATE_8000_96000
    324
    325#define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
    326	SNDRV_PCM_FMTBIT_S24_LE)
    327
    328static const struct snd_soc_dai_ops wm8711_ops = {
    329	.prepare = wm8711_pcm_prepare,
    330	.hw_params = wm8711_hw_params,
    331	.shutdown = wm8711_shutdown,
    332	.mute_stream = wm8711_mute,
    333	.set_sysclk = wm8711_set_dai_sysclk,
    334	.set_fmt = wm8711_set_dai_fmt,
    335	.no_capture_mute = 1,
    336};
    337
    338static struct snd_soc_dai_driver wm8711_dai = {
    339	.name = "wm8711-hifi",
    340	.playback = {
    341		.stream_name = "Playback",
    342		.channels_min = 1,
    343		.channels_max = 2,
    344		.rates = WM8711_RATES,
    345		.formats = WM8711_FORMATS,
    346	},
    347	.ops = &wm8711_ops,
    348};
    349
    350static int wm8711_probe(struct snd_soc_component *component)
    351{
    352	int ret;
    353
    354	ret = wm8711_reset(component);
    355	if (ret < 0) {
    356		dev_err(component->dev, "Failed to issue reset\n");
    357		return ret;
    358	}
    359
    360	/* Latch the update bits */
    361	snd_soc_component_update_bits(component, WM8711_LOUT1V, 0x0100, 0x0100);
    362	snd_soc_component_update_bits(component, WM8711_ROUT1V, 0x0100, 0x0100);
    363
    364	return ret;
    365
    366}
    367
    368static const struct snd_soc_component_driver soc_component_dev_wm8711 = {
    369	.probe			= wm8711_probe,
    370	.set_bias_level		= wm8711_set_bias_level,
    371	.controls		= wm8711_snd_controls,
    372	.num_controls		= ARRAY_SIZE(wm8711_snd_controls),
    373	.dapm_widgets		= wm8711_dapm_widgets,
    374	.num_dapm_widgets	= ARRAY_SIZE(wm8711_dapm_widgets),
    375	.dapm_routes		= wm8711_intercon,
    376	.num_dapm_routes	= ARRAY_SIZE(wm8711_intercon),
    377	.suspend_bias_off	= 1,
    378	.idle_bias_on		= 1,
    379	.use_pmdown_time	= 1,
    380	.endianness		= 1,
    381	.non_legacy_dai_naming	= 1,
    382};
    383
    384static const struct of_device_id wm8711_of_match[] = {
    385	{ .compatible = "wlf,wm8711", },
    386	{ }
    387};
    388MODULE_DEVICE_TABLE(of, wm8711_of_match);
    389
    390static const struct regmap_config wm8711_regmap = {
    391	.reg_bits = 7,
    392	.val_bits = 9,
    393	.max_register = WM8711_RESET,
    394
    395	.reg_defaults = wm8711_reg_defaults,
    396	.num_reg_defaults = ARRAY_SIZE(wm8711_reg_defaults),
    397	.cache_type = REGCACHE_RBTREE,
    398
    399	.volatile_reg = wm8711_volatile,
    400};
    401
    402#if defined(CONFIG_SPI_MASTER)
    403static int wm8711_spi_probe(struct spi_device *spi)
    404{
    405	struct wm8711_priv *wm8711;
    406	int ret;
    407
    408	wm8711 = devm_kzalloc(&spi->dev, sizeof(struct wm8711_priv),
    409			      GFP_KERNEL);
    410	if (wm8711 == NULL)
    411		return -ENOMEM;
    412
    413	wm8711->regmap = devm_regmap_init_spi(spi, &wm8711_regmap);
    414	if (IS_ERR(wm8711->regmap))
    415		return PTR_ERR(wm8711->regmap);
    416
    417	spi_set_drvdata(spi, wm8711);
    418
    419	ret = devm_snd_soc_register_component(&spi->dev,
    420			&soc_component_dev_wm8711, &wm8711_dai, 1);
    421
    422	return ret;
    423}
    424
    425static struct spi_driver wm8711_spi_driver = {
    426	.driver = {
    427		.name	= "wm8711",
    428		.of_match_table = wm8711_of_match,
    429	},
    430	.probe		= wm8711_spi_probe,
    431};
    432#endif /* CONFIG_SPI_MASTER */
    433
    434#if IS_ENABLED(CONFIG_I2C)
    435static int wm8711_i2c_probe(struct i2c_client *client)
    436{
    437	struct wm8711_priv *wm8711;
    438	int ret;
    439
    440	wm8711 = devm_kzalloc(&client->dev, sizeof(struct wm8711_priv),
    441			      GFP_KERNEL);
    442	if (wm8711 == NULL)
    443		return -ENOMEM;
    444
    445	wm8711->regmap = devm_regmap_init_i2c(client, &wm8711_regmap);
    446	if (IS_ERR(wm8711->regmap))
    447		return PTR_ERR(wm8711->regmap);
    448
    449	i2c_set_clientdata(client, wm8711);
    450
    451	ret = devm_snd_soc_register_component(&client->dev,
    452			&soc_component_dev_wm8711, &wm8711_dai, 1);
    453
    454	return ret;
    455}
    456
    457static const struct i2c_device_id wm8711_i2c_id[] = {
    458	{ "wm8711", 0 },
    459	{ }
    460};
    461MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id);
    462
    463static struct i2c_driver wm8711_i2c_driver = {
    464	.driver = {
    465		.name = "wm8711",
    466		.of_match_table = wm8711_of_match,
    467	},
    468	.probe_new = wm8711_i2c_probe,
    469	.id_table = wm8711_i2c_id,
    470};
    471#endif
    472
    473static int __init wm8711_modinit(void)
    474{
    475	int ret;
    476#if IS_ENABLED(CONFIG_I2C)
    477	ret = i2c_add_driver(&wm8711_i2c_driver);
    478	if (ret != 0) {
    479		printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n",
    480		       ret);
    481	}
    482#endif
    483#if defined(CONFIG_SPI_MASTER)
    484	ret = spi_register_driver(&wm8711_spi_driver);
    485	if (ret != 0) {
    486		printk(KERN_ERR "Failed to register WM8711 SPI driver: %d\n",
    487		       ret);
    488	}
    489#endif
    490	return 0;
    491}
    492module_init(wm8711_modinit);
    493
    494static void __exit wm8711_exit(void)
    495{
    496#if IS_ENABLED(CONFIG_I2C)
    497	i2c_del_driver(&wm8711_i2c_driver);
    498#endif
    499#if defined(CONFIG_SPI_MASTER)
    500	spi_unregister_driver(&wm8711_spi_driver);
    501#endif
    502}
    503module_exit(wm8711_exit);
    504
    505MODULE_DESCRIPTION("ASoC WM8711 driver");
    506MODULE_AUTHOR("Mike Arthur");
    507MODULE_LICENSE("GPL");