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

wm8776.c (13449B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * wm8776.c  --  WM8776 ALSA SoC Audio driver
      4 *
      5 * Copyright 2009-12 Wolfson Microelectronics plc
      6 *
      7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
      8 *
      9 * TODO: Input ALC/limiter support
     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/of_device.h>
     19#include <linux/regmap.h>
     20#include <linux/spi/spi.h>
     21#include <linux/slab.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/initval.h>
     27#include <sound/tlv.h>
     28
     29#include "wm8776.h"
     30
     31enum wm8776_chip_type {
     32	WM8775 = 1,
     33	WM8776,
     34};
     35
     36/* codec private data */
     37struct wm8776_priv {
     38	struct regmap *regmap;
     39	int sysclk[2];
     40};
     41
     42static const struct reg_default wm8776_reg_defaults[] = {
     43	{  0, 0x79 },
     44	{  1, 0x79 },
     45	{  2, 0x79 },
     46	{  3, 0xff },
     47	{  4, 0xff },
     48	{  5, 0xff },
     49	{  6, 0x00 },
     50	{  7, 0x90 },
     51	{  8, 0x00 },
     52	{  9, 0x00 },
     53	{ 10, 0x22 },
     54	{ 11, 0x22 },
     55	{ 12, 0x22 },
     56	{ 13, 0x08 },
     57	{ 14, 0xcf },
     58	{ 15, 0xcf },
     59	{ 16, 0x7b },
     60	{ 17, 0x00 },
     61	{ 18, 0x32 },
     62	{ 19, 0x00 },
     63	{ 20, 0xa6 },
     64	{ 21, 0x01 },
     65	{ 22, 0x01 },
     66};
     67
     68static bool wm8776_volatile(struct device *dev, unsigned int reg)
     69{
     70	switch (reg) {
     71	case WM8776_RESET:
     72		return true;
     73	default:
     74		return false;
     75	}
     76}
     77
     78static int wm8776_reset(struct snd_soc_component *component)
     79{
     80	return snd_soc_component_write(component, WM8776_RESET, 0);
     81}
     82
     83static const DECLARE_TLV_DB_SCALE(hp_tlv, -12100, 100, 1);
     84static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
     85static const DECLARE_TLV_DB_SCALE(adc_tlv, -10350, 50, 1);
     86
     87static const struct snd_kcontrol_new wm8776_snd_controls[] = {
     88SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8776_HPLVOL, WM8776_HPRVOL,
     89		 0, 127, 0, hp_tlv),
     90SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8776_DACLVOL, WM8776_DACRVOL,
     91		 0, 255, 0, dac_tlv),
     92SOC_SINGLE("Digital Playback ZC Switch", WM8776_DACCTRL1, 0, 1, 0),
     93
     94SOC_SINGLE("Deemphasis Switch", WM8776_DACCTRL2, 0, 1, 0),
     95
     96SOC_DOUBLE_R_TLV("Capture Volume", WM8776_ADCLVOL, WM8776_ADCRVOL,
     97		 0, 255, 0, adc_tlv),
     98SOC_DOUBLE("Capture Switch", WM8776_ADCMUX, 7, 6, 1, 1),
     99SOC_DOUBLE_R("Capture ZC Switch", WM8776_ADCLVOL, WM8776_ADCRVOL, 8, 1, 0),
    100SOC_SINGLE("Capture HPF Switch", WM8776_ADCIFCTRL, 8, 1, 1),
    101};
    102
    103static const struct snd_kcontrol_new inmix_controls[] = {
    104SOC_DAPM_SINGLE("AIN1 Switch", WM8776_ADCMUX, 0, 1, 0),
    105SOC_DAPM_SINGLE("AIN2 Switch", WM8776_ADCMUX, 1, 1, 0),
    106SOC_DAPM_SINGLE("AIN3 Switch", WM8776_ADCMUX, 2, 1, 0),
    107SOC_DAPM_SINGLE("AIN4 Switch", WM8776_ADCMUX, 3, 1, 0),
    108SOC_DAPM_SINGLE("AIN5 Switch", WM8776_ADCMUX, 4, 1, 0),
    109};
    110
    111static const struct snd_kcontrol_new outmix_controls[] = {
    112SOC_DAPM_SINGLE("DAC Switch", WM8776_OUTMUX, 0, 1, 0),
    113SOC_DAPM_SINGLE("AUX Switch", WM8776_OUTMUX, 1, 1, 0),
    114SOC_DAPM_SINGLE("Bypass Switch", WM8776_OUTMUX, 2, 1, 0),
    115};
    116
    117static const struct snd_soc_dapm_widget wm8776_dapm_widgets[] = {
    118SND_SOC_DAPM_INPUT("AUX"),
    119
    120SND_SOC_DAPM_INPUT("AIN1"),
    121SND_SOC_DAPM_INPUT("AIN2"),
    122SND_SOC_DAPM_INPUT("AIN3"),
    123SND_SOC_DAPM_INPUT("AIN4"),
    124SND_SOC_DAPM_INPUT("AIN5"),
    125
    126SND_SOC_DAPM_MIXER("Input Mixer", WM8776_PWRDOWN, 6, 1,
    127		   inmix_controls, ARRAY_SIZE(inmix_controls)),
    128
    129SND_SOC_DAPM_ADC("ADC", "Capture", WM8776_PWRDOWN, 1, 1),
    130SND_SOC_DAPM_DAC("DAC", "Playback", WM8776_PWRDOWN, 2, 1),
    131
    132SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
    133		   outmix_controls, ARRAY_SIZE(outmix_controls)),
    134
    135SND_SOC_DAPM_PGA("Headphone PGA", WM8776_PWRDOWN, 3, 1, NULL, 0),
    136
    137SND_SOC_DAPM_OUTPUT("VOUT"),
    138
    139SND_SOC_DAPM_OUTPUT("HPOUTL"),
    140SND_SOC_DAPM_OUTPUT("HPOUTR"),
    141};
    142
    143static const struct snd_soc_dapm_route routes[] = {
    144	{ "Input Mixer", "AIN1 Switch", "AIN1" },
    145	{ "Input Mixer", "AIN2 Switch", "AIN2" },
    146	{ "Input Mixer", "AIN3 Switch", "AIN3" },
    147	{ "Input Mixer", "AIN4 Switch", "AIN4" },
    148	{ "Input Mixer", "AIN5 Switch", "AIN5" },
    149
    150	{ "ADC", NULL, "Input Mixer" },
    151
    152	{ "Output Mixer", "DAC Switch", "DAC" },
    153	{ "Output Mixer", "AUX Switch", "AUX" },
    154	{ "Output Mixer", "Bypass Switch", "Input Mixer" },
    155
    156	{ "VOUT", NULL, "Output Mixer" },
    157
    158	{ "Headphone PGA", NULL, "Output Mixer" },
    159
    160	{ "HPOUTL", NULL, "Headphone PGA" },
    161	{ "HPOUTR", NULL, "Headphone PGA" },
    162};
    163
    164static int wm8776_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
    165{
    166	struct snd_soc_component *component = dai->component;
    167	int reg, iface, master;
    168
    169	switch (dai->driver->id) {
    170	case WM8776_DAI_DAC:
    171		reg = WM8776_DACIFCTRL;
    172		master = 0x80;
    173		break;
    174	case WM8776_DAI_ADC:
    175		reg = WM8776_ADCIFCTRL;
    176		master = 0x100;
    177		break;
    178	default:
    179		return -EINVAL;
    180	}
    181
    182	iface = 0;
    183
    184	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
    185	case SND_SOC_DAIFMT_CBM_CFM:
    186		break;
    187	case SND_SOC_DAIFMT_CBS_CFS:
    188		master = 0;
    189		break;
    190	default:
    191		return -EINVAL;
    192	}
    193
    194	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
    195	case SND_SOC_DAIFMT_I2S:
    196		iface |= 0x0002;
    197		break;
    198	case SND_SOC_DAIFMT_RIGHT_J:
    199		break;
    200	case SND_SOC_DAIFMT_LEFT_J:
    201		iface |= 0x0001;
    202		break;
    203	default:
    204		return -EINVAL;
    205	}
    206
    207	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
    208	case SND_SOC_DAIFMT_NB_NF:
    209		break;
    210	case SND_SOC_DAIFMT_IB_IF:
    211		iface |= 0x00c;
    212		break;
    213	case SND_SOC_DAIFMT_IB_NF:
    214		iface |= 0x008;
    215		break;
    216	case SND_SOC_DAIFMT_NB_IF:
    217		iface |= 0x004;
    218		break;
    219	default:
    220		return -EINVAL;
    221	}
    222
    223	/* Finally, write out the values */
    224	snd_soc_component_update_bits(component, reg, 0xf, iface);
    225	snd_soc_component_update_bits(component, WM8776_MSTRCTRL, 0x180, master);
    226
    227	return 0;
    228}
    229
    230static int mclk_ratios[] = {
    231	128,
    232	192,
    233	256,
    234	384,
    235	512,
    236	768,
    237};
    238
    239static int wm8776_hw_params(struct snd_pcm_substream *substream,
    240			    struct snd_pcm_hw_params *params,
    241			    struct snd_soc_dai *dai)
    242{
    243	struct snd_soc_component *component = dai->component;
    244	struct wm8776_priv *wm8776 = snd_soc_component_get_drvdata(component);
    245	int iface_reg, iface;
    246	int ratio_shift, master;
    247	int i;
    248
    249	switch (dai->driver->id) {
    250	case WM8776_DAI_DAC:
    251		iface_reg = WM8776_DACIFCTRL;
    252		master = 0x80;
    253		ratio_shift = 4;
    254		break;
    255	case WM8776_DAI_ADC:
    256		iface_reg = WM8776_ADCIFCTRL;
    257		master = 0x100;
    258		ratio_shift = 0;
    259		break;
    260	default:
    261		return -EINVAL;
    262	}
    263
    264	/* Set word length */
    265	switch (params_width(params)) {
    266	case 16:
    267		iface = 0;
    268		break;
    269	case 20:
    270		iface = 0x10;
    271		break;
    272	case 24:
    273		iface = 0x20;
    274		break;
    275	case 32:
    276		iface = 0x30;
    277		break;
    278	default:
    279		dev_err(component->dev, "Unsupported sample size: %i\n",
    280			params_width(params));
    281		return -EINVAL;
    282	}
    283
    284	/* Only need to set MCLK/LRCLK ratio if we're master */
    285	if (snd_soc_component_read(component, WM8776_MSTRCTRL) & master) {
    286		for (i = 0; i < ARRAY_SIZE(mclk_ratios); i++) {
    287			if (wm8776->sysclk[dai->driver->id] / params_rate(params)
    288			    == mclk_ratios[i])
    289				break;
    290		}
    291
    292		if (i == ARRAY_SIZE(mclk_ratios)) {
    293			dev_err(component->dev,
    294				"Unable to configure MCLK ratio %d/%d\n",
    295				wm8776->sysclk[dai->driver->id], params_rate(params));
    296			return -EINVAL;
    297		}
    298
    299		dev_dbg(component->dev, "MCLK is %dfs\n", mclk_ratios[i]);
    300
    301		snd_soc_component_update_bits(component, WM8776_MSTRCTRL,
    302				    0x7 << ratio_shift, i << ratio_shift);
    303	} else {
    304		dev_dbg(component->dev, "DAI in slave mode\n");
    305	}
    306
    307	snd_soc_component_update_bits(component, iface_reg, 0x30, iface);
    308
    309	return 0;
    310}
    311
    312static int wm8776_mute(struct snd_soc_dai *dai, int mute, int direction)
    313{
    314	struct snd_soc_component *component = dai->component;
    315
    316	return snd_soc_component_write(component, WM8776_DACMUTE, !!mute);
    317}
    318
    319static int wm8776_set_sysclk(struct snd_soc_dai *dai,
    320			     int clk_id, unsigned int freq, int dir)
    321{
    322	struct snd_soc_component *component = dai->component;
    323	struct wm8776_priv *wm8776 = snd_soc_component_get_drvdata(component);
    324
    325	if (WARN_ON(dai->driver->id >= ARRAY_SIZE(wm8776->sysclk)))
    326		return -EINVAL;
    327
    328	wm8776->sysclk[dai->driver->id] = freq;
    329
    330	return 0;
    331}
    332
    333static int wm8776_set_bias_level(struct snd_soc_component *component,
    334				 enum snd_soc_bias_level level)
    335{
    336	struct wm8776_priv *wm8776 = snd_soc_component_get_drvdata(component);
    337
    338	switch (level) {
    339	case SND_SOC_BIAS_ON:
    340		break;
    341	case SND_SOC_BIAS_PREPARE:
    342		break;
    343	case SND_SOC_BIAS_STANDBY:
    344		if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
    345			regcache_sync(wm8776->regmap);
    346
    347			/* Disable the global powerdown; DAPM does the rest */
    348			snd_soc_component_update_bits(component, WM8776_PWRDOWN, 1, 0);
    349		}
    350
    351		break;
    352	case SND_SOC_BIAS_OFF:
    353		snd_soc_component_update_bits(component, WM8776_PWRDOWN, 1, 1);
    354		break;
    355	}
    356
    357	return 0;
    358}
    359
    360#define WM8776_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
    361			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
    362
    363static const struct snd_soc_dai_ops wm8776_dac_ops = {
    364	.mute_stream	= wm8776_mute,
    365	.hw_params      = wm8776_hw_params,
    366	.set_fmt        = wm8776_set_fmt,
    367	.set_sysclk     = wm8776_set_sysclk,
    368	.no_capture_mute = 1,
    369};
    370
    371static const struct snd_soc_dai_ops wm8776_adc_ops = {
    372	.hw_params      = wm8776_hw_params,
    373	.set_fmt        = wm8776_set_fmt,
    374	.set_sysclk     = wm8776_set_sysclk,
    375};
    376
    377static struct snd_soc_dai_driver wm8776_dai[] = {
    378	{
    379		.name = "wm8776-hifi-playback",
    380		.id	= WM8776_DAI_DAC,
    381		.playback = {
    382			.stream_name = "Playback",
    383			.channels_min = 2,
    384			.channels_max = 2,
    385			.rates = SNDRV_PCM_RATE_CONTINUOUS,
    386			.rate_min = 32000,
    387			.rate_max = 192000,
    388			.formats = WM8776_FORMATS,
    389		},
    390		.ops = &wm8776_dac_ops,
    391	},
    392	{
    393		.name = "wm8776-hifi-capture",
    394		.id	= WM8776_DAI_ADC,
    395		.capture = {
    396			.stream_name = "Capture",
    397			.channels_min = 2,
    398			.channels_max = 2,
    399			.rates = SNDRV_PCM_RATE_CONTINUOUS,
    400			.rate_min = 32000,
    401			.rate_max = 96000,
    402			.formats = WM8776_FORMATS,
    403		},
    404		.ops = &wm8776_adc_ops,
    405	},
    406};
    407
    408static int wm8776_probe(struct snd_soc_component *component)
    409{
    410	int ret = 0;
    411
    412	ret = wm8776_reset(component);
    413	if (ret < 0) {
    414		dev_err(component->dev, "Failed to issue reset: %d\n", ret);
    415		return ret;
    416	}
    417
    418	/* Latch the update bits; right channel only since we always
    419	 * update both. */
    420	snd_soc_component_update_bits(component, WM8776_HPRVOL, 0x100, 0x100);
    421	snd_soc_component_update_bits(component, WM8776_DACRVOL, 0x100, 0x100);
    422
    423	return ret;
    424}
    425
    426static const struct snd_soc_component_driver soc_component_dev_wm8776 = {
    427	.probe			= wm8776_probe,
    428	.set_bias_level		= wm8776_set_bias_level,
    429	.controls		= wm8776_snd_controls,
    430	.num_controls		= ARRAY_SIZE(wm8776_snd_controls),
    431	.dapm_widgets		= wm8776_dapm_widgets,
    432	.num_dapm_widgets	= ARRAY_SIZE(wm8776_dapm_widgets),
    433	.dapm_routes		= routes,
    434	.num_dapm_routes	= ARRAY_SIZE(routes),
    435	.suspend_bias_off	= 1,
    436	.idle_bias_on		= 1,
    437	.use_pmdown_time	= 1,
    438	.endianness		= 1,
    439	.non_legacy_dai_naming	= 1,
    440};
    441
    442static const struct of_device_id wm8776_of_match[] = {
    443	{ .compatible = "wlf,wm8776", },
    444	{ }
    445};
    446MODULE_DEVICE_TABLE(of, wm8776_of_match);
    447
    448static const struct regmap_config wm8776_regmap = {
    449	.reg_bits = 7,
    450	.val_bits = 9,
    451	.max_register = WM8776_RESET,
    452
    453	.reg_defaults = wm8776_reg_defaults,
    454	.num_reg_defaults = ARRAY_SIZE(wm8776_reg_defaults),
    455	.cache_type = REGCACHE_RBTREE,
    456
    457	.volatile_reg = wm8776_volatile,
    458};
    459
    460#if defined(CONFIG_SPI_MASTER)
    461static int wm8776_spi_probe(struct spi_device *spi)
    462{
    463	struct wm8776_priv *wm8776;
    464	int ret;
    465
    466	wm8776 = devm_kzalloc(&spi->dev, sizeof(struct wm8776_priv),
    467			      GFP_KERNEL);
    468	if (wm8776 == NULL)
    469		return -ENOMEM;
    470
    471	wm8776->regmap = devm_regmap_init_spi(spi, &wm8776_regmap);
    472	if (IS_ERR(wm8776->regmap))
    473		return PTR_ERR(wm8776->regmap);
    474
    475	spi_set_drvdata(spi, wm8776);
    476
    477	ret = devm_snd_soc_register_component(&spi->dev,
    478			&soc_component_dev_wm8776, wm8776_dai, ARRAY_SIZE(wm8776_dai));
    479
    480	return ret;
    481}
    482
    483static struct spi_driver wm8776_spi_driver = {
    484	.driver = {
    485		.name	= "wm8776",
    486		.of_match_table = wm8776_of_match,
    487	},
    488	.probe		= wm8776_spi_probe,
    489};
    490#endif /* CONFIG_SPI_MASTER */
    491
    492#if IS_ENABLED(CONFIG_I2C)
    493static int wm8776_i2c_probe(struct i2c_client *i2c)
    494{
    495	struct wm8776_priv *wm8776;
    496	int ret;
    497
    498	wm8776 = devm_kzalloc(&i2c->dev, sizeof(struct wm8776_priv),
    499			      GFP_KERNEL);
    500	if (wm8776 == NULL)
    501		return -ENOMEM;
    502
    503	wm8776->regmap = devm_regmap_init_i2c(i2c, &wm8776_regmap);
    504	if (IS_ERR(wm8776->regmap))
    505		return PTR_ERR(wm8776->regmap);
    506
    507	i2c_set_clientdata(i2c, wm8776);
    508
    509	ret = devm_snd_soc_register_component(&i2c->dev,
    510			&soc_component_dev_wm8776, wm8776_dai, ARRAY_SIZE(wm8776_dai));
    511
    512	return ret;
    513}
    514
    515static const struct i2c_device_id wm8776_i2c_id[] = {
    516	{ "wm8775", WM8775 },
    517	{ "wm8776", WM8776 },
    518	{ }
    519};
    520MODULE_DEVICE_TABLE(i2c, wm8776_i2c_id);
    521
    522static struct i2c_driver wm8776_i2c_driver = {
    523	.driver = {
    524		.name = "wm8776",
    525		.of_match_table = wm8776_of_match,
    526	},
    527	.probe_new = wm8776_i2c_probe,
    528	.id_table = wm8776_i2c_id,
    529};
    530#endif
    531
    532static int __init wm8776_modinit(void)
    533{
    534	int ret = 0;
    535#if IS_ENABLED(CONFIG_I2C)
    536	ret = i2c_add_driver(&wm8776_i2c_driver);
    537	if (ret != 0) {
    538		printk(KERN_ERR "Failed to register wm8776 I2C driver: %d\n",
    539		       ret);
    540	}
    541#endif
    542#if defined(CONFIG_SPI_MASTER)
    543	ret = spi_register_driver(&wm8776_spi_driver);
    544	if (ret != 0) {
    545		printk(KERN_ERR "Failed to register wm8776 SPI driver: %d\n",
    546		       ret);
    547	}
    548#endif
    549	return ret;
    550}
    551module_init(wm8776_modinit);
    552
    553static void __exit wm8776_exit(void)
    554{
    555#if IS_ENABLED(CONFIG_I2C)
    556	i2c_del_driver(&wm8776_i2c_driver);
    557#endif
    558#if defined(CONFIG_SPI_MASTER)
    559	spi_unregister_driver(&wm8776_spi_driver);
    560#endif
    561}
    562module_exit(wm8776_exit);
    563
    564MODULE_DESCRIPTION("ASoC WM8776 driver");
    565MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
    566MODULE_LICENSE("GPL");