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

si476x.c (6418B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * sound/soc/codecs/si476x.c -- Codec driver for SI476X chips
      4 *
      5 * Copyright (C) 2012 Innovative Converged Devices(ICD)
      6 * Copyright (C) 2013 Andrey Smirnov
      7 *
      8 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
      9 */
     10
     11#include <linux/module.h>
     12#include <linux/slab.h>
     13#include <sound/pcm.h>
     14#include <sound/pcm_params.h>
     15#include <linux/regmap.h>
     16#include <sound/soc.h>
     17#include <sound/initval.h>
     18
     19#include <linux/i2c.h>
     20
     21#include <linux/mfd/si476x-core.h>
     22
     23enum si476x_audio_registers {
     24	SI476X_DIGITAL_IO_OUTPUT_FORMAT		= 0x0203,
     25	SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE	= 0x0202,
     26};
     27
     28enum si476x_digital_io_output_format {
     29	SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT	= 11,
     30	SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT	= 8,
     31};
     32
     33#define SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK	((0x7 << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | \
     34						  (0x7 << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT))
     35#define SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK	(0x7e)
     36
     37enum si476x_daudio_formats {
     38	SI476X_DAUDIO_MODE_I2S		= (0x0 << 1),
     39	SI476X_DAUDIO_MODE_DSP_A	= (0x6 << 1),
     40	SI476X_DAUDIO_MODE_DSP_B	= (0x7 << 1),
     41	SI476X_DAUDIO_MODE_LEFT_J	= (0x8 << 1),
     42	SI476X_DAUDIO_MODE_RIGHT_J	= (0x9 << 1),
     43
     44	SI476X_DAUDIO_MODE_IB		= (1 << 5),
     45	SI476X_DAUDIO_MODE_IF		= (1 << 6),
     46};
     47
     48enum si476x_pcm_format {
     49	SI476X_PCM_FORMAT_S8		= 2,
     50	SI476X_PCM_FORMAT_S16_LE	= 4,
     51	SI476X_PCM_FORMAT_S20_3LE	= 5,
     52	SI476X_PCM_FORMAT_S24_LE	= 6,
     53};
     54
     55static const struct snd_soc_dapm_widget si476x_dapm_widgets[] = {
     56SND_SOC_DAPM_OUTPUT("LOUT"),
     57SND_SOC_DAPM_OUTPUT("ROUT"),
     58};
     59
     60static const struct snd_soc_dapm_route si476x_dapm_routes[] = {
     61	{ "Capture", NULL, "LOUT" },
     62	{ "Capture", NULL, "ROUT" },
     63};
     64
     65static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
     66				    unsigned int fmt)
     67{
     68	struct si476x_core *core = i2c_mfd_cell_to_core(codec_dai->dev);
     69	int err;
     70	u16 format = 0;
     71
     72	if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC)
     73		return -EINVAL;
     74
     75	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
     76	case SND_SOC_DAIFMT_DSP_A:
     77		format |= SI476X_DAUDIO_MODE_DSP_A;
     78		break;
     79	case SND_SOC_DAIFMT_DSP_B:
     80		format |= SI476X_DAUDIO_MODE_DSP_B;
     81		break;
     82	case SND_SOC_DAIFMT_I2S:
     83		format |= SI476X_DAUDIO_MODE_I2S;
     84		break;
     85	case SND_SOC_DAIFMT_RIGHT_J:
     86		format |= SI476X_DAUDIO_MODE_RIGHT_J;
     87		break;
     88	case SND_SOC_DAIFMT_LEFT_J:
     89		format |= SI476X_DAUDIO_MODE_LEFT_J;
     90		break;
     91	default:
     92		return -EINVAL;
     93	}
     94
     95	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
     96	case SND_SOC_DAIFMT_DSP_A:
     97	case SND_SOC_DAIFMT_DSP_B:
     98		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
     99		case SND_SOC_DAIFMT_NB_NF:
    100			break;
    101		case SND_SOC_DAIFMT_IB_NF:
    102			format |= SI476X_DAUDIO_MODE_IB;
    103			break;
    104		default:
    105			return -EINVAL;
    106		}
    107		break;
    108	case SND_SOC_DAIFMT_I2S:
    109	case SND_SOC_DAIFMT_RIGHT_J:
    110	case SND_SOC_DAIFMT_LEFT_J:
    111		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
    112		case SND_SOC_DAIFMT_NB_NF:
    113			break;
    114		case SND_SOC_DAIFMT_IB_IF:
    115			format |= SI476X_DAUDIO_MODE_IB |
    116				SI476X_DAUDIO_MODE_IF;
    117			break;
    118		case SND_SOC_DAIFMT_IB_NF:
    119			format |= SI476X_DAUDIO_MODE_IB;
    120			break;
    121		case SND_SOC_DAIFMT_NB_IF:
    122			format |= SI476X_DAUDIO_MODE_IF;
    123			break;
    124		default:
    125			return -EINVAL;
    126		}
    127		break;
    128	default:
    129		return -EINVAL;
    130	}
    131
    132	si476x_core_lock(core);
    133
    134	err = snd_soc_component_update_bits(codec_dai->component, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
    135				  SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK,
    136				  format);
    137
    138	si476x_core_unlock(core);
    139
    140	if (err < 0) {
    141		dev_err(codec_dai->component->dev, "Failed to set output format\n");
    142		return err;
    143	}
    144
    145	return 0;
    146}
    147
    148static int si476x_codec_hw_params(struct snd_pcm_substream *substream,
    149				  struct snd_pcm_hw_params *params,
    150				  struct snd_soc_dai *dai)
    151{
    152	struct si476x_core *core = i2c_mfd_cell_to_core(dai->dev);
    153	int rate, width, err;
    154
    155	rate = params_rate(params);
    156	if (rate < 32000 || rate > 48000) {
    157		dev_err(dai->component->dev, "Rate: %d is not supported\n", rate);
    158		return -EINVAL;
    159	}
    160
    161	switch (params_width(params)) {
    162	case 8:
    163		width = SI476X_PCM_FORMAT_S8;
    164		break;
    165	case 16:
    166		width = SI476X_PCM_FORMAT_S16_LE;
    167		break;
    168	case 20:
    169		width = SI476X_PCM_FORMAT_S20_3LE;
    170		break;
    171	case 24:
    172		width = SI476X_PCM_FORMAT_S24_LE;
    173		break;
    174	default:
    175		return -EINVAL;
    176	}
    177
    178	si476x_core_lock(core);
    179
    180	err = snd_soc_component_write(dai->component, SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE,
    181			    rate);
    182	if (err < 0) {
    183		dev_err(dai->component->dev, "Failed to set sample rate\n");
    184		goto out;
    185	}
    186
    187	err = snd_soc_component_update_bits(dai->component, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
    188				  SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK,
    189				  (width << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) |
    190				  (width << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT));
    191	if (err < 0) {
    192		dev_err(dai->component->dev, "Failed to set output width\n");
    193		goto out;
    194	}
    195
    196out:
    197	si476x_core_unlock(core);
    198
    199	return err;
    200}
    201
    202static const struct snd_soc_dai_ops si476x_dai_ops = {
    203	.hw_params	= si476x_codec_hw_params,
    204	.set_fmt	= si476x_codec_set_dai_fmt,
    205};
    206
    207static struct snd_soc_dai_driver si476x_dai = {
    208	.name		= "si476x-codec",
    209	.capture	= {
    210		.stream_name	= "Capture",
    211		.channels_min	= 2,
    212		.channels_max	= 2,
    213
    214		.rates = SNDRV_PCM_RATE_32000 |
    215		SNDRV_PCM_RATE_44100 |
    216		SNDRV_PCM_RATE_48000,
    217		.formats = SNDRV_PCM_FMTBIT_S8 |
    218		SNDRV_PCM_FMTBIT_S16_LE |
    219		SNDRV_PCM_FMTBIT_S20_3LE |
    220		SNDRV_PCM_FMTBIT_S24_LE
    221	},
    222	.ops		= &si476x_dai_ops,
    223};
    224
    225static int si476x_probe(struct snd_soc_component *component)
    226{
    227	snd_soc_component_init_regmap(component,
    228				dev_get_regmap(component->dev->parent, NULL));
    229
    230	return 0;
    231}
    232
    233static const struct snd_soc_component_driver soc_component_dev_si476x = {
    234	.probe			= si476x_probe,
    235	.dapm_widgets		= si476x_dapm_widgets,
    236	.num_dapm_widgets	= ARRAY_SIZE(si476x_dapm_widgets),
    237	.dapm_routes		= si476x_dapm_routes,
    238	.num_dapm_routes	= ARRAY_SIZE(si476x_dapm_routes),
    239	.idle_bias_on		= 1,
    240	.use_pmdown_time	= 1,
    241	.endianness		= 1,
    242	.non_legacy_dai_naming	= 1,
    243};
    244
    245static int si476x_platform_probe(struct platform_device *pdev)
    246{
    247	return devm_snd_soc_register_component(&pdev->dev,
    248				      &soc_component_dev_si476x,
    249				      &si476x_dai, 1);
    250}
    251
    252MODULE_ALIAS("platform:si476x-codec");
    253
    254static struct platform_driver si476x_platform_driver = {
    255	.driver		= {
    256		.name	= "si476x-codec",
    257	},
    258	.probe		= si476x_platform_probe,
    259};
    260module_platform_driver(si476x_platform_driver);
    261
    262MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
    263MODULE_DESCRIPTION("ASoC Si4761/64 codec driver");
    264MODULE_LICENSE("GPL");