brintos

brintos / linux-shallow public Read only

0
0
Text · 8.7 KiB · d87aae3 Raw
353 lines · c
1/* SPDX-License-Identifier: GPL-2.0+ */2/*3 *  Cirrus Logic CS4341A ALSA SoC Codec Driver4 *  Author: Alexander Shiyan <shc_work@mail.ru>5 */6 7#include <linux/i2c.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/regmap.h>11#include <linux/spi/spi.h>12 13#include <sound/pcm.h>14#include <sound/pcm_params.h>15#include <sound/soc.h>16#include <sound/tlv.h>17 18#define CS4341_REG_MODE1	0x0019#define CS4341_REG_MODE2	0x0120#define CS4341_REG_MIX		0x0221#define CS4341_REG_VOLA		0x0322#define CS4341_REG_VOLB		0x0423 24#define CS4341_MODE2_DIF	(7 << 4)25#define CS4341_MODE2_DIF_I2S_24	(0 << 4)26#define CS4341_MODE2_DIF_I2S_16	(1 << 4)27#define CS4341_MODE2_DIF_LJ_24	(2 << 4)28#define CS4341_MODE2_DIF_RJ_24	(3 << 4)29#define CS4341_MODE2_DIF_RJ_16	(5 << 4)30#define CS4341_VOLX_MUTE	(1 << 7)31 32struct cs4341_priv {33	unsigned int		fmt;34	struct regmap		*regmap;35	struct regmap_config	regcfg;36};37 38static const struct reg_default cs4341_reg_defaults[] = {39	{ CS4341_REG_MODE1,	0x00 },40	{ CS4341_REG_MODE2,	0x82 },41	{ CS4341_REG_MIX,	0x49 },42	{ CS4341_REG_VOLA,	0x80 },43	{ CS4341_REG_VOLB,	0x80 },44};45 46static int cs4341_set_fmt(struct snd_soc_dai *dai, unsigned int format)47{48	struct snd_soc_component *component = dai->component;49	struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component);50 51	switch (format & SND_SOC_DAIFMT_MASTER_MASK) {52	case SND_SOC_DAIFMT_CBS_CFS:53		break;54	default:55		return -EINVAL;56	}57 58	switch (format & SND_SOC_DAIFMT_INV_MASK) {59	case SND_SOC_DAIFMT_NB_NF:60		break;61	default:62		return -EINVAL;63	}64 65	switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {66	case SND_SOC_DAIFMT_I2S:67	case SND_SOC_DAIFMT_LEFT_J:68	case SND_SOC_DAIFMT_RIGHT_J:69		cs4341->fmt = format & SND_SOC_DAIFMT_FORMAT_MASK;70		break;71	default:72		return -EINVAL;73	}74 75	return 0;76}77 78static int cs4341_hw_params(struct snd_pcm_substream *substream,79			    struct snd_pcm_hw_params *params,80			    struct snd_soc_dai *dai)81{82	struct snd_soc_component *component = dai->component;83	struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component);84	unsigned int mode = 0;85	int b24 = 0;86 87	switch (params_format(params)) {88	case SNDRV_PCM_FORMAT_S24_LE:89		b24 = 1;90		break;91	case SNDRV_PCM_FORMAT_S16_LE:92		break;93	default:94		dev_err(component->dev, "Unsupported PCM format 0x%08x.\n",95			params_format(params));96		return -EINVAL;97	}98 99	switch (cs4341->fmt) {100	case SND_SOC_DAIFMT_I2S:101		mode = b24 ? CS4341_MODE2_DIF_I2S_24 : CS4341_MODE2_DIF_I2S_16;102		break;103	case SND_SOC_DAIFMT_LEFT_J:104		mode = CS4341_MODE2_DIF_LJ_24;105		break;106	case SND_SOC_DAIFMT_RIGHT_J:107		mode = b24 ? CS4341_MODE2_DIF_RJ_24 : CS4341_MODE2_DIF_RJ_16;108		break;109	default:110		dev_err(component->dev, "Unsupported DAI format 0x%08x.\n",111			cs4341->fmt);112		return -EINVAL;113	}114 115	return snd_soc_component_update_bits(component, CS4341_REG_MODE2,116					     CS4341_MODE2_DIF, mode);117}118 119static int cs4341_mute(struct snd_soc_dai *dai, int mute, int direction)120{121	struct snd_soc_component *component = dai->component;122	int ret;123 124	ret = snd_soc_component_update_bits(component, CS4341_REG_VOLA,125					    CS4341_VOLX_MUTE,126					    mute ? CS4341_VOLX_MUTE : 0);127	if (ret < 0)128		return ret;129 130	return snd_soc_component_update_bits(component, CS4341_REG_VOLB,131					     CS4341_VOLX_MUTE,132					     mute ? CS4341_VOLX_MUTE : 0);133}134 135static DECLARE_TLV_DB_SCALE(out_tlv, -9000, 100, 0);136 137static const char * const deemph[] = {138	"None", "44.1k", "48k", "32k",139};140 141static const struct soc_enum deemph_enum =142	SOC_ENUM_SINGLE(CS4341_REG_MODE2, 2, 4, deemph);143 144static const char * const srzc[] = {145	"Immediate", "Zero Cross", "Soft Ramp", "SR on ZC",146};147 148static const struct soc_enum srzc_enum =149	SOC_ENUM_SINGLE(CS4341_REG_MIX, 5, 4, srzc);150 151 152static const struct snd_soc_dapm_widget cs4341_dapm_widgets[] = {153	SND_SOC_DAPM_DAC("HiFi DAC", NULL, SND_SOC_NOPM, 0, 0),154	SND_SOC_DAPM_OUTPUT("OutA"),155	SND_SOC_DAPM_OUTPUT("OutB"),156};157 158static const struct snd_soc_dapm_route cs4341_routes[] = {159	{ "OutA", NULL, "HiFi DAC" },160	{ "OutB", NULL, "HiFi DAC" },161	{ "DAC Playback", NULL, "OutA" },162	{ "DAC Playback", NULL, "OutB" },163};164 165static const struct snd_kcontrol_new cs4341_controls[] = {166	SOC_DOUBLE_R_TLV("Master Playback Volume",167			 CS4341_REG_VOLA, CS4341_REG_VOLB, 0, 90, 1, out_tlv),168	SOC_ENUM("De-Emphasis Control", deemph_enum),169	SOC_ENUM("Soft Ramp Zero Cross Control", srzc_enum),170	SOC_SINGLE("Auto-Mute Switch", CS4341_REG_MODE2, 7, 1, 0),171	SOC_SINGLE("Popguard Transient Switch", CS4341_REG_MODE2, 1, 1, 0),172};173 174static const struct snd_soc_dai_ops cs4341_dai_ops = {175	.set_fmt	= cs4341_set_fmt,176	.hw_params	= cs4341_hw_params,177	.mute_stream	= cs4341_mute,178	.no_capture_mute = 1,179};180 181static struct snd_soc_dai_driver cs4341_dai = {182	.name			= "cs4341a-hifi",183	.playback		= {184		.stream_name	= "DAC Playback",185		.channels_min	= 1,186		.channels_max	= 2,187		.rates		= SNDRV_PCM_RATE_8000_96000,188		.formats	= SNDRV_PCM_FMTBIT_S16_LE |189				  SNDRV_PCM_FMTBIT_S24_LE,190	},191	.ops			= &cs4341_dai_ops,192	.symmetric_rate		= 1,193};194 195static const struct snd_soc_component_driver soc_component_cs4341 = {196	.controls		= cs4341_controls,197	.num_controls		= ARRAY_SIZE(cs4341_controls),198	.dapm_widgets		= cs4341_dapm_widgets,199	.num_dapm_widgets	= ARRAY_SIZE(cs4341_dapm_widgets),200	.dapm_routes		= cs4341_routes,201	.num_dapm_routes	= ARRAY_SIZE(cs4341_routes),202	.idle_bias_on		= 1,203	.use_pmdown_time	= 1,204	.endianness		= 1,205};206 207static const struct of_device_id __maybe_unused cs4341_dt_ids[] = {208	{ .compatible = "cirrus,cs4341a", },209	{ }210};211MODULE_DEVICE_TABLE(of, cs4341_dt_ids);212 213static int cs4341_probe(struct device *dev)214{215	struct cs4341_priv *cs4341 = dev_get_drvdata(dev);216	int i;217 218	for (i = 0; i < ARRAY_SIZE(cs4341_reg_defaults); i++)219		regmap_write(cs4341->regmap, cs4341_reg_defaults[i].reg,220			     cs4341_reg_defaults[i].def);221 222	return devm_snd_soc_register_component(dev, &soc_component_cs4341,223					       &cs4341_dai, 1);224}225 226#if IS_ENABLED(CONFIG_I2C)227static int cs4341_i2c_probe(struct i2c_client *i2c)228{229	struct cs4341_priv *cs4341;230 231	cs4341 = devm_kzalloc(&i2c->dev, sizeof(*cs4341), GFP_KERNEL);232	if (!cs4341)233		return -ENOMEM;234 235	i2c_set_clientdata(i2c, cs4341);236 237	cs4341->regcfg.reg_bits		= 8;238	cs4341->regcfg.val_bits		= 8;239	cs4341->regcfg.max_register	= CS4341_REG_VOLB;240	cs4341->regcfg.cache_type	= REGCACHE_FLAT;241	cs4341->regcfg.reg_defaults	= cs4341_reg_defaults;242	cs4341->regcfg.num_reg_defaults	= ARRAY_SIZE(cs4341_reg_defaults);243	cs4341->regmap = devm_regmap_init_i2c(i2c, &cs4341->regcfg);244	if (IS_ERR(cs4341->regmap))245		return PTR_ERR(cs4341->regmap);246 247	return cs4341_probe(&i2c->dev);248}249 250static const struct i2c_device_id cs4341_i2c_id[] = {251	{ "cs4341" },252	{ }253};254MODULE_DEVICE_TABLE(i2c, cs4341_i2c_id);255 256static struct i2c_driver cs4341_i2c_driver = {257	.driver = {258		.name = "cs4341-i2c",259		.of_match_table = of_match_ptr(cs4341_dt_ids),260	},261	.probe = cs4341_i2c_probe,262	.id_table = cs4341_i2c_id,263};264#endif265 266#if defined(CONFIG_SPI_MASTER)267static bool cs4341_reg_readable(struct device *dev, unsigned int reg)268{269	return false;270}271 272static int cs4341_spi_probe(struct spi_device *spi)273{274	struct cs4341_priv *cs4341;275	int ret;276 277	cs4341 = devm_kzalloc(&spi->dev, sizeof(*cs4341), GFP_KERNEL);278	if (!cs4341)279		return -ENOMEM;280 281	if (!spi->bits_per_word)282		spi->bits_per_word = 8;283	if (!spi->max_speed_hz)284		spi->max_speed_hz = 6000000;285	ret = spi_setup(spi);286	if (ret)287		return ret;288 289	spi_set_drvdata(spi, cs4341);290 291	cs4341->regcfg.reg_bits		= 16;292	cs4341->regcfg.val_bits		= 8;293	cs4341->regcfg.write_flag_mask	= 0x20;294	cs4341->regcfg.max_register	= CS4341_REG_VOLB;295	cs4341->regcfg.cache_type	= REGCACHE_FLAT;296	cs4341->regcfg.readable_reg	= cs4341_reg_readable;297	cs4341->regcfg.reg_defaults	= cs4341_reg_defaults;298	cs4341->regcfg.num_reg_defaults	= ARRAY_SIZE(cs4341_reg_defaults);299	cs4341->regmap = devm_regmap_init_spi(spi, &cs4341->regcfg);300	if (IS_ERR(cs4341->regmap))301		return PTR_ERR(cs4341->regmap);302 303	return cs4341_probe(&spi->dev);304}305 306static const struct spi_device_id cs4341_spi_ids[] = {307	{ "cs4341a" },308	{ }309};310MODULE_DEVICE_TABLE(spi, cs4341_spi_ids);311 312static struct spi_driver cs4341_spi_driver = {313	.driver = {314		.name = "cs4341-spi",315		.of_match_table = of_match_ptr(cs4341_dt_ids),316	},317	.probe = cs4341_spi_probe,318	.id_table = cs4341_spi_ids,319};320#endif321 322static int __init cs4341_init(void)323{324	int ret = 0;325 326#if IS_ENABLED(CONFIG_I2C)327	ret = i2c_add_driver(&cs4341_i2c_driver);328	if (ret)329		return ret;330#endif331#if defined(CONFIG_SPI_MASTER)332	ret = spi_register_driver(&cs4341_spi_driver);333#endif334 335	return ret;336}337module_init(cs4341_init);338 339static void __exit cs4341_exit(void)340{341#if IS_ENABLED(CONFIG_I2C)342	i2c_del_driver(&cs4341_i2c_driver);343#endif344#if defined(CONFIG_SPI_MASTER)345	spi_unregister_driver(&cs4341_spi_driver);346#endif347}348module_exit(cs4341_exit);349 350MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");351MODULE_DESCRIPTION("Cirrus Logic CS4341 ALSA SoC Codec Driver");352MODULE_LICENSE("GPL");353