brintos

brintos / linux-shallow public Read only

0
0
Text · 6.2 KiB · d87141b Raw
264 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * sound/soc/codecs/si476x.c -- Codec driver for SI476X chips4 *5 * Copyright (C) 2012 Innovative Converged Devices(ICD)6 * Copyright (C) 2013 Andrey Smirnov7 *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_LE221	},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};243 244static int si476x_platform_probe(struct platform_device *pdev)245{246	return devm_snd_soc_register_component(&pdev->dev,247				      &soc_component_dev_si476x,248				      &si476x_dai, 1);249}250 251MODULE_ALIAS("platform:si476x-codec");252 253static struct platform_driver si476x_platform_driver = {254	.driver		= {255		.name	= "si476x-codec",256	},257	.probe		= si476x_platform_probe,258};259module_platform_driver(si476x_platform_driver);260 261MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");262MODULE_DESCRIPTION("ASoC Si4761/64 codec driver");263MODULE_LICENSE("GPL");264