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

pcm1789.c (6881B)


      1// SPDX-License-Identifier: GPL-2.0
      2// Audio driver for PCM1789
      3// Copyright (C) 2018 Bootlin
      4// Mylène Josserand <mylene.josserand@bootlin.com>
      5
      6#include <linux/gpio/consumer.h>
      7#include <linux/module.h>
      8#include <linux/workqueue.h>
      9
     10#include <sound/pcm_params.h>
     11#include <sound/soc.h>
     12#include <sound/tlv.h>
     13
     14#include "pcm1789.h"
     15
     16#define PCM1789_MUTE_CONTROL	0x10
     17#define PCM1789_FMT_CONTROL	0x11
     18#define PCM1789_SOFT_MUTE	0x14
     19#define PCM1789_DAC_VOL_LEFT	0x18
     20#define PCM1789_DAC_VOL_RIGHT	0x19
     21
     22#define PCM1789_FMT_MASK	0x07
     23#define PCM1789_MUTE_MASK	0x03
     24#define PCM1789_MUTE_SRET	0x06
     25
     26struct pcm1789_private {
     27	struct regmap *regmap;
     28	unsigned int format;
     29	unsigned int rate;
     30	struct gpio_desc *reset;
     31	struct work_struct work;
     32	struct device *dev;
     33};
     34
     35static const struct reg_default pcm1789_reg_defaults[] = {
     36	{ PCM1789_FMT_CONTROL, 0x00 },
     37	{ PCM1789_SOFT_MUTE, 0x00 },
     38	{ PCM1789_DAC_VOL_LEFT, 0xff },
     39	{ PCM1789_DAC_VOL_RIGHT, 0xff },
     40};
     41
     42static bool pcm1789_accessible_reg(struct device *dev, unsigned int reg)
     43{
     44	return reg >= PCM1789_MUTE_CONTROL && reg <= PCM1789_DAC_VOL_RIGHT;
     45}
     46
     47static bool pcm1789_writeable_reg(struct device *dev, unsigned int reg)
     48{
     49	return pcm1789_accessible_reg(dev, reg);
     50}
     51
     52static int pcm1789_set_dai_fmt(struct snd_soc_dai *codec_dai,
     53			       unsigned int format)
     54{
     55	struct snd_soc_component *component = codec_dai->component;
     56	struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);
     57
     58	priv->format = format;
     59
     60	return 0;
     61}
     62
     63static int pcm1789_mute(struct snd_soc_dai *codec_dai, int mute, int direction)
     64{
     65	struct snd_soc_component *component = codec_dai->component;
     66	struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);
     67
     68	return regmap_update_bits(priv->regmap, PCM1789_SOFT_MUTE,
     69				  PCM1789_MUTE_MASK,
     70				  mute ? 0 : PCM1789_MUTE_MASK);
     71}
     72
     73static int pcm1789_hw_params(struct snd_pcm_substream *substream,
     74			     struct snd_pcm_hw_params *params,
     75			     struct snd_soc_dai *codec_dai)
     76{
     77	struct snd_soc_component *component = codec_dai->component;
     78	struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);
     79	int val = 0, ret;
     80
     81	priv->rate = params_rate(params);
     82
     83	switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
     84	case SND_SOC_DAIFMT_RIGHT_J:
     85		switch (params_width(params)) {
     86		case 24:
     87			val = 2;
     88			break;
     89		case 16:
     90			val = 3;
     91			break;
     92		default:
     93			return -EINVAL;
     94		}
     95		break;
     96	case SND_SOC_DAIFMT_I2S:
     97		switch (params_width(params)) {
     98		case 16:
     99		case 24:
    100		case 32:
    101			val = 0;
    102			break;
    103		default:
    104			return -EINVAL;
    105		}
    106		break;
    107	case SND_SOC_DAIFMT_LEFT_J:
    108		switch (params_width(params)) {
    109		case 16:
    110		case 24:
    111		case 32:
    112			val = 1;
    113			break;
    114		default:
    115			return -EINVAL;
    116		}
    117		break;
    118	default:
    119		dev_err(component->dev, "Invalid DAI format\n");
    120		return -EINVAL;
    121	}
    122
    123	ret = regmap_update_bits(priv->regmap, PCM1789_FMT_CONTROL,
    124				 PCM1789_FMT_MASK, val);
    125	if (ret < 0)
    126		return ret;
    127
    128	return 0;
    129}
    130
    131static void pcm1789_work_queue(struct work_struct *work)
    132{
    133	struct pcm1789_private *priv = container_of(work,
    134						    struct pcm1789_private,
    135						    work);
    136
    137	/* Perform a software reset to remove codec from desynchronized state */
    138	if (regmap_update_bits(priv->regmap, PCM1789_MUTE_CONTROL,
    139			       0x3 << PCM1789_MUTE_SRET, 0) < 0)
    140		dev_err(priv->dev, "Error while setting SRET");
    141}
    142
    143static int pcm1789_trigger(struct snd_pcm_substream *substream, int cmd,
    144			   struct snd_soc_dai *dai)
    145{
    146	struct snd_soc_component *component = dai->component;
    147	struct pcm1789_private *priv = snd_soc_component_get_drvdata(component);
    148	int ret = 0;
    149
    150	switch (cmd) {
    151	case SNDRV_PCM_TRIGGER_START:
    152	case SNDRV_PCM_TRIGGER_RESUME:
    153	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    154		schedule_work(&priv->work);
    155		break;
    156	case SNDRV_PCM_TRIGGER_STOP:
    157	case SNDRV_PCM_TRIGGER_SUSPEND:
    158	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    159		break;
    160	default:
    161		ret = -EINVAL;
    162	}
    163
    164	return ret;
    165}
    166
    167static const struct snd_soc_dai_ops pcm1789_dai_ops = {
    168	.set_fmt	= pcm1789_set_dai_fmt,
    169	.hw_params	= pcm1789_hw_params,
    170	.mute_stream	= pcm1789_mute,
    171	.trigger	= pcm1789_trigger,
    172	.no_capture_mute = 1,
    173};
    174
    175static const DECLARE_TLV_DB_SCALE(pcm1789_dac_tlv, -12000, 50, 1);
    176
    177static const struct snd_kcontrol_new pcm1789_controls[] = {
    178	SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1789_DAC_VOL_LEFT,
    179			       PCM1789_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
    180			       pcm1789_dac_tlv),
    181};
    182
    183static const struct snd_soc_dapm_widget pcm1789_dapm_widgets[] = {
    184	SND_SOC_DAPM_OUTPUT("IOUTL+"),
    185	SND_SOC_DAPM_OUTPUT("IOUTL-"),
    186	SND_SOC_DAPM_OUTPUT("IOUTR+"),
    187	SND_SOC_DAPM_OUTPUT("IOUTR-"),
    188};
    189
    190static const struct snd_soc_dapm_route pcm1789_dapm_routes[] = {
    191	{ "IOUTL+", NULL, "Playback" },
    192	{ "IOUTL-", NULL, "Playback" },
    193	{ "IOUTR+", NULL, "Playback" },
    194	{ "IOUTR-", NULL, "Playback" },
    195};
    196
    197static struct snd_soc_dai_driver pcm1789_dai = {
    198	.name = "pcm1789-hifi",
    199	.playback = {
    200		.stream_name = "Playback",
    201		.channels_min = 2,
    202		.channels_max = 2,
    203		.rates = SNDRV_PCM_RATE_CONTINUOUS,
    204		.rate_min = 10000,
    205		.rate_max = 200000,
    206		.formats = PCM1789_FORMATS,
    207	},
    208	.ops = &pcm1789_dai_ops,
    209};
    210
    211const struct regmap_config pcm1789_regmap_config = {
    212	.reg_bits		= 8,
    213	.val_bits		= 8,
    214	.max_register		= PCM1789_DAC_VOL_RIGHT,
    215	.reg_defaults		= pcm1789_reg_defaults,
    216	.num_reg_defaults	= ARRAY_SIZE(pcm1789_reg_defaults),
    217	.writeable_reg		= pcm1789_writeable_reg,
    218	.readable_reg		= pcm1789_accessible_reg,
    219};
    220EXPORT_SYMBOL_GPL(pcm1789_regmap_config);
    221
    222static const struct snd_soc_component_driver soc_component_dev_pcm1789 = {
    223	.controls		= pcm1789_controls,
    224	.num_controls		= ARRAY_SIZE(pcm1789_controls),
    225	.dapm_widgets		= pcm1789_dapm_widgets,
    226	.num_dapm_widgets	= ARRAY_SIZE(pcm1789_dapm_widgets),
    227	.dapm_routes		= pcm1789_dapm_routes,
    228	.num_dapm_routes	= ARRAY_SIZE(pcm1789_dapm_routes),
    229	.idle_bias_on		= 1,
    230	.use_pmdown_time	= 1,
    231	.endianness		= 1,
    232	.non_legacy_dai_naming	= 1,
    233};
    234
    235int pcm1789_common_init(struct device *dev, struct regmap *regmap)
    236{
    237	struct pcm1789_private *pcm1789;
    238
    239	pcm1789 = devm_kzalloc(dev, sizeof(struct pcm1789_private),
    240			       GFP_KERNEL);
    241	if (!pcm1789)
    242		return -ENOMEM;
    243
    244	pcm1789->regmap = regmap;
    245	pcm1789->dev = dev;
    246	dev_set_drvdata(dev, pcm1789);
    247
    248	pcm1789->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
    249	if (IS_ERR(pcm1789->reset))
    250		return PTR_ERR(pcm1789->reset);
    251
    252	gpiod_set_value_cansleep(pcm1789->reset, 0);
    253	msleep(300);
    254
    255	INIT_WORK(&pcm1789->work, pcm1789_work_queue);
    256
    257	return devm_snd_soc_register_component(dev, &soc_component_dev_pcm1789,
    258					       &pcm1789_dai, 1);
    259}
    260EXPORT_SYMBOL_GPL(pcm1789_common_init);
    261
    262void pcm1789_common_exit(struct device *dev)
    263{
    264	struct pcm1789_private *priv = dev_get_drvdata(dev);
    265
    266	flush_work(&priv->work);
    267}
    268EXPORT_SYMBOL_GPL(pcm1789_common_exit);
    269
    270MODULE_DESCRIPTION("ASoC PCM1789 driver");
    271MODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>");
    272MODULE_LICENSE("GPL");