162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// tfa9879.c -- driver for NXP Semiconductors TFA9879 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (C) 2014 Axentia Technologies AB 662306a36Sopenharmony_ci// Author: Peter Rosin <peda@axentia.se> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/i2c.h> 1162306a36Sopenharmony_ci#include <linux/regmap.h> 1262306a36Sopenharmony_ci#include <sound/soc.h> 1362306a36Sopenharmony_ci#include <sound/tlv.h> 1462306a36Sopenharmony_ci#include <sound/pcm_params.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "tfa9879.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct tfa9879_priv { 1962306a36Sopenharmony_ci struct regmap *regmap; 2062306a36Sopenharmony_ci int lsb_justified; 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int tfa9879_hw_params(struct snd_pcm_substream *substream, 2462306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 2562306a36Sopenharmony_ci struct snd_soc_dai *dai) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 2862306a36Sopenharmony_ci struct tfa9879_priv *tfa9879 = snd_soc_component_get_drvdata(component); 2962306a36Sopenharmony_ci int fs; 3062306a36Sopenharmony_ci int i2s_set = 0; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci switch (params_rate(params)) { 3362306a36Sopenharmony_ci case 8000: 3462306a36Sopenharmony_ci fs = TFA9879_I2S_FS_8000; 3562306a36Sopenharmony_ci break; 3662306a36Sopenharmony_ci case 11025: 3762306a36Sopenharmony_ci fs = TFA9879_I2S_FS_11025; 3862306a36Sopenharmony_ci break; 3962306a36Sopenharmony_ci case 12000: 4062306a36Sopenharmony_ci fs = TFA9879_I2S_FS_12000; 4162306a36Sopenharmony_ci break; 4262306a36Sopenharmony_ci case 16000: 4362306a36Sopenharmony_ci fs = TFA9879_I2S_FS_16000; 4462306a36Sopenharmony_ci break; 4562306a36Sopenharmony_ci case 22050: 4662306a36Sopenharmony_ci fs = TFA9879_I2S_FS_22050; 4762306a36Sopenharmony_ci break; 4862306a36Sopenharmony_ci case 24000: 4962306a36Sopenharmony_ci fs = TFA9879_I2S_FS_24000; 5062306a36Sopenharmony_ci break; 5162306a36Sopenharmony_ci case 32000: 5262306a36Sopenharmony_ci fs = TFA9879_I2S_FS_32000; 5362306a36Sopenharmony_ci break; 5462306a36Sopenharmony_ci case 44100: 5562306a36Sopenharmony_ci fs = TFA9879_I2S_FS_44100; 5662306a36Sopenharmony_ci break; 5762306a36Sopenharmony_ci case 48000: 5862306a36Sopenharmony_ci fs = TFA9879_I2S_FS_48000; 5962306a36Sopenharmony_ci break; 6062306a36Sopenharmony_ci case 64000: 6162306a36Sopenharmony_ci fs = TFA9879_I2S_FS_64000; 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci case 88200: 6462306a36Sopenharmony_ci fs = TFA9879_I2S_FS_88200; 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci case 96000: 6762306a36Sopenharmony_ci fs = TFA9879_I2S_FS_96000; 6862306a36Sopenharmony_ci break; 6962306a36Sopenharmony_ci default: 7062306a36Sopenharmony_ci return -EINVAL; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci switch (params_width(params)) { 7462306a36Sopenharmony_ci case 16: 7562306a36Sopenharmony_ci i2s_set = TFA9879_I2S_SET_LSB_J_16; 7662306a36Sopenharmony_ci break; 7762306a36Sopenharmony_ci case 24: 7862306a36Sopenharmony_ci i2s_set = TFA9879_I2S_SET_LSB_J_24; 7962306a36Sopenharmony_ci break; 8062306a36Sopenharmony_ci default: 8162306a36Sopenharmony_ci return -EINVAL; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (tfa9879->lsb_justified) 8562306a36Sopenharmony_ci snd_soc_component_update_bits(component, 8662306a36Sopenharmony_ci TFA9879_SERIAL_INTERFACE_1, 8762306a36Sopenharmony_ci TFA9879_I2S_SET_MASK, 8862306a36Sopenharmony_ci i2s_set << TFA9879_I2S_SET_SHIFT); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci snd_soc_component_update_bits(component, TFA9879_SERIAL_INTERFACE_1, 9162306a36Sopenharmony_ci TFA9879_I2S_FS_MASK, 9262306a36Sopenharmony_ci fs << TFA9879_I2S_FS_SHIFT); 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int tfa9879_mute_stream(struct snd_soc_dai *dai, int mute, int direction) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci snd_soc_component_update_bits(component, TFA9879_MISC_CONTROL, 10162306a36Sopenharmony_ci TFA9879_S_MUTE_MASK, 10262306a36Sopenharmony_ci !!mute << TFA9879_S_MUTE_SHIFT); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int tfa9879_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 11062306a36Sopenharmony_ci struct tfa9879_priv *tfa9879 = snd_soc_component_get_drvdata(component); 11162306a36Sopenharmony_ci int i2s_set; 11262306a36Sopenharmony_ci int sck_pol; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 11562306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBC_CFC: 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci default: 11862306a36Sopenharmony_ci return -EINVAL; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 12262306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 12362306a36Sopenharmony_ci sck_pol = TFA9879_SCK_POL_NORMAL; 12462306a36Sopenharmony_ci break; 12562306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 12662306a36Sopenharmony_ci sck_pol = TFA9879_SCK_POL_INVERSE; 12762306a36Sopenharmony_ci break; 12862306a36Sopenharmony_ci default: 12962306a36Sopenharmony_ci return -EINVAL; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 13362306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 13462306a36Sopenharmony_ci tfa9879->lsb_justified = 0; 13562306a36Sopenharmony_ci i2s_set = TFA9879_I2S_SET_I2S_24; 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 13862306a36Sopenharmony_ci tfa9879->lsb_justified = 0; 13962306a36Sopenharmony_ci i2s_set = TFA9879_I2S_SET_MSB_J_24; 14062306a36Sopenharmony_ci break; 14162306a36Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 14262306a36Sopenharmony_ci tfa9879->lsb_justified = 1; 14362306a36Sopenharmony_ci i2s_set = TFA9879_I2S_SET_LSB_J_24; 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci default: 14662306a36Sopenharmony_ci return -EINVAL; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci snd_soc_component_update_bits(component, TFA9879_SERIAL_INTERFACE_1, 15062306a36Sopenharmony_ci TFA9879_SCK_POL_MASK, 15162306a36Sopenharmony_ci sck_pol << TFA9879_SCK_POL_SHIFT); 15262306a36Sopenharmony_ci snd_soc_component_update_bits(component, TFA9879_SERIAL_INTERFACE_1, 15362306a36Sopenharmony_ci TFA9879_I2S_SET_MASK, 15462306a36Sopenharmony_ci i2s_set << TFA9879_I2S_SET_SHIFT); 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic const struct reg_default tfa9879_regs[] = { 15962306a36Sopenharmony_ci { TFA9879_DEVICE_CONTROL, 0x0000 }, /* 0x00 */ 16062306a36Sopenharmony_ci { TFA9879_SERIAL_INTERFACE_1, 0x0a18 }, /* 0x01 */ 16162306a36Sopenharmony_ci { TFA9879_PCM_IOM2_FORMAT_1, 0x0007 }, /* 0x02 */ 16262306a36Sopenharmony_ci { TFA9879_SERIAL_INTERFACE_2, 0x0a18 }, /* 0x03 */ 16362306a36Sopenharmony_ci { TFA9879_PCM_IOM2_FORMAT_2, 0x0007 }, /* 0x04 */ 16462306a36Sopenharmony_ci { TFA9879_EQUALIZER_A1, 0x59dd }, /* 0x05 */ 16562306a36Sopenharmony_ci { TFA9879_EQUALIZER_A2, 0xc63e }, /* 0x06 */ 16662306a36Sopenharmony_ci { TFA9879_EQUALIZER_B1, 0x651a }, /* 0x07 */ 16762306a36Sopenharmony_ci { TFA9879_EQUALIZER_B2, 0xe53e }, /* 0x08 */ 16862306a36Sopenharmony_ci { TFA9879_EQUALIZER_C1, 0x4616 }, /* 0x09 */ 16962306a36Sopenharmony_ci { TFA9879_EQUALIZER_C2, 0xd33e }, /* 0x0a */ 17062306a36Sopenharmony_ci { TFA9879_EQUALIZER_D1, 0x4df3 }, /* 0x0b */ 17162306a36Sopenharmony_ci { TFA9879_EQUALIZER_D2, 0xea3e }, /* 0x0c */ 17262306a36Sopenharmony_ci { TFA9879_EQUALIZER_E1, 0x5ee0 }, /* 0x0d */ 17362306a36Sopenharmony_ci { TFA9879_EQUALIZER_E2, 0xf93e }, /* 0x0e */ 17462306a36Sopenharmony_ci { TFA9879_BYPASS_CONTROL, 0x0093 }, /* 0x0f */ 17562306a36Sopenharmony_ci { TFA9879_DYNAMIC_RANGE_COMPR, 0x92ba }, /* 0x10 */ 17662306a36Sopenharmony_ci { TFA9879_BASS_TREBLE, 0x12a5 }, /* 0x11 */ 17762306a36Sopenharmony_ci { TFA9879_HIGH_PASS_FILTER, 0x0004 }, /* 0x12 */ 17862306a36Sopenharmony_ci { TFA9879_VOLUME_CONTROL, 0x10bd }, /* 0x13 */ 17962306a36Sopenharmony_ci { TFA9879_MISC_CONTROL, 0x0000 }, /* 0x14 */ 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic bool tfa9879_volatile_reg(struct device *dev, unsigned int reg) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci return reg == TFA9879_MISC_STATUS; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(volume_tlv, -7050, 50, 1); 18862306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(tb_gain_tlv, -1800, 200, 0); 18962306a36Sopenharmony_cistatic const char * const tb_freq_text[] = { 19062306a36Sopenharmony_ci "Low", "Mid", "High" 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_cistatic const struct soc_enum treble_freq_enum = 19362306a36Sopenharmony_ci SOC_ENUM_SINGLE(TFA9879_BASS_TREBLE, TFA9879_F_TRBLE_SHIFT, 19462306a36Sopenharmony_ci ARRAY_SIZE(tb_freq_text), tb_freq_text); 19562306a36Sopenharmony_cistatic const struct soc_enum bass_freq_enum = 19662306a36Sopenharmony_ci SOC_ENUM_SINGLE(TFA9879_BASS_TREBLE, TFA9879_F_BASS_SHIFT, 19762306a36Sopenharmony_ci ARRAY_SIZE(tb_freq_text), tb_freq_text); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic const struct snd_kcontrol_new tfa9879_controls[] = { 20062306a36Sopenharmony_ci SOC_SINGLE_TLV("PCM Playback Volume", TFA9879_VOLUME_CONTROL, 20162306a36Sopenharmony_ci TFA9879_VOL_SHIFT, 0xbd, 1, volume_tlv), 20262306a36Sopenharmony_ci SOC_SINGLE_TLV("Treble Volume", TFA9879_BASS_TREBLE, 20362306a36Sopenharmony_ci TFA9879_G_TRBLE_SHIFT, 18, 0, tb_gain_tlv), 20462306a36Sopenharmony_ci SOC_SINGLE_TLV("Bass Volume", TFA9879_BASS_TREBLE, 20562306a36Sopenharmony_ci TFA9879_G_BASS_SHIFT, 18, 0, tb_gain_tlv), 20662306a36Sopenharmony_ci SOC_ENUM("Treble Corner Freq", treble_freq_enum), 20762306a36Sopenharmony_ci SOC_ENUM("Bass Corner Freq", bass_freq_enum), 20862306a36Sopenharmony_ci}; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget tfa9879_dapm_widgets[] = { 21162306a36Sopenharmony_ciSND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0), 21262306a36Sopenharmony_ciSND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0), 21362306a36Sopenharmony_ciSND_SOC_DAPM_DAC("DAC", NULL, TFA9879_DEVICE_CONTROL, TFA9879_OPMODE_SHIFT, 0), 21462306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("LINEOUT"), 21562306a36Sopenharmony_ciSND_SOC_DAPM_SUPPLY("POWER", TFA9879_DEVICE_CONTROL, TFA9879_POWERUP_SHIFT, 0, 21662306a36Sopenharmony_ci NULL, 0), 21762306a36Sopenharmony_ci}; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic const struct snd_soc_dapm_route tfa9879_dapm_routes[] = { 22062306a36Sopenharmony_ci { "DAC", NULL, "AIFINL" }, 22162306a36Sopenharmony_ci { "DAC", NULL, "AIFINR" }, 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci { "LINEOUT", NULL, "DAC" }, 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci { "DAC", NULL, "POWER" }, 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic const struct snd_soc_component_driver tfa9879_component = { 22962306a36Sopenharmony_ci .controls = tfa9879_controls, 23062306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(tfa9879_controls), 23162306a36Sopenharmony_ci .dapm_widgets = tfa9879_dapm_widgets, 23262306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(tfa9879_dapm_widgets), 23362306a36Sopenharmony_ci .dapm_routes = tfa9879_dapm_routes, 23462306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(tfa9879_dapm_routes), 23562306a36Sopenharmony_ci .idle_bias_on = 1, 23662306a36Sopenharmony_ci .use_pmdown_time = 1, 23762306a36Sopenharmony_ci .endianness = 1, 23862306a36Sopenharmony_ci}; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic const struct regmap_config tfa9879_regmap = { 24162306a36Sopenharmony_ci .reg_bits = 8, 24262306a36Sopenharmony_ci .val_bits = 16, 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci .volatile_reg = tfa9879_volatile_reg, 24562306a36Sopenharmony_ci .max_register = TFA9879_MISC_STATUS, 24662306a36Sopenharmony_ci .reg_defaults = tfa9879_regs, 24762306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(tfa9879_regs), 24862306a36Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 24962306a36Sopenharmony_ci}; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic const struct snd_soc_dai_ops tfa9879_dai_ops = { 25262306a36Sopenharmony_ci .hw_params = tfa9879_hw_params, 25362306a36Sopenharmony_ci .mute_stream = tfa9879_mute_stream, 25462306a36Sopenharmony_ci .set_fmt = tfa9879_set_fmt, 25562306a36Sopenharmony_ci .no_capture_mute = 1, 25662306a36Sopenharmony_ci}; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci#define TFA9879_RATES SNDRV_PCM_RATE_8000_96000 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci#define TFA9879_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ 26162306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE) 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic struct snd_soc_dai_driver tfa9879_dai = { 26462306a36Sopenharmony_ci .name = "tfa9879-hifi", 26562306a36Sopenharmony_ci .playback = { 26662306a36Sopenharmony_ci .stream_name = "Playback", 26762306a36Sopenharmony_ci .channels_min = 2, 26862306a36Sopenharmony_ci .channels_max = 2, 26962306a36Sopenharmony_ci .rates = TFA9879_RATES, 27062306a36Sopenharmony_ci .formats = TFA9879_FORMATS, }, 27162306a36Sopenharmony_ci .ops = &tfa9879_dai_ops, 27262306a36Sopenharmony_ci}; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic int tfa9879_i2c_probe(struct i2c_client *i2c) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct tfa9879_priv *tfa9879; 27762306a36Sopenharmony_ci int i; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci tfa9879 = devm_kzalloc(&i2c->dev, sizeof(*tfa9879), GFP_KERNEL); 28062306a36Sopenharmony_ci if (!tfa9879) 28162306a36Sopenharmony_ci return -ENOMEM; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci i2c_set_clientdata(i2c, tfa9879); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci tfa9879->regmap = devm_regmap_init_i2c(i2c, &tfa9879_regmap); 28662306a36Sopenharmony_ci if (IS_ERR(tfa9879->regmap)) 28762306a36Sopenharmony_ci return PTR_ERR(tfa9879->regmap); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* Ensure the device is in reset state */ 29062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tfa9879_regs); i++) 29162306a36Sopenharmony_ci regmap_write(tfa9879->regmap, 29262306a36Sopenharmony_ci tfa9879_regs[i].reg, tfa9879_regs[i].def); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return devm_snd_soc_register_component(&i2c->dev, &tfa9879_component, 29562306a36Sopenharmony_ci &tfa9879_dai, 1); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic const struct i2c_device_id tfa9879_i2c_id[] = { 29962306a36Sopenharmony_ci { "tfa9879", 0 }, 30062306a36Sopenharmony_ci { } 30162306a36Sopenharmony_ci}; 30262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tfa9879_i2c_id); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic const struct of_device_id tfa9879_of_match[] = { 30562306a36Sopenharmony_ci { .compatible = "nxp,tfa9879", }, 30662306a36Sopenharmony_ci { } 30762306a36Sopenharmony_ci}; 30862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tfa9879_of_match); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic struct i2c_driver tfa9879_i2c_driver = { 31162306a36Sopenharmony_ci .driver = { 31262306a36Sopenharmony_ci .name = "tfa9879", 31362306a36Sopenharmony_ci .of_match_table = tfa9879_of_match, 31462306a36Sopenharmony_ci }, 31562306a36Sopenharmony_ci .probe = tfa9879_i2c_probe, 31662306a36Sopenharmony_ci .id_table = tfa9879_i2c_id, 31762306a36Sopenharmony_ci}; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cimodule_i2c_driver(tfa9879_i2c_driver); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC NXP Semiconductors TFA9879 driver"); 32262306a36Sopenharmony_ciMODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); 32362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 324