brintos

brintos / linux-shallow public Read only

0
0
Text · 11.1 KiB · 23e868e Raw
417 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * ak4118.c  --  Asahi Kasei ALSA Soc Audio driver4 *5 * Copyright 2018 DEVIALET6 */7 8#include <linux/i2c.h>9#include <linux/gpio/consumer.h>10#include <linux/module.h>11#include <linux/of.h>12#include <linux/regmap.h>13#include <linux/slab.h>14 15#include <sound/asoundef.h>16#include <sound/core.h>17#include <sound/initval.h>18#include <sound/soc.h>19 20#define AK4118_REG_CLK_PWR_CTL		0x0021#define AK4118_REG_FORMAT_CTL		0x0122#define AK4118_REG_IO_CTL0		0x0223#define AK4118_REG_IO_CTL1		0x0324#define AK4118_REG_INT0_MASK		0x0425#define AK4118_REG_INT1_MASK		0x0526#define AK4118_REG_RCV_STATUS0		0x0627#define AK4118_REG_RCV_STATUS1		0x0728#define AK4118_REG_RXCHAN_STATUS0	0x0829#define AK4118_REG_RXCHAN_STATUS1	0x0930#define AK4118_REG_RXCHAN_STATUS2	0x0a31#define AK4118_REG_RXCHAN_STATUS3	0x0b32#define AK4118_REG_RXCHAN_STATUS4	0x0c33#define AK4118_REG_TXCHAN_STATUS0	0x0d34#define AK4118_REG_TXCHAN_STATUS1	0x0e35#define AK4118_REG_TXCHAN_STATUS2	0x0f36#define AK4118_REG_TXCHAN_STATUS3	0x1037#define AK4118_REG_TXCHAN_STATUS4	0x1138#define AK4118_REG_BURST_PREAMB_PC0	0x1239#define AK4118_REG_BURST_PREAMB_PC1	0x1340#define AK4118_REG_BURST_PREAMB_PD0	0x1441#define AK4118_REG_BURST_PREAMB_PD1	0x1542#define AK4118_REG_QSUB_CTL		0x1643#define AK4118_REG_QSUB_TRACK		0x1744#define AK4118_REG_QSUB_INDEX		0x1845#define AK4118_REG_QSUB_MIN		0x1946#define AK4118_REG_QSUB_SEC		0x1a47#define AK4118_REG_QSUB_FRAME		0x1b48#define AK4118_REG_QSUB_ZERO		0x1c49#define AK4118_REG_QSUB_ABS_MIN		0x1d50#define AK4118_REG_QSUB_ABS_SEC		0x1e51#define AK4118_REG_QSUB_ABS_FRAME	0x1f52#define AK4118_REG_GPE			0x2053#define AK4118_REG_GPDR			0x2154#define AK4118_REG_GPSCR		0x2255#define AK4118_REG_GPLR			0x2356#define AK4118_REG_DAT_MASK_DTS		0x2457#define AK4118_REG_RX_DETECT		0x2558#define AK4118_REG_STC_DAT_DETECT	0x2659#define AK4118_REG_RXCHAN_STATUS5	0x2760#define AK4118_REG_TXCHAN_STATUS5	0x2861#define AK4118_REG_MAX			0x2962 63#define AK4118_REG_FORMAT_CTL_DIF0	(1 << 4)64#define AK4118_REG_FORMAT_CTL_DIF1	(1 << 5)65#define AK4118_REG_FORMAT_CTL_DIF2	(1 << 6)66 67struct ak4118_priv {68	struct regmap *regmap;69	struct gpio_desc *reset;70	struct gpio_desc *irq;71	struct snd_soc_component *component;72};73 74static const struct reg_default ak4118_reg_defaults[] = {75	{AK4118_REG_CLK_PWR_CTL,	0x43},76	{AK4118_REG_FORMAT_CTL,		0x6a},77	{AK4118_REG_IO_CTL0,		0x88},78	{AK4118_REG_IO_CTL1,		0x48},79	{AK4118_REG_INT0_MASK,		0xee},80	{AK4118_REG_INT1_MASK,		0xb5},81	{AK4118_REG_RCV_STATUS0,	0x00},82	{AK4118_REG_RCV_STATUS1,	0x10},83	{AK4118_REG_TXCHAN_STATUS0,	0x00},84	{AK4118_REG_TXCHAN_STATUS1,	0x00},85	{AK4118_REG_TXCHAN_STATUS2,	0x00},86	{AK4118_REG_TXCHAN_STATUS3,	0x00},87	{AK4118_REG_TXCHAN_STATUS4,	0x00},88	{AK4118_REG_GPE,		0x77},89	{AK4118_REG_GPDR,		0x00},90	{AK4118_REG_GPSCR,		0x00},91	{AK4118_REG_GPLR,		0x00},92	{AK4118_REG_DAT_MASK_DTS,	0x3f},93	{AK4118_REG_RX_DETECT,		0x00},94	{AK4118_REG_STC_DAT_DETECT,	0x00},95	{AK4118_REG_TXCHAN_STATUS5,	0x00},96};97 98static const char * const ak4118_input_select_txt[] = {99	"RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7",100};101static SOC_ENUM_SINGLE_DECL(ak4118_insel_enum, AK4118_REG_IO_CTL1, 0x0,102			    ak4118_input_select_txt);103 104static const struct snd_kcontrol_new ak4118_input_mux_controls =105	SOC_DAPM_ENUM("Input Select", ak4118_insel_enum);106 107static const char * const ak4118_iec958_fs_txt[] = {108	"44100", "48000", "32000", "22050", "11025", "24000", "16000", "88200",109	"8000", "96000", "64000", "176400", "192000",110};111 112static const int ak4118_iec958_fs_val[] = {113	0x0, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xE,114};115 116static SOC_VALUE_ENUM_SINGLE_DECL(ak4118_iec958_fs_enum, AK4118_REG_RCV_STATUS1,117				  0x4, 0x4, ak4118_iec958_fs_txt,118				  ak4118_iec958_fs_val);119 120static struct snd_kcontrol_new ak4118_iec958_controls[] = {121	SOC_SINGLE("IEC958 Parity Errors", AK4118_REG_RCV_STATUS0, 0, 1, 0),122	SOC_SINGLE("IEC958 No Audio", AK4118_REG_RCV_STATUS0, 1, 1, 0),123	SOC_SINGLE("IEC958 PLL Lock", AK4118_REG_RCV_STATUS0, 4, 1, 1),124	SOC_SINGLE("IEC958 Non PCM", AK4118_REG_RCV_STATUS0, 6, 1, 0),125	SOC_ENUM("IEC958 Sampling Freq", ak4118_iec958_fs_enum),126};127 128static const struct snd_soc_dapm_widget ak4118_dapm_widgets[] = {129	SND_SOC_DAPM_INPUT("INRX0"),130	SND_SOC_DAPM_INPUT("INRX1"),131	SND_SOC_DAPM_INPUT("INRX2"),132	SND_SOC_DAPM_INPUT("INRX3"),133	SND_SOC_DAPM_INPUT("INRX4"),134	SND_SOC_DAPM_INPUT("INRX5"),135	SND_SOC_DAPM_INPUT("INRX6"),136	SND_SOC_DAPM_INPUT("INRX7"),137	SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,138			 &ak4118_input_mux_controls),139};140 141static const struct snd_soc_dapm_route ak4118_dapm_routes[] = {142	{"Input Mux", "RX0", "INRX0"},143	{"Input Mux", "RX1", "INRX1"},144	{"Input Mux", "RX2", "INRX2"},145	{"Input Mux", "RX3", "INRX3"},146	{"Input Mux", "RX4", "INRX4"},147	{"Input Mux", "RX5", "INRX5"},148	{"Input Mux", "RX6", "INRX6"},149	{"Input Mux", "RX7", "INRX7"},150};151 152 153static int ak4118_set_dai_fmt_provider(struct ak4118_priv *ak4118,154				       unsigned int format)155{156	int dif;157 158	switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {159	case SND_SOC_DAIFMT_I2S:160		dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF2;161		break;162	case SND_SOC_DAIFMT_RIGHT_J:163		dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF1;164		break;165	case SND_SOC_DAIFMT_LEFT_J:166		dif = AK4118_REG_FORMAT_CTL_DIF2;167		break;168	default:169		return -ENOTSUPP;170	}171 172	return dif;173}174 175static int ak4118_set_dai_fmt_consumer(struct ak4118_priv *ak4118,176				       unsigned int format)177{178	int dif;179 180	switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {181	case SND_SOC_DAIFMT_I2S:182		dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF1 |183		      AK4118_REG_FORMAT_CTL_DIF2;184		break;185	case SND_SOC_DAIFMT_LEFT_J:186		dif = AK4118_REG_FORMAT_CTL_DIF1 | AK4118_REG_FORMAT_CTL_DIF2;187		break;188	default:189		return -ENOTSUPP;190	}191 192	return dif;193}194 195static int ak4118_set_dai_fmt(struct snd_soc_dai *dai,196			      unsigned int format)197{198	struct snd_soc_component *component = dai->component;199	struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component);200	int dif;201	int ret = 0;202 203	switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {204	case SND_SOC_DAIFMT_CBP_CFP:205		dif = ak4118_set_dai_fmt_provider(ak4118, format);206		break;207	case SND_SOC_DAIFMT_CBC_CFC:208		dif = ak4118_set_dai_fmt_consumer(ak4118, format);209		break;210	default:211		ret = -ENOTSUPP;212		goto exit;213	}214 215	/* format not supported */216	if (dif < 0) {217		ret = dif;218		goto exit;219	}220 221	ret = regmap_update_bits(ak4118->regmap, AK4118_REG_FORMAT_CTL,222				 AK4118_REG_FORMAT_CTL_DIF0 |223				 AK4118_REG_FORMAT_CTL_DIF1 |224				 AK4118_REG_FORMAT_CTL_DIF2, dif);225	if (ret < 0)226		goto exit;227 228exit:229	return ret;230}231 232static int ak4118_hw_params(struct snd_pcm_substream *substream,233			    struct snd_pcm_hw_params *params,234			    struct snd_soc_dai *dai)235{236	return 0;237}238 239static const struct snd_soc_dai_ops ak4118_dai_ops = {240	.hw_params = ak4118_hw_params,241	.set_fmt   = ak4118_set_dai_fmt,242};243 244static struct snd_soc_dai_driver ak4118_dai = {245	.name = "ak4118-hifi",246	.capture = {247		.stream_name = "Capture",248		.channels_min = 2,249		.channels_max = 2,250		.rates = SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |251			 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |252			 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |253			 SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,254		.formats = SNDRV_PCM_FMTBIT_S16_LE  |255			   SNDRV_PCM_FMTBIT_S24_3LE |256			   SNDRV_PCM_FMTBIT_S24_LE257	},258	.ops = &ak4118_dai_ops,259};260 261static irqreturn_t ak4118_irq_handler(int irq, void *data)262{263	struct ak4118_priv *ak4118 = data;264	struct snd_soc_component *component = ak4118->component;265	struct snd_kcontrol_new *kctl_new;266	unsigned int i;267 268	if (!component)269		return IRQ_NONE;270 271	for (i = 0; i < ARRAY_SIZE(ak4118_iec958_controls); i++) {272		kctl_new = &ak4118_iec958_controls[i];273 274		snd_soc_component_notify_control(component, kctl_new->name);275	}276 277	return IRQ_HANDLED;278}279 280static int ak4118_probe(struct snd_soc_component *component)281{282	struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component);283	int ret = 0;284 285	ak4118->component = component;286 287	/* release reset */288	gpiod_set_value(ak4118->reset, 0);289 290	/* unmask all int1 sources */291	ret = regmap_write(ak4118->regmap, AK4118_REG_INT1_MASK, 0x00);292	if (ret < 0) {293		dev_err(component->dev,294			"failed to write regmap 0x%x 0x%x: %d\n",295			AK4118_REG_INT1_MASK, 0x00, ret);296		return ret;297	}298 299	/* rx detect enable on all channels */300	ret = regmap_write(ak4118->regmap, AK4118_REG_RX_DETECT, 0xff);301	if (ret < 0) {302		dev_err(component->dev,303			"failed to write regmap 0x%x 0x%x: %d\n",304			AK4118_REG_RX_DETECT, 0xff, ret);305		return ret;306	}307 308	ret = snd_soc_add_component_controls(component, ak4118_iec958_controls,309					 ARRAY_SIZE(ak4118_iec958_controls));310	if (ret) {311		dev_err(component->dev,312			"failed to add component kcontrols: %d\n", ret);313		return ret;314	}315 316	return 0;317}318 319static void ak4118_remove(struct snd_soc_component *component)320{321	struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component);322 323	/* hold reset */324	gpiod_set_value(ak4118->reset, 1);325}326 327static const struct snd_soc_component_driver soc_component_drv_ak4118 = {328	.probe			= ak4118_probe,329	.remove			= ak4118_remove,330	.dapm_widgets		= ak4118_dapm_widgets,331	.num_dapm_widgets	= ARRAY_SIZE(ak4118_dapm_widgets),332	.dapm_routes		= ak4118_dapm_routes,333	.num_dapm_routes	= ARRAY_SIZE(ak4118_dapm_routes),334	.idle_bias_on		= 1,335	.use_pmdown_time	= 1,336	.endianness		= 1,337};338 339static const struct regmap_config ak4118_regmap = {340	.reg_bits = 8,341	.val_bits = 8,342 343	.reg_defaults = ak4118_reg_defaults,344	.num_reg_defaults = ARRAY_SIZE(ak4118_reg_defaults),345 346	.cache_type = REGCACHE_NONE,347	.max_register = AK4118_REG_MAX - 1,348};349 350static int ak4118_i2c_probe(struct i2c_client *i2c)351{352	struct ak4118_priv *ak4118;353	int ret;354 355	ak4118 = devm_kzalloc(&i2c->dev, sizeof(struct ak4118_priv),356			      GFP_KERNEL);357	if (ak4118 == NULL)358		return -ENOMEM;359 360	ak4118->regmap = devm_regmap_init_i2c(i2c, &ak4118_regmap);361	if (IS_ERR(ak4118->regmap))362		return PTR_ERR(ak4118->regmap);363 364	i2c_set_clientdata(i2c, ak4118);365 366	ak4118->reset = devm_gpiod_get(&i2c->dev, "reset", GPIOD_OUT_HIGH);367	if (IS_ERR(ak4118->reset))368		return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->reset),369				     "Failed to get reset\n");370 371	ak4118->irq = devm_gpiod_get(&i2c->dev, "irq", GPIOD_IN);372	if (IS_ERR(ak4118->irq))373		return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->irq),374				     "Failed to get IRQ\n");375 376	ret = devm_request_threaded_irq(&i2c->dev, gpiod_to_irq(ak4118->irq),377					NULL, ak4118_irq_handler,378					IRQF_TRIGGER_RISING | IRQF_ONESHOT,379					"ak4118-irq", ak4118);380	if (ret < 0) {381		dev_err(&i2c->dev, "Fail to request_irq: %d\n", ret);382		return ret;383	}384 385	return devm_snd_soc_register_component(&i2c->dev,386				&soc_component_drv_ak4118, &ak4118_dai, 1);387}388 389#ifdef CONFIG_OF390static const struct of_device_id ak4118_of_match[] = {391	{ .compatible = "asahi-kasei,ak4118", },392	{}393};394MODULE_DEVICE_TABLE(of, ak4118_of_match);395#endif396 397static const struct i2c_device_id ak4118_id_table[] = {398	{ "ak4118" },399	{}400};401MODULE_DEVICE_TABLE(i2c, ak4118_id_table);402 403static struct i2c_driver ak4118_i2c_driver = {404	.driver  = {405		.name = "ak4118",406		.of_match_table = of_match_ptr(ak4118_of_match),407	},408	.id_table = ak4118_id_table,409	.probe = ak4118_i2c_probe,410};411 412module_i2c_driver(ak4118_i2c_driver);413 414MODULE_DESCRIPTION("Asahi Kasei AK4118 ALSA SoC driver");415MODULE_AUTHOR("Adrien Charruel <adrien.charruel@devialet.com>");416MODULE_LICENSE("GPL");417