diff options
Diffstat (limited to 'sound/soc/codecs/ak4458.c')
| -rw-r--r-- | sound/soc/codecs/ak4458.c | 388 |
1 files changed, 272 insertions, 116 deletions
diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 70d4c89bd6fc..f0b465f9ded5 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -9,9 +9,10 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> -#include <linux/of_device.h> -#include <linux/of_gpio.h> +#include <linux/of.h> #include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> #include <linux/slab.h> #include <sound/initval.h> #include <sound/pcm_params.h> @@ -21,17 +22,37 @@ #include "ak4458.h" +#define AK4458_NUM_SUPPLIES 2 +static const char *ak4458_supply_names[AK4458_NUM_SUPPLIES] = { + "DVDD", + "AVDD", +}; + +enum ak4458_type { + AK4458 = 0, + AK4497 = 1, +}; + +struct ak4458_drvdata { + struct snd_soc_dai_driver *dai_drv; + const struct snd_soc_component_driver *comp_drv; + enum ak4458_type type; +}; + /* AK4458 Codec Private Data */ struct ak4458_priv { + struct regulator_bulk_data supplies[AK4458_NUM_SUPPLIES]; + const struct ak4458_drvdata *drvdata; struct device *dev; struct regmap *regmap; - struct gpio_desc *reset_gpiod; + struct reset_control *reset; struct gpio_desc *mute_gpiod; int digfil; /* SSLOW, SD, SLOW bits */ int fs; /* sampling rate */ int fmt; int slots; int slot_width; + u32 dsd_path; /* For ak4497 */ }; static const struct reg_default ak4458_reg_defaults[] = { @@ -166,7 +187,7 @@ static const struct soc_enum ak4458_dif_enum = static int get_digfil(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); ucontrol->value.enumerated.item[0] = ak4458->digfil; @@ -177,7 +198,7 @@ static int get_digfil(struct snd_kcontrol *kcontrol, static int set_digfil(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); int num; @@ -258,6 +279,47 @@ static const struct snd_soc_dapm_route ak4458_intercon[] = { {"AK4458 AOUTD", NULL, "AK4458 DAC4"}, }; +/* ak4497 controls */ +static const struct snd_kcontrol_new ak4497_snd_controls[] = { + SOC_DOUBLE_R_TLV("DAC Playback Volume", AK4458_03_LCHATT, + AK4458_04_RCHATT, 0, 0xFF, 0, dac_tlv), + SOC_ENUM("AK4497 De-emphasis Response DAC", ak4458_dac1_dem_enum), + SOC_ENUM_EXT("AK4497 Digital Filter Setting", ak4458_digfil_enum, + get_digfil, set_digfil), + SOC_ENUM("AK4497 Inverting Enable of DZFB", ak4458_dzfb_enum), + SOC_ENUM("AK4497 Sound Mode", ak4458_sm_enum), + SOC_ENUM("AK4497 Attenuation transition Time Setting", + ak4458_ats_enum), +}; + +/* ak4497 dapm widgets */ +static const struct snd_soc_dapm_widget ak4497_dapm_widgets[] = { + SND_SOC_DAPM_DAC("AK4497 DAC", NULL, AK4458_0A_CONTROL6, 2, 0), + SND_SOC_DAPM_AIF_IN("AK4497 SDTI", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("AK4497 AOUT"), +}; + +/* ak4497 dapm routes */ +static const struct snd_soc_dapm_route ak4497_intercon[] = { + {"AK4497 DAC", NULL, "AK4497 SDTI"}, + {"AK4497 AOUT", NULL, "AK4497 DAC"}, + +}; + +static int ak4458_get_tdm_mode(struct ak4458_priv *ak4458) +{ + switch (ak4458->slots * ak4458->slot_width) { + case 128: + return 1; + case 256: + return 2; + case 512: + return 3; + default: + return 0; + } +} + static int ak4458_rstn_control(struct snd_soc_component *component, int bit) { int ret; @@ -272,7 +334,10 @@ static int ak4458_rstn_control(struct snd_soc_component *component, int bit) AK4458_00_CONTROL1, AK4458_RSTN_MASK, 0x0); - return ret; + if (ret < 0) + return ret; + + return 0; } static int ak4458_hw_params(struct snd_pcm_substream *substream, @@ -282,12 +347,57 @@ static int ak4458_hw_params(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); int pcm_width = max(params_physical_width(params), ak4458->slot_width); - int nfs1; - u8 format; + u8 format, dsdsel0, dsdsel1, dchn; + int nfs1, dsd_bclk, ret, channels, channels_max; nfs1 = params_rate(params); ak4458->fs = nfs1; + /* calculate bit clock */ + channels = params_channels(params); + channels_max = dai->driver->playback.channels_max; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_DSD_U8: + case SNDRV_PCM_FORMAT_DSD_U16_LE: + case SNDRV_PCM_FORMAT_DSD_U16_BE: + case SNDRV_PCM_FORMAT_DSD_U32_LE: + case SNDRV_PCM_FORMAT_DSD_U32_BE: + dsd_bclk = nfs1 * params_physical_width(params); + switch (dsd_bclk) { + case 2822400: + dsdsel0 = 0; + dsdsel1 = 0; + break; + case 5644800: + dsdsel0 = 1; + dsdsel1 = 0; + break; + case 11289600: + dsdsel0 = 0; + dsdsel1 = 1; + break; + case 22579200: + if (ak4458->drvdata->type == AK4497) { + dsdsel0 = 1; + dsdsel1 = 1; + } else { + dev_err(dai->dev, "DSD512 not supported.\n"); + return -EINVAL; + } + break; + default: + dev_err(dai->dev, "Unsupported dsd bclk.\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, AK4458_06_DSD1, + AK4458_DSDSEL_MASK, dsdsel0); + snd_soc_component_update_bits(component, AK4458_09_DSD2, + AK4458_DSDSEL_MASK, dsdsel1); + break; + } + /* Master Clock Frequency Auto Setting Mode Enable */ snd_soc_component_update_bits(component, AK4458_00_CONTROL1, 0x80, 0x80); @@ -312,6 +422,9 @@ static int ak4458_hw_params(struct snd_pcm_substream *substream, case SND_SOC_DAIFMT_DSP_B: format = AK4458_DIF_32BIT_MSB; break; + case SND_SOC_DAIFMT_PDM: + format = AK4458_DIF_32BIT_MSB; + break; default: return -EINVAL; } @@ -323,8 +436,31 @@ static int ak4458_hw_params(struct snd_pcm_substream *substream, snd_soc_component_update_bits(component, AK4458_00_CONTROL1, AK4458_DIF_MASK, format); - ak4458_rstn_control(component, 0); - ak4458_rstn_control(component, 1); + /* + * Enable/disable Daisy Chain if in TDM mode and the number of played + * channels is bigger than the maximum supported number of channels + */ + dchn = ak4458_get_tdm_mode(ak4458) && + (ak4458->fmt == SND_SOC_DAIFMT_DSP_B) && + (channels > channels_max) ? AK4458_DCHAIN_MASK : 0; + + snd_soc_component_update_bits(component, AK4458_0B_CONTROL7, + AK4458_DCHAIN_MASK, dchn); + + if (ak4458->drvdata->type == AK4497) { + ret = snd_soc_component_update_bits(component, AK4458_09_DSD2, + 0x4, (ak4458->dsd_path << 2)); + if (ret < 0) + return ret; + } + + ret = ak4458_rstn_control(component, 0); + if (ret) + return ret; + + ret = ak4458_rstn_control(component, 1); + if (ret) + return ret; return 0; } @@ -333,15 +469,16 @@ static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_component *component = dai->component; struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); + int ret; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: /* Slave Mode */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: /* Consumer Mode */ break; - case SND_SOC_DAIFMT_CBM_CFM: /* Master Mode is not supported */ - case SND_SOC_DAIFMT_CBS_CFM: - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFP: /* Provider Mode is not supported */ + case SND_SOC_DAIFMT_CBC_CFP: + case SND_SOC_DAIFMT_CBP_CFC: default: - dev_err(component->dev, "Master mode unsupported\n"); + dev_err(component->dev, "Clock provider mode unsupported\n"); return -EINVAL; } @@ -350,6 +487,7 @@ static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_LEFT_J: case SND_SOC_DAIFMT_RIGHT_J: case SND_SOC_DAIFMT_DSP_B: + case SND_SOC_DAIFMT_PDM: ak4458->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; break; default: @@ -358,37 +496,48 @@ static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } - ak4458_rstn_control(component, 0); - ak4458_rstn_control(component, 1); + /* DSD mode */ + snd_soc_component_update_bits(component, AK4458_02_CONTROL3, + AK4458_DP_MASK, + ak4458->fmt == SND_SOC_DAIFMT_PDM ? + AK4458_DP_MASK : 0); + + ret = ak4458_rstn_control(component, 0); + if (ret) + return ret; + + ret = ak4458_rstn_control(component, 1); + if (ret) + return ret; return 0; } static const int att_speed[] = { 4080, 2040, 510, 255 }; -static int ak4458_set_dai_mute(struct snd_soc_dai *dai, int mute) +static int ak4458_set_dai_mute(struct snd_soc_dai *dai, int mute, int direction) { struct snd_soc_component *component = dai->component; struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); - int nfs, ndt, ret, reg; + int nfs, ndt, reg; int ats; nfs = ak4458->fs; - reg = snd_soc_component_read32(component, AK4458_0B_CONTROL7); + reg = snd_soc_component_read(component, AK4458_0B_CONTROL7); ats = (reg & AK4458_ATS_MASK) >> AK4458_ATS_SHIFT; ndt = att_speed[ats] / (nfs / 1000); if (mute) { - ret = snd_soc_component_update_bits(component, AK4458_01_CONTROL2, 0x01, 1); + snd_soc_component_update_bits(component, AK4458_01_CONTROL2, 0x01, 1); mdelay(ndt); if (ak4458->mute_gpiod) gpiod_set_value_cansleep(ak4458->mute_gpiod, 1); } else { if (ak4458->mute_gpiod) gpiod_set_value_cansleep(ak4458->mute_gpiod, 0); - ret = snd_soc_component_update_bits(component, AK4458_01_CONTROL2, 0x01, 0); + snd_soc_component_update_bits(component, AK4458_01_CONTROL2, 0x01, 0); mdelay(ndt); } @@ -405,20 +554,7 @@ static int ak4458_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, ak4458->slots = slots; ak4458->slot_width = slot_width; - switch (slots * slot_width) { - case 128: - mode = AK4458_MODE_TDM128; - break; - case 256: - mode = AK4458_MODE_TDM256; - break; - case 512: - mode = AK4458_MODE_TDM512; - break; - default: - mode = AK4458_MODE_NORMAL; - break; - } + mode = ak4458_get_tdm_mode(ak4458) << AK4458_MODE_SHIFT; snd_soc_component_update_bits(component, AK4458_0A_CONTROL6, AK4458_MODE_MASK, @@ -429,7 +565,10 @@ static int ak4458_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, #define AK4458_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S24_LE |\ - SNDRV_PCM_FMTBIT_S32_LE) + SNDRV_PCM_FMTBIT_S32_LE |\ + SNDRV_PCM_FMTBIT_DSD_U8 |\ + SNDRV_PCM_FMTBIT_DSD_U16_LE |\ + SNDRV_PCM_FMTBIT_DSD_U32_LE) static const unsigned int ak4458_rates[] = { 8000, 11025, 16000, 22050, @@ -447,21 +586,18 @@ static const struct snd_pcm_hw_constraint_list ak4458_rate_constraints = { static int ak4458_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int ret; - - ret = snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &ak4458_rate_constraints); - - return ret; + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &ak4458_rate_constraints); } static const struct snd_soc_dai_ops ak4458_dai_ops = { .startup = ak4458_startup, .hw_params = ak4458_hw_params, .set_fmt = ak4458_set_dai_fmt, - .digital_mute = ak4458_set_dai_mute, + .mute_stream = ak4458_set_dai_mute, .set_tdm_slot = ak4458_set_tdm_slot, + .no_capture_mute = 1, }; static struct snd_soc_dai_driver ak4458_dai = { @@ -476,91 +612,69 @@ static struct snd_soc_dai_driver ak4458_dai = { .ops = &ak4458_dai_ops, }; -static void ak4458_power_off(struct ak4458_priv *ak4458) -{ - if (ak4458->reset_gpiod) { - gpiod_set_value_cansleep(ak4458->reset_gpiod, 0); - usleep_range(1000, 2000); - } -} +static struct snd_soc_dai_driver ak4497_dai = { + .name = "ak4497-aif", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = AK4458_FORMATS, + }, + .ops = &ak4458_dai_ops, +}; -static void ak4458_power_on(struct ak4458_priv *ak4458) +static void ak4458_reset(struct ak4458_priv *ak4458, bool active) { - if (ak4458->reset_gpiod) { - gpiod_set_value_cansleep(ak4458->reset_gpiod, 1); + if (!IS_ERR_OR_NULL(ak4458->reset)) { + if (active) + reset_control_assert(ak4458->reset); + else + reset_control_deassert(ak4458->reset); usleep_range(1000, 2000); } } -static void ak4458_init(struct snd_soc_component *component) -{ - struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); - - /* External Mute ON */ - if (ak4458->mute_gpiod) - gpiod_set_value_cansleep(ak4458->mute_gpiod, 1); - - ak4458_power_on(ak4458); - - snd_soc_component_update_bits(component, AK4458_00_CONTROL1, - 0x80, 0x80); /* ACKS bit = 1; 10000000 */ - - ak4458_rstn_control(component, 1); -} - -static int ak4458_probe(struct snd_soc_component *component) -{ - struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); - - ak4458_init(component); - - ak4458->fs = 48000; - - return 0; -} - -static void ak4458_remove(struct snd_soc_component *component) -{ - struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); - - ak4458_power_off(ak4458); -} - -#ifdef CONFIG_PM -static int __maybe_unused ak4458_runtime_suspend(struct device *dev) +static int ak4458_runtime_suspend(struct device *dev) { struct ak4458_priv *ak4458 = dev_get_drvdata(dev); regcache_cache_only(ak4458->regmap, true); - ak4458_power_off(ak4458); + ak4458_reset(ak4458, true); if (ak4458->mute_gpiod) gpiod_set_value_cansleep(ak4458->mute_gpiod, 0); + regulator_bulk_disable(ARRAY_SIZE(ak4458->supplies), + ak4458->supplies); return 0; } -static int __maybe_unused ak4458_runtime_resume(struct device *dev) +static int ak4458_runtime_resume(struct device *dev) { struct ak4458_priv *ak4458 = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ak4458->supplies), + ak4458->supplies); + if (ret != 0) { + dev_err(ak4458->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } if (ak4458->mute_gpiod) gpiod_set_value_cansleep(ak4458->mute_gpiod, 1); - ak4458_power_off(ak4458); - ak4458_power_on(ak4458); + ak4458_reset(ak4458, false); regcache_cache_only(ak4458->regmap, false); regcache_mark_dirty(ak4458->regmap); return regcache_sync(ak4458->regmap); } -#endif /* CONFIG_PM */ static const struct snd_soc_component_driver soc_codec_dev_ak4458 = { - .probe = ak4458_probe, - .remove = ak4458_remove, .controls = ak4458_snd_controls, .num_controls = ARRAY_SIZE(ak4458_snd_controls), .dapm_widgets = ak4458_dapm_widgets, @@ -570,7 +684,18 @@ static const struct snd_soc_component_driver soc_codec_dev_ak4458 = { .idle_bias_on = 1, .use_pmdown_time = 1, .endianness = 1, - .non_legacy_dai_naming = 1, +}; + +static const struct snd_soc_component_driver soc_codec_dev_ak4497 = { + .controls = ak4497_snd_controls, + .num_controls = ARRAY_SIZE(ak4497_snd_controls), + .dapm_widgets = ak4497_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4497_dapm_widgets), + .dapm_routes = ak4497_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4497_intercon), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, }; static const struct regmap_config ak4458_regmap = { @@ -583,16 +708,27 @@ static const struct regmap_config ak4458_regmap = { .cache_type = REGCACHE_RBTREE, }; +static const struct ak4458_drvdata ak4458_drvdata = { + .dai_drv = &ak4458_dai, + .comp_drv = &soc_codec_dev_ak4458, + .type = AK4458, +}; + +static const struct ak4458_drvdata ak4497_drvdata = { + .dai_drv = &ak4497_dai, + .comp_drv = &soc_codec_dev_ak4497, + .type = AK4497, +}; + static const struct dev_pm_ops ak4458_pm = { - SET_RUNTIME_PM_OPS(ak4458_runtime_suspend, ak4458_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(ak4458_runtime_suspend, ak4458_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static int ak4458_i2c_probe(struct i2c_client *i2c) { struct ak4458_priv *ak4458; - int ret; + int ret, i; ak4458 = devm_kzalloc(&i2c->dev, sizeof(*ak4458), GFP_KERNEL); if (!ak4458) @@ -605,47 +741,67 @@ static int ak4458_i2c_probe(struct i2c_client *i2c) i2c_set_clientdata(i2c, ak4458); ak4458->dev = &i2c->dev; - ak4458->reset_gpiod = devm_gpiod_get_optional(ak4458->dev, "reset", - GPIOD_OUT_LOW); - if (IS_ERR(ak4458->reset_gpiod)) - return PTR_ERR(ak4458->reset_gpiod); + ak4458->drvdata = of_device_get_match_data(&i2c->dev); + + ak4458->reset = devm_reset_control_get_optional_shared(ak4458->dev, NULL); + if (IS_ERR(ak4458->reset)) + return PTR_ERR(ak4458->reset); ak4458->mute_gpiod = devm_gpiod_get_optional(ak4458->dev, "mute", GPIOD_OUT_LOW); if (IS_ERR(ak4458->mute_gpiod)) return PTR_ERR(ak4458->mute_gpiod); - ret = devm_snd_soc_register_component(ak4458->dev, &soc_codec_dev_ak4458, - &ak4458_dai, 1); + /* Optional property for ak4497 */ + of_property_read_u32(i2c->dev.of_node, "dsd-path", &ak4458->dsd_path); + + for (i = 0; i < ARRAY_SIZE(ak4458->supplies); i++) + ak4458->supplies[i].supply = ak4458_supply_names[i]; + + ret = devm_regulator_bulk_get(ak4458->dev, ARRAY_SIZE(ak4458->supplies), + ak4458->supplies); + if (ret != 0) { + dev_err(ak4458->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(ak4458->dev, + ak4458->drvdata->comp_drv, + ak4458->drvdata->dai_drv, 1); if (ret < 0) { dev_err(ak4458->dev, "Failed to register CODEC: %d\n", ret); return ret; } pm_runtime_enable(&i2c->dev); + regcache_cache_only(ak4458->regmap, true); + ak4458_reset(ak4458, false); return 0; } -static int ak4458_i2c_remove(struct i2c_client *i2c) +static void ak4458_i2c_remove(struct i2c_client *i2c) { - pm_runtime_disable(&i2c->dev); + struct ak4458_priv *ak4458 = i2c_get_clientdata(i2c); - return 0; + ak4458_reset(ak4458, true); + pm_runtime_disable(&i2c->dev); } static const struct of_device_id ak4458_of_match[] = { - { .compatible = "asahi-kasei,ak4458", }, + { .compatible = "asahi-kasei,ak4458", .data = &ak4458_drvdata}, + { .compatible = "asahi-kasei,ak4497", .data = &ak4497_drvdata}, { }, }; +MODULE_DEVICE_TABLE(of, ak4458_of_match); static struct i2c_driver ak4458_i2c_driver = { .driver = { .name = "ak4458", - .pm = &ak4458_pm, + .pm = pm_ptr(&ak4458_pm), .of_match_table = ak4458_of_match, }, - .probe_new = ak4458_i2c_probe, + .probe = ak4458_i2c_probe, .remove = ak4458_i2c_remove, }; |
