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

pcm1681.c (9053B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * PCM1681 ASoC codec driver
      4 *
      5 * Copyright (c) StreamUnlimited GmbH 2013
      6 *	Marek Belisko <marek.belisko@streamunlimited.com>
      7 */
      8
      9#include <linux/module.h>
     10#include <linux/slab.h>
     11#include <linux/delay.h>
     12#include <linux/gpio.h>
     13#include <linux/i2c.h>
     14#include <linux/regmap.h>
     15#include <linux/of.h>
     16#include <linux/of_device.h>
     17#include <linux/of_gpio.h>
     18#include <sound/pcm.h>
     19#include <sound/pcm_params.h>
     20#include <sound/soc.h>
     21#include <sound/tlv.h>
     22
     23#define PCM1681_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE  |		\
     24			     SNDRV_PCM_FMTBIT_S24_LE)
     25
     26#define PCM1681_PCM_RATES   (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
     27			     SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100  | \
     28			     SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200  | \
     29			     SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
     30
     31#define PCM1681_SOFT_MUTE_ALL		0xff
     32#define PCM1681_DEEMPH_RATE_MASK	0x18
     33#define PCM1681_DEEMPH_MASK		0x01
     34
     35#define PCM1681_ATT_CONTROL(X)	(X <= 6 ? X : X + 9) /* Attenuation level */
     36#define PCM1681_SOFT_MUTE	0x07	/* Soft mute control register */
     37#define PCM1681_DAC_CONTROL	0x08	/* DAC operation control */
     38#define PCM1681_FMT_CONTROL	0x09	/* Audio interface data format */
     39#define PCM1681_DEEMPH_CONTROL	0x0a	/* De-emphasis control */
     40#define PCM1681_ZERO_DETECT_STATUS	0x0e	/* Zero detect status reg */
     41
     42static const struct reg_default pcm1681_reg_defaults[] = {
     43	{ 0x01,	0xff },
     44	{ 0x02,	0xff },
     45	{ 0x03,	0xff },
     46	{ 0x04,	0xff },
     47	{ 0x05,	0xff },
     48	{ 0x06,	0xff },
     49	{ 0x07,	0x00 },
     50	{ 0x08,	0x00 },
     51	{ 0x09,	0x06 },
     52	{ 0x0A,	0x00 },
     53	{ 0x0B,	0xff },
     54	{ 0x0C,	0x0f },
     55	{ 0x0D,	0x00 },
     56	{ 0x10,	0xff },
     57	{ 0x11,	0xff },
     58	{ 0x12,	0x00 },
     59	{ 0x13,	0x00 },
     60};
     61
     62static bool pcm1681_accessible_reg(struct device *dev, unsigned int reg)
     63{
     64	return !((reg == 0x00) || (reg == 0x0f));
     65}
     66
     67static bool pcm1681_writeable_reg(struct device *dev, unsigned int reg)
     68{
     69	return pcm1681_accessible_reg(dev, reg) &&
     70		(reg != PCM1681_ZERO_DETECT_STATUS);
     71}
     72
     73struct pcm1681_private {
     74	struct regmap *regmap;
     75	unsigned int format;
     76	/* Current deemphasis status */
     77	unsigned int deemph;
     78	/* Current rate for deemphasis control */
     79	unsigned int rate;
     80};
     81
     82static const int pcm1681_deemph[] = { 44100, 48000, 32000 };
     83
     84static int pcm1681_set_deemph(struct snd_soc_component *component)
     85{
     86	struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
     87	int i, val = -1, enable = 0;
     88
     89	if (priv->deemph) {
     90		for (i = 0; i < ARRAY_SIZE(pcm1681_deemph); i++) {
     91			if (pcm1681_deemph[i] == priv->rate) {
     92				val = i;
     93				break;
     94			}
     95		}
     96	}
     97
     98	if (val != -1) {
     99		regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL,
    100				   PCM1681_DEEMPH_RATE_MASK, val << 3);
    101		enable = 1;
    102	} else {
    103		enable = 0;
    104	}
    105
    106	/* enable/disable deemphasis functionality */
    107	return regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL,
    108					PCM1681_DEEMPH_MASK, enable);
    109}
    110
    111static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol,
    112			      struct snd_ctl_elem_value *ucontrol)
    113{
    114	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
    115	struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
    116
    117	ucontrol->value.integer.value[0] = priv->deemph;
    118
    119	return 0;
    120}
    121
    122static int pcm1681_put_deemph(struct snd_kcontrol *kcontrol,
    123			      struct snd_ctl_elem_value *ucontrol)
    124{
    125	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
    126	struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
    127
    128	priv->deemph = ucontrol->value.integer.value[0];
    129
    130	return pcm1681_set_deemph(component);
    131}
    132
    133static int pcm1681_set_dai_fmt(struct snd_soc_dai *codec_dai,
    134			      unsigned int format)
    135{
    136	struct snd_soc_component *component = codec_dai->component;
    137	struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
    138
    139	/* The PCM1681 can only be consumer to all clocks */
    140	if ((format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) {
    141		dev_err(component->dev, "Invalid clocking mode\n");
    142		return -EINVAL;
    143	}
    144
    145	priv->format = format;
    146
    147	return 0;
    148}
    149
    150static int pcm1681_mute(struct snd_soc_dai *dai, int mute, int direction)
    151{
    152	struct snd_soc_component *component = dai->component;
    153	struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
    154	int val;
    155
    156	if (mute)
    157		val = PCM1681_SOFT_MUTE_ALL;
    158	else
    159		val = 0;
    160
    161	return regmap_write(priv->regmap, PCM1681_SOFT_MUTE, val);
    162}
    163
    164static int pcm1681_hw_params(struct snd_pcm_substream *substream,
    165			     struct snd_pcm_hw_params *params,
    166			     struct snd_soc_dai *dai)
    167{
    168	struct snd_soc_component *component = dai->component;
    169	struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
    170	int val = 0, ret;
    171
    172	priv->rate = params_rate(params);
    173
    174	switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
    175	case SND_SOC_DAIFMT_RIGHT_J:
    176		switch (params_width(params)) {
    177		case 24:
    178			val = 0;
    179			break;
    180		case 16:
    181			val = 3;
    182			break;
    183		default:
    184			return -EINVAL;
    185		}
    186		break;
    187	case SND_SOC_DAIFMT_I2S:
    188		val = 0x04;
    189		break;
    190	case SND_SOC_DAIFMT_LEFT_J:
    191		val = 0x05;
    192		break;
    193	default:
    194		dev_err(component->dev, "Invalid DAI format\n");
    195		return -EINVAL;
    196	}
    197
    198	ret = regmap_update_bits(priv->regmap, PCM1681_FMT_CONTROL, 0x0f, val);
    199	if (ret < 0)
    200		return ret;
    201
    202	return pcm1681_set_deemph(component);
    203}
    204
    205static const struct snd_soc_dai_ops pcm1681_dai_ops = {
    206	.set_fmt	= pcm1681_set_dai_fmt,
    207	.hw_params	= pcm1681_hw_params,
    208	.mute_stream	= pcm1681_mute,
    209	.no_capture_mute = 1,
    210};
    211
    212static const struct snd_soc_dapm_widget pcm1681_dapm_widgets[] = {
    213SND_SOC_DAPM_OUTPUT("VOUT1"),
    214SND_SOC_DAPM_OUTPUT("VOUT2"),
    215SND_SOC_DAPM_OUTPUT("VOUT3"),
    216SND_SOC_DAPM_OUTPUT("VOUT4"),
    217SND_SOC_DAPM_OUTPUT("VOUT5"),
    218SND_SOC_DAPM_OUTPUT("VOUT6"),
    219SND_SOC_DAPM_OUTPUT("VOUT7"),
    220SND_SOC_DAPM_OUTPUT("VOUT8"),
    221};
    222
    223static const struct snd_soc_dapm_route pcm1681_dapm_routes[] = {
    224	{ "VOUT1", NULL, "Playback" },
    225	{ "VOUT2", NULL, "Playback" },
    226	{ "VOUT3", NULL, "Playback" },
    227	{ "VOUT4", NULL, "Playback" },
    228	{ "VOUT5", NULL, "Playback" },
    229	{ "VOUT6", NULL, "Playback" },
    230	{ "VOUT7", NULL, "Playback" },
    231	{ "VOUT8", NULL, "Playback" },
    232};
    233
    234static const DECLARE_TLV_DB_SCALE(pcm1681_dac_tlv, -6350, 50, 1);
    235
    236static const struct snd_kcontrol_new pcm1681_controls[] = {
    237	SOC_DOUBLE_R_TLV("Channel 1/2 Playback Volume",
    238			PCM1681_ATT_CONTROL(1), PCM1681_ATT_CONTROL(2), 0,
    239			0x7f, 0, pcm1681_dac_tlv),
    240	SOC_DOUBLE_R_TLV("Channel 3/4 Playback Volume",
    241			PCM1681_ATT_CONTROL(3), PCM1681_ATT_CONTROL(4), 0,
    242			0x7f, 0, pcm1681_dac_tlv),
    243	SOC_DOUBLE_R_TLV("Channel 5/6 Playback Volume",
    244			PCM1681_ATT_CONTROL(5), PCM1681_ATT_CONTROL(6), 0,
    245			0x7f, 0, pcm1681_dac_tlv),
    246	SOC_DOUBLE_R_TLV("Channel 7/8 Playback Volume",
    247			PCM1681_ATT_CONTROL(7), PCM1681_ATT_CONTROL(8), 0,
    248			0x7f, 0, pcm1681_dac_tlv),
    249	SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0,
    250			    pcm1681_get_deemph, pcm1681_put_deemph),
    251};
    252
    253static struct snd_soc_dai_driver pcm1681_dai = {
    254	.name = "pcm1681-hifi",
    255	.playback = {
    256		.stream_name = "Playback",
    257		.channels_min = 2,
    258		.channels_max = 8,
    259		.rates = PCM1681_PCM_RATES,
    260		.formats = PCM1681_PCM_FORMATS,
    261	},
    262	.ops = &pcm1681_dai_ops,
    263};
    264
    265#ifdef CONFIG_OF
    266static const struct of_device_id pcm1681_dt_ids[] = {
    267	{ .compatible = "ti,pcm1681", },
    268	{ }
    269};
    270MODULE_DEVICE_TABLE(of, pcm1681_dt_ids);
    271#endif
    272
    273static const struct regmap_config pcm1681_regmap = {
    274	.reg_bits		= 8,
    275	.val_bits		= 8,
    276	.max_register		= 0x13,
    277	.reg_defaults		= pcm1681_reg_defaults,
    278	.num_reg_defaults	= ARRAY_SIZE(pcm1681_reg_defaults),
    279	.writeable_reg		= pcm1681_writeable_reg,
    280	.readable_reg		= pcm1681_accessible_reg,
    281};
    282
    283static const struct snd_soc_component_driver soc_component_dev_pcm1681 = {
    284	.controls		= pcm1681_controls,
    285	.num_controls		= ARRAY_SIZE(pcm1681_controls),
    286	.dapm_widgets		= pcm1681_dapm_widgets,
    287	.num_dapm_widgets	= ARRAY_SIZE(pcm1681_dapm_widgets),
    288	.dapm_routes		= pcm1681_dapm_routes,
    289	.num_dapm_routes	= ARRAY_SIZE(pcm1681_dapm_routes),
    290	.idle_bias_on		= 1,
    291	.use_pmdown_time	= 1,
    292	.endianness		= 1,
    293	.non_legacy_dai_naming	= 1,
    294};
    295
    296static const struct i2c_device_id pcm1681_i2c_id[] = {
    297	{"pcm1681", 0},
    298	{}
    299};
    300MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id);
    301
    302static int pcm1681_i2c_probe(struct i2c_client *client)
    303{
    304	int ret;
    305	struct pcm1681_private *priv;
    306
    307	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
    308	if (!priv)
    309		return -ENOMEM;
    310
    311	priv->regmap = devm_regmap_init_i2c(client, &pcm1681_regmap);
    312	if (IS_ERR(priv->regmap)) {
    313		ret = PTR_ERR(priv->regmap);
    314		dev_err(&client->dev, "Failed to create regmap: %d\n", ret);
    315		return ret;
    316	}
    317
    318	i2c_set_clientdata(client, priv);
    319
    320	return devm_snd_soc_register_component(&client->dev,
    321		&soc_component_dev_pcm1681,
    322		&pcm1681_dai, 1);
    323}
    324
    325static struct i2c_driver pcm1681_i2c_driver = {
    326	.driver = {
    327		.name	= "pcm1681",
    328		.of_match_table = of_match_ptr(pcm1681_dt_ids),
    329	},
    330	.id_table	= pcm1681_i2c_id,
    331	.probe_new	= pcm1681_i2c_probe,
    332};
    333
    334module_i2c_driver(pcm1681_i2c_driver);
    335
    336MODULE_DESCRIPTION("Texas Instruments PCM1681 ALSA SoC Codec Driver");
    337MODULE_AUTHOR("Marek Belisko <marek.belisko@streamunlimited.com>");
    338MODULE_LICENSE("GPL");