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