[alsa-devel] [PATCH 00/13] ASoC: tlv320aic32x4: Rework Clock Setting
The current tlv320aic32x4 code sets the various clock parameters by looking in a static table in the kernel. This works well enough, but it has a few disadvantages.
1) If the master clock doesn't match one of the precalculated values the driver cannot work. This could be because the designer decided to use a different crystal, or because the clock comes from a PLL that can't quite achieve the proper frequency exactly.
2) The driver only supports certain pre-calculated sample rates. The actual hardware can support many more. These changes enable those rates. Additionally, certain sample rates at certain clock rates were previously unavailable as combinations.
This patch dynamically calculates the various PLL and divider values to find something that works.
Additionally, to enable much of this, the clock tree is modeled as a part of the Common Clock Framework. This allows for easier debugigng as you can use the standard tools such as /sys/kernel/debug/clk/clk_summary. It also simplifies enabling the entirety of the clock tree needed, and makes sure unused clocks become disabled.
These patches have been tested and are working on a Raspberry Pi with the simplecard driver.
Annaliese McDermond (13): ASoC: tlv320aic32x4: Break out clock setting into separate function ASoC: tlv320aic32x4: Model PLL in CCF ASoC: tlv320aic32x4: Model CODEC_CLKIN in CCF ASoC: tlv320aic32x4: Model DAC/ADC dividers in CCF ASoC: tlv320aic32x4: Model BDIV divider in CCF ASoC: tlv320aic32x4: Control clock gating with CCF ASoC: tlv320aic32x4: Move aosr and dosr setting to separate functions ASoC: tlv320aic32x4: Dynamically Determine Clocking ASoC: tlv320aic32x4: Fix clock activation on bias level change ASoC: tlv320aic32x4: Remove sysclk references ASoC: tlv320aic32x4: Remove mclk references ASoC: tlv320aic32x4: Propery Set Processing Blocks ASoC: tlv320aic32x4: Allow 192000 Sample Rate
sound/soc/codecs/Makefile | 2 +- sound/soc/codecs/tlv320aic32x4-clk.c | 483 +++++++++++++++++++++++++++ sound/soc/codecs/tlv320aic32x4.c | 374 ++++++++++----------- sound/soc/codecs/tlv320aic32x4.h | 11 + 4 files changed, 670 insertions(+), 200 deletions(-) create mode 100644 sound/soc/codecs/tlv320aic32x4-clk.c
Break the clock setting logic out from the main hw_params. It's rather large and unweildy and makes for a large function. This also better enables some of the following changes to the clock tree access in the driver.
Signed-off-by: Annaliese McDermond nh6z@nh6z.net --- sound/soc/codecs/tlv320aic32x4.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-)
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 96f1526cb258..1aa8f5aa4225 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -696,17 +696,13 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) return 0; }
-static int aic32x4_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) +static int aic32x4_setup_clocks(struct snd_soc_component *component, + unsigned int sample_rate, + unsigned int parent_rate) { - struct snd_soc_component *component = dai->component; - struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); - u8 iface1_reg = 0; - u8 dacsetup_reg = 0; int i;
- i = aic32x4_get_divs(aic32x4->sysclk, params_rate(params)); + i = aic32x4_get_divs(parent_rate, sample_rate); if (i < 0) { printk(KERN_ERR "aic32x4: sampling rate not supported\n"); return i; @@ -763,6 +759,20 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, snd_soc_component_update_bits(component, AIC32X4_BCLKN, AIC32X4_BCLK_MASK, aic32x4_divs[i].blck_N);
+ return 0; +} + +static int aic32x4_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); + u8 iface1_reg = 0; + u8 dacsetup_reg = 0; + + aic32x4_setup_clocks(component, params_rate(params), aic32x4->sysclk); + switch (params_width(params)) { case 16: iface1_reg |= (AIC32X4_WORD_LEN_16BITS <<
Model and manage the on-board PLL as a component in the Core Clock Framework. This should allow us to do some more complex clock management and power control. Also, some of the on-board chip clocks can be exposed to the outside, and this change will make those clocks easier to consume by other parts of the kernel.
Signed-off-by: Annaliese McDermond nh6z@nh6z.net --- sound/soc/codecs/Makefile | 2 +- sound/soc/codecs/tlv320aic32x4-clk.c | 323 +++++++++++++++++++++++++++ sound/soc/codecs/tlv320aic32x4.c | 93 ++++---- sound/soc/codecs/tlv320aic32x4.h | 5 + 4 files changed, 379 insertions(+), 44 deletions(-)
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index aab2ad95a137..c0c9a07975ad 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -192,7 +192,7 @@ snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o snd-soc-tlv320aic26-objs := tlv320aic26.o snd-soc-tlv320aic31xx-objs := tlv320aic31xx.o -snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o +snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o tlv320aic32x4-clk.o snd-soc-tlv320aic32x4-i2c-objs := tlv320aic32x4-i2c.o snd-soc-tlv320aic32x4-spi-objs := tlv320aic32x4-spi.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c new file mode 100644 index 000000000000..9db31ca62b35 --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4-clk.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/sound/soc/codecs/tlv320aic32x4-clk.c + * + * Copyright 2019 Annaliese McDermond + * + * Author: Annaliese McDermond nh6z@nh6z.net + */ + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/regmap.h> +#include <linux/device.h> + +#include "tlv320aic32x4.h" + +#define to_clk_aic32x4(_hw) container_of(_hw, struct clk_aic32x4, hw) +struct clk_aic32x4 { + struct clk_hw hw; + struct device *dev; + struct regmap *regmap; + unsigned int reg; +}; + +/* + * struct clk_aic32x4_pll_muldiv - Multiplier/divider settings + * @p: Divider + * @r: first multiplier + * @j: integer part of second multiplier + * @d: decimal part of second multiplier + */ +struct clk_aic32x4_pll_muldiv { + u8 p; + u16 r; + u8 j; + u16 d; +}; + +struct aic32x4_clkdesc { + const char *name; + const char * const *parent_names; + unsigned int num_parents; + const struct clk_ops *ops; + unsigned int reg; +}; + +static int clk_aic32x4_pll_enable(struct clk_hw *hw) +{ + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + + return regmap_update_bits(pll->regmap, AIC32X4_PLLPR, + AIC32X4_PLLEN, AIC32X4_PLLEN); +} + +static void clk_aic32x4_pll_disable(struct clk_hw *hw) +{ + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + + regmap_update_bits(pll->regmap, AIC32X4_PLLPR, + AIC32X4_PLLEN, 0); +} + +static int clk_aic32x4_pll_is_enabled(struct clk_hw *hw) +{ + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + + unsigned int val; + int ret; + + ret = regmap_read(pll->regmap, AIC32X4_PLLPR, &val); + if (ret < 0) + return ret; + + return !!(val & AIC32X4_PLLEN); +} + +static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll, + struct clk_aic32x4_pll_muldiv *settings) +{ + /* Change to use regmap_bulk_read? */ + unsigned int val; + int ret; + + ret = regmap_read(pll->regmap, AIC32X4_PLLPR, &val); + if (ret) + return ret; + settings->r = val & AIC32X4_PLL_R_MASK; + settings->p = (val & AIC32X4_PLL_P_MASK) >> AIC32X4_PLL_P_SHIFT; + + ret = regmap_read(pll->regmap, AIC32X4_PLLJ, &val); + if (ret < 0) + return ret; + settings->j = val; + + ret = regmap_read(pll->regmap, AIC32X4_PLLDMSB, &val); + if (ret < 0) + return ret; + settings->d = val << 8; + + ret = regmap_read(pll->regmap, AIC32X4_PLLDLSB, &val); + if (ret < 0) + return ret; + settings->d |= val; + + return 0; +} + +static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll, + struct clk_aic32x4_pll_muldiv *settings) +{ + int ret; + /* Change to use regmap_bulk_write for some if not all? */ + + ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR, + AIC32X4_PLL_R_MASK, settings->r); + if (ret < 0) + return ret; + + ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR, + AIC32X4_PLL_P_MASK, + settings->p << AIC32X4_PLL_P_SHIFT); + if (ret < 0) + return ret; + + ret = regmap_write(pll->regmap, AIC32X4_PLLJ, settings->j); + if (ret < 0) + return ret; + + ret = regmap_write(pll->regmap, AIC32X4_PLLDMSB, (settings->d >> 8)); + if (ret < 0) + return ret; + ret = regmap_write(pll->regmap, AIC32X4_PLLDLSB, (settings->d & 0xff)); + if (ret < 0) + return ret; + + return 0; +} + +static unsigned long clk_aic32x4_pll_calc_rate( + struct clk_aic32x4_pll_muldiv *settings, + unsigned long parent_rate) +{ + u64 rate; + /* + * We scale j by 10000 to account for the decimal part of P and divide + * it back out later. + */ + rate = (u64) parent_rate * settings->r * + ((settings->j * 10000) + settings->d); + + return (unsigned long) DIV_ROUND_UP_ULL(rate, settings->p * 10000); +} + +static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, + unsigned long rate, unsigned long parent_rate) +{ + u64 multiplier; + + settings->p = parent_rate / AIC32X4_MAX_PLL_CLKIN + 1; + if (settings->p > 8) + return -1; + + /* + * We scale this figure by 10000 so that we can get the decimal part + * of the multiplier. This is because we can't do floating point + * math in the kernel. + */ + multiplier = (u64) rate * settings->p * 10000; + do_div(multiplier, parent_rate); + + /* + * J can't be over 64, so R can scale this. + * R can't be greater than 4. + */ + settings->r = ((u32) multiplier / 640000) + 1; + if (settings->r > 4) + return -1; + do_div(multiplier, settings->r); + + /* + * J can't be < 1. + */ + if (multiplier < 10000) + return -1; + + /* Figure out the integer part, J, and the fractional part, D. */ + settings->j = (u32) multiplier / 10000; + settings->d = (u32) multiplier % 10000; + + return 0; +} + +static unsigned long clk_aic32x4_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + struct clk_aic32x4_pll_muldiv settings; + int ret; + + ret = clk_aic32x4_pll_get_muldiv(pll, &settings); + if (ret < 0) + return 0; + + return clk_aic32x4_pll_calc_rate(&settings, parent_rate); +} + +static long clk_aic32x4_pll_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_aic32x4_pll_muldiv settings; + int ret; + + ret = clk_aic32x4_pll_calc_muldiv(&settings, rate, *parent_rate); + if (ret < 0) + return 0; + + return clk_aic32x4_pll_calc_rate(&settings, *parent_rate); +} + +static int clk_aic32x4_pll_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + struct clk_aic32x4_pll_muldiv settings; + int ret; + + ret = clk_aic32x4_pll_calc_muldiv(&settings, rate, parent_rate); + if (ret < 0) + return -EINVAL; + + return clk_aic32x4_pll_set_muldiv(pll, &settings); +} + +static int clk_aic32x4_pll_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + + return regmap_update_bits(pll->regmap, + AIC32X4_CLKMUX, + AIC32X4_PLL_CLKIN_MASK, + index << AIC32X4_PLL_CLKIN_SHIFT); +} + +static u8 clk_aic32x4_pll_get_parent(struct clk_hw *hw) +{ + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + unsigned int val; + + regmap_read(pll->regmap, AIC32X4_PLLPR, &val); + + return (val & AIC32X4_PLL_CLKIN_MASK) >> AIC32X4_PLL_CLKIN_SHIFT; +} + + +static const struct clk_ops aic32x4_pll_ops = { + .enable = clk_aic32x4_pll_enable, + .disable = clk_aic32x4_pll_disable, + .is_enabled = clk_aic32x4_pll_is_enabled, + .recalc_rate = clk_aic32x4_pll_recalc_rate, + .round_rate = clk_aic32x4_pll_round_rate, + .set_rate = clk_aic32x4_pll_set_rate, + .set_parent = clk_aic32x4_pll_set_parent, + .get_parent = clk_aic32x4_pll_get_parent, +}; + +static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { + { + .name = "pll", + .parent_names = + (const char* []) { "mclk", "bclk", "gpio", "din" }, + .num_parents = 4, + .ops = &aic32x4_pll_ops, + .reg = 0, + }, +}; + +static struct clk *aic32x4_register_clk(struct device *dev, + struct aic32x4_clkdesc *desc) +{ + struct clk_init_data init; + struct clk_aic32x4 *priv; + const char *devname = dev_name(dev); + + init.ops = desc->ops; + init.name = desc->name; + init.parent_names = desc->parent_names; + init.num_parents = desc->num_parents; + init.flags = 0; + + priv = devm_kzalloc(dev, sizeof(struct clk_aic32x4), GFP_KERNEL); + if (priv == NULL) + return (struct clk *) -ENOMEM; + + priv->dev = dev; + priv->hw.init = &init; + priv->regmap = dev_get_regmap(dev, NULL); + priv->reg = desc->reg; + + clk_hw_register_clkdev(&priv->hw, desc->name, devname); + return devm_clk_register(dev, &priv->hw); +} + +int aic32x4_register_clocks(struct device *dev, const char *mclk_name) +{ + int i; + + /* + * These lines are here to preserve the current functionality of + * the driver with regard to the DT. These should eventually be set + * by DT nodes so that the connections can be set up in configuration + * rather than code. + */ + aic32x4_clkdesc_array[0].parent_names = + (const char* []) { mclk_name, "bclk", "gpio", "din" }; + + for (i = 0; i < ARRAY_SIZE(aic32x4_clkdesc_array); ++i) + aic32x4_register_clk(dev, &aic32x4_clkdesc_array[i]); + + return 0; +} +EXPORT_SYMBOL(aic32x4_register_clocks); diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 1aa8f5aa4225..0e0af3a3cdf9 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -33,6 +33,7 @@ #include <linux/cdev.h> #include <linux/slab.h> #include <linux/clk.h> +#include <linux/of_clk.h> #include <linux/regulator/consumer.h>
#include <sound/tlv320aic32x4.h> @@ -49,9 +50,7 @@ struct aic32x4_rate_divs { u32 mclk; u32 rate; - u8 p_val; - u8 pll_j; - u16 pll_d; + unsigned long pll_rate; u16 dosr; u8 ndac; u8 mdac; @@ -69,6 +68,7 @@ struct aic32x4_priv { bool swapdacs; int rstn_gpio; struct clk *mclk; + const char *mclk_name;
struct regulator *supply_ldo; struct regulator *supply_iov; @@ -307,34 +307,34 @@ static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
static const struct aic32x4_rate_divs aic32x4_divs[] = { /* 8k rate */ - {12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24}, - {24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24}, - {25000000, 8000, 2, 7, 3728, 768, 15, 1, 64, 45, 4, 24}, + { 12000000, 8000, 57120000, 768, 5, 3, 128, 5, 18, 24 }, + { 24000000, 8000, 57120000, 768, 15, 1, 64, 45, 4, 24 }, + { 25000000, 8000, 32620000, 768, 15, 1, 64, 45, 4, 24 }, /* 11.025k rate */ - {12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16}, - {24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16}, + { 12000000, 11025, 44217600, 512, 8, 2, 128, 8, 8, 16 }, + { 24000000, 11025, 44217600, 512, 16, 1, 64, 32, 4, 16 }, /* 16k rate */ - {12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12}, - {24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12}, - {25000000, 16000, 2, 7, 3728, 384, 15, 1, 64, 18, 5, 12}, + { 12000000, 16000, 57120000, 384, 5, 3, 128, 5, 9, 12 }, + { 24000000, 16000, 57120000, 384, 15, 1, 64, 18, 5, 12 }, + { 25000000, 16000, 32620000, 384, 15, 1, 64, 18, 5, 12 }, /* 22.05k rate */ - {12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8}, - {24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8}, - {25000000, 22050, 2, 7, 2253, 256, 16, 1, 64, 16, 4, 8}, + { 12000000, 22050, 44217600, 256, 4, 4, 128, 4, 8, 8 }, + { 24000000, 22050, 44217600, 256, 16, 1, 64, 16, 4, 8 }, + { 25000000, 22050, 19713750, 256, 16, 1, 64, 16, 4, 8 }, /* 32k rate */ - {12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6}, - {24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6}, + { 12000000, 32000, 14112000, 192, 2, 7, 64, 2, 21, 6 }, + { 24000000, 32000, 14112000, 192, 7, 2, 64, 7, 6, 6 }, /* 44.1k rate */ - {12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4}, - {24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4}, - {25000000, 44100, 2, 7, 2253, 128, 8, 2, 64, 8, 4, 4}, + { 12000000, 44100, 44217600, 128, 2, 8, 128, 2, 8, 4 }, + { 24000000, 44100, 44217600, 128, 8, 2, 64, 8, 4, 4 }, + { 25000000, 44100, 19713750, 128, 8, 2, 64, 8, 4, 4 }, /* 48k rate */ - {12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4}, - {24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4}, - {25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4}, + { 12000000, 48000, 18432000, 128, 2, 8, 128, 2, 8, 4 }, + { 24000000, 48000, 18432000, 128, 8, 2, 64, 8, 4, 4 }, + { 25000000, 48000, 75626250, 128, 8, 2, 64, 8, 4, 4 },
/* 96k rate */ - {25000000, 96000, 2, 7, 8643, 64, 4, 4, 64, 4, 4, 1}, + { 25000000, 96000, 75626250, 64, 4, 4, 64, 4, 4, 1 }, };
static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { @@ -701,6 +701,11 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, unsigned int parent_rate) { int i; + int ret; + + struct clk_bulk_data clocks[] = { + { .id = "pll" }, + };
i = aic32x4_get_divs(parent_rate, sample_rate); if (i < 0) { @@ -708,30 +713,20 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, return i; }
- /* MCLK as PLL_CLKIN */ - snd_soc_component_update_bits(component, AIC32X4_CLKMUX, AIC32X4_PLL_CLKIN_MASK, - AIC32X4_PLL_CLKIN_MCLK << AIC32X4_PLL_CLKIN_SHIFT); + ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks); + if (ret) + return ret; + + clk_set_rate(clocks[0].clk, sample_rate); + /* PLL as CODEC_CLKIN */ - snd_soc_component_update_bits(component, AIC32X4_CLKMUX, AIC32X4_CODEC_CLKIN_MASK, + snd_soc_component_update_bits(component, AIC32X4_CLKMUX, + AIC32X4_CODEC_CLKIN_MASK, AIC32X4_CODEC_CLKIN_PLL << AIC32X4_CODEC_CLKIN_SHIFT); /* DAC_MOD_CLK as BDIV_CLKIN */ snd_soc_component_update_bits(component, AIC32X4_IFACE3, AIC32X4_BDIVCLK_MASK, AIC32X4_DACMOD2BCLK << AIC32X4_BDIVCLK_SHIFT);
- /* We will fix R value to 1 and will make P & J=K.D as variable */ - snd_soc_component_update_bits(component, AIC32X4_PLLPR, AIC32X4_PLL_R_MASK, 0x01); - - /* PLL P value */ - snd_soc_component_update_bits(component, AIC32X4_PLLPR, AIC32X4_PLL_P_MASK, - aic32x4_divs[i].p_val << AIC32X4_PLL_P_SHIFT); - - /* PLL J value */ - snd_soc_component_write(component, AIC32X4_PLLJ, aic32x4_divs[i].pll_j); - - /* PLL D value */ - snd_soc_component_write(component, AIC32X4_PLLDMSB, (aic32x4_divs[i].pll_d >> 8)); - snd_soc_component_write(component, AIC32X4_PLLDLSB, (aic32x4_divs[i].pll_d & 0xff)); - /* NDAC divider value */ snd_soc_component_update_bits(component, AIC32X4_NDAC, AIC32X4_NDAC_MASK, aic32x4_divs[i].ndac); @@ -990,8 +985,8 @@ static int aic32x4_component_probe(struct snd_soc_component *component)
/* Power platform configuration */ if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) { - snd_soc_component_write(component, AIC32X4_MICBIAS, AIC32X4_MICBIAS_LDOIN | - AIC32X4_MICBIAS_2075V); + snd_soc_component_write(component, AIC32X4_MICBIAS, + AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V); } if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) snd_soc_component_write(component, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE); @@ -1054,12 +1049,18 @@ static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, struct device_node *np) { struct aic32x4_setup_data *aic32x4_setup; + int ret;
aic32x4_setup = devm_kzalloc(aic32x4->dev, sizeof(*aic32x4_setup), GFP_KERNEL); if (!aic32x4_setup) return -ENOMEM;
+ ret = of_property_match_string(np, "clock-names", "mclk"); + if (ret < 0) + return -EINVAL; + aic32x4->mclk_name = of_clk_get_parent_name(np, ret); + aic32x4->swapdacs = false; aic32x4->micpga_routing = 0; aic32x4->rstn_gpio = of_get_named_gpio(np, "reset-gpios", 0); @@ -1193,6 +1194,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap) aic32x4->swapdacs = pdata->swapdacs; aic32x4->micpga_routing = pdata->micpga_routing; aic32x4->rstn_gpio = pdata->rstn_gpio; + aic32x4->mclk_name = "mclk"; } else if (np) { ret = aic32x4_parse_dt(aic32x4, np); if (ret) { @@ -1204,6 +1206,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap) aic32x4->swapdacs = false; aic32x4->micpga_routing = 0; aic32x4->rstn_gpio = -1; + aic32x4->mclk_name = "mclk"; }
aic32x4->mclk = devm_clk_get(dev, "mclk"); @@ -1212,6 +1215,10 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap) return PTR_ERR(aic32x4->mclk); }
+ ret = aic32x4_register_clocks(dev, aic32x4->mclk_name); + if (ret) + return ret; + if (gpio_is_valid(aic32x4->rstn_gpio)) { ret = devm_gpio_request_one(dev, aic32x4->rstn_gpio, GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn"); diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index c2d74025bf4b..e2b65bbba7c2 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -16,6 +16,7 @@ struct regmap_config; extern const struct regmap_config aic32x4_regmap_config; int aic32x4_probe(struct device *dev, struct regmap *regmap); int aic32x4_remove(struct device *dev); +int aic32x4_register_clocks(struct device *dev, const char *mclk_name);
/* tlv320aic32x4 register space (in decimal to match datasheet) */
@@ -205,4 +206,8 @@ int aic32x4_remove(struct device *dev); #define AIC32X4_RMICPGANIN_IN1L_10K 0x10 #define AIC32X4_RMICPGANIN_CM1R_10K 0x40
+/* Clock Limits */ +#define AIC32X4_MAX_PLL_CLKIN 20000000 + + #endif /* _TLV320AIC32X4_H */
On Mon, Mar 18, 2019 at 08:37:45PM -0700, Annaliese McDermond wrote:
sound/soc/codecs/Makefile | 2 +- sound/soc/codecs/tlv320aic32x4-clk.c | 323 +++++++++++++++++++++++++++ sound/soc/codecs/tlv320aic32x4.c | 93 ++++---- sound/soc/codecs/tlv320aic32x4.h | 5 + 4 files changed, 379 insertions(+), 44 deletions(-)
I'm not seeing any Kconfig updates which add dependencies on the clock API or conditionally build the clock implementation only if that's been enabled.
+++ b/sound/soc/codecs/tlv320aic32x4-clk.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- linux/sound/soc/codecs/tlv320aic32x4-clk.c
- Copyright 2019 Annaliese McDermond
- Author: Annaliese McDermond nh6z@nh6z.net
- */
Please make the entire comment a C++ comment so it looks more intentional. It's also better to not have the path in the header since that's prone to bitrot, just write something in words.
+static const struct clk_ops aic32x4_pll_ops = {
- .enable = clk_aic32x4_pll_enable,
- .disable = clk_aic32x4_pll_disable,
- .is_enabled = clk_aic32x4_pll_is_enabled,
These are enable and disable operations - shouldn't they be prepare and unprepare? The device is controlled over buses that require sleeping but the prepare and enable operations require atomic context.
+} +EXPORT_SYMBOL(aic32x4_register_clocks);
ASoC is all EXPORT_SYMBOL_GPL()...
Model and manage codec clock input as a component in the Core Clock Framework. This should allow us to do some more complex clock management and power control. Also, some of the on-board chip clocks can be exposed to the outside, and this change will make those clocks easier to consume by other parts of the kernel.
Signed-off-by: Annaliese McDermond nh6z@nh6z.net --- sound/soc/codecs/tlv320aic32x4-clk.c | 34 ++++++++++++++++++++++++++++ sound/soc/codecs/tlv320aic32x4.c | 18 +++++++++++---- 2 files changed, 47 insertions(+), 5 deletions(-)
diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c index 9db31ca62b35..e9c4bec795db 100644 --- a/sound/soc/codecs/tlv320aic32x4-clk.c +++ b/sound/soc/codecs/tlv320aic32x4-clk.c @@ -265,6 +265,30 @@ static const struct clk_ops aic32x4_pll_ops = { .get_parent = clk_aic32x4_pll_get_parent, };
+static int clk_aic32x4_codec_clkin_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_aic32x4 *mux = to_clk_aic32x4(hw); + + return regmap_update_bits(mux->regmap, + AIC32X4_CLKMUX, + AIC32X4_CODEC_CLKIN_MASK, index << AIC32X4_CODEC_CLKIN_SHIFT); +} + +static u8 clk_aic32x4_codec_clkin_get_parent(struct clk_hw *hw) +{ + struct clk_aic32x4 *mux = to_clk_aic32x4(hw); + unsigned int val; + + regmap_read(mux->regmap, AIC32X4_CLKMUX, &val); + + return (val & AIC32X4_CODEC_CLKIN_MASK) >> AIC32X4_CODEC_CLKIN_SHIFT; +} + +static const struct clk_ops aic32x4_codec_clkin_ops = { + .set_parent = clk_aic32x4_codec_clkin_set_parent, + .get_parent = clk_aic32x4_codec_clkin_get_parent, +}; + static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { { .name = "pll", @@ -274,6 +298,14 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { .ops = &aic32x4_pll_ops, .reg = 0, }, + { + .name = "codec_clkin", + .parent_names = + (const char *[]) { "mclk", "bclk", "gpio", "pll" }, + .num_parents = 4, + .ops = &aic32x4_codec_clkin_ops, + .reg = 0, + }, };
static struct clk *aic32x4_register_clk(struct device *dev, @@ -314,6 +346,8 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name) */ aic32x4_clkdesc_array[0].parent_names = (const char* []) { mclk_name, "bclk", "gpio", "din" }; + aic32x4_clkdesc_array[1].parent_names = + (const char *[]) { mclk_name, "bclk", "gpio", "pll" };
for (i = 0; i < ARRAY_SIZE(aic32x4_clkdesc_array); ++i) aic32x4_register_clk(dev, &aic32x4_clkdesc_array[i]); diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 0e0af3a3cdf9..6b682743cf9b 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -719,12 +719,9 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component,
clk_set_rate(clocks[0].clk, sample_rate);
- /* PLL as CODEC_CLKIN */ - snd_soc_component_update_bits(component, AIC32X4_CLKMUX, - AIC32X4_CODEC_CLKIN_MASK, - AIC32X4_CODEC_CLKIN_PLL << AIC32X4_CODEC_CLKIN_SHIFT); /* DAC_MOD_CLK as BDIV_CLKIN */ - snd_soc_component_update_bits(component, AIC32X4_IFACE3, AIC32X4_BDIVCLK_MASK, + snd_soc_component_update_bits(component, AIC32X4_IFACE3, + AIC32X4_BDIVCLK_MASK, AIC32X4_DACMOD2BCLK << AIC32X4_BDIVCLK_SHIFT);
/* NDAC divider value */ @@ -971,6 +968,15 @@ static int aic32x4_component_probe(struct snd_soc_component *component) { struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); u32 tmp_reg; + int ret; + + struct clk_bulk_data clocks[] = { + { .id = "codec_clkin" }, + }; + + ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks); + if (ret) + return ret;
if (gpio_is_valid(aic32x4->rstn_gpio)) { ndelay(10); @@ -983,6 +989,8 @@ static int aic32x4_component_probe(struct snd_soc_component *component) if (aic32x4->setup) aic32x4_setup_gpios(component);
+ clk_set_parent(clocks[0].clk, clocks[1].clk); + /* Power platform configuration */ if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) { snd_soc_component_write(component, AIC32X4_MICBIAS,
Model and manage DAC/ADC dividers as components in the Core Clock Framework. This should allow us to do some more complex clock management and power control. Also, some of the on-board chip clocks can be exposed to the outside, and this change will make those clocks easier to consume by other parts of the kernel.
Signed-off-by: Annaliese McDermond nh6z@nh6z.net
Signed-off-by: Annaliese McDermond nh6z@nh6z.net --- sound/soc/codecs/tlv320aic32x4-clk.c | 90 ++++++++++++++++++++++++++ sound/soc/codecs/tlv320aic32x4.c | 95 ++++++++++++++++------------ sound/soc/codecs/tlv320aic32x4.h | 4 ++ 3 files changed, 148 insertions(+), 41 deletions(-)
diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c index e9c4bec795db..87103b3878ab 100644 --- a/sound/soc/codecs/tlv320aic32x4-clk.c +++ b/sound/soc/codecs/tlv320aic32x4-clk.c @@ -289,6 +289,68 @@ static const struct clk_ops aic32x4_codec_clkin_ops = { .get_parent = clk_aic32x4_codec_clkin_get_parent, };
+static int clk_aic32x4_div_enable(struct clk_hw *hw) +{ + struct clk_aic32x4 *div = to_clk_aic32x4(hw); + + return regmap_update_bits(div->regmap, div->reg, + AIC32X4_DIVEN, AIC32X4_DIVEN); +} + +static void clk_aic32x4_div_disable(struct clk_hw *hw) +{ + struct clk_aic32x4 *div = to_clk_aic32x4(hw); + + regmap_update_bits(div->regmap, div->reg, + AIC32X4_DIVEN, 0); +} + +static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_aic32x4 *div = to_clk_aic32x4(hw); + u8 divisor; + + divisor = DIV_ROUND_UP(parent_rate, rate); + if (divisor > 128) + return -EINVAL; + + return regmap_update_bits(div->regmap, div->reg, + AIC32X4_DIV_MASK, divisor); +} + +static long clk_aic32x4_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long divisor; + + divisor = DIV_ROUND_UP(*parent_rate, rate); + if (divisor > 128) + return -EINVAL; + + return DIV_ROUND_UP(*parent_rate, divisor); +} + +static unsigned long clk_aic32x4_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_aic32x4 *div = to_clk_aic32x4(hw); + + unsigned int val; + + regmap_read(div->regmap, div->reg, &val); + + return DIV_ROUND_UP(parent_rate, val & AIC32X4_DIV_MASK); +} + +static const struct clk_ops aic32x4_div_ops = { + .enable = clk_aic32x4_div_enable, + .disable = clk_aic32x4_div_disable, + .set_rate = clk_aic32x4_div_set_rate, + .round_rate = clk_aic32x4_div_round_rate, + .recalc_rate = clk_aic32x4_div_recalc_rate, +}; + static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { { .name = "pll", @@ -306,6 +368,34 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { .ops = &aic32x4_codec_clkin_ops, .reg = 0, }, + { + .name = "ndac", + .parent_names = (const char * []) { "codec_clkin" }, + .num_parents = 1, + .ops = &aic32x4_div_ops, + .reg = AIC32X4_NDAC, + }, + { + .name = "mdac", + .parent_names = (const char * []) { "ndac" }, + .num_parents = 1, + .ops = &aic32x4_div_ops, + .reg = AIC32X4_MDAC, + }, + { + .name = "nadc", + .parent_names = (const char * []) { "codec_clkin" }, + .num_parents = 1, + .ops = &aic32x4_div_ops, + .reg = AIC32X4_NADC, + }, + { + .name = "madc", + .parent_names = (const char * []) { "nadc" }, + .num_parents = 1, + .ops = &aic32x4_div_ops, + .reg = AIC32X4_MADC, + }, };
static struct clk *aic32x4_register_clk(struct device *dev, diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 6b682743cf9b..aac4c3f335d1 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -52,11 +52,11 @@ struct aic32x4_rate_divs { u32 rate; unsigned long pll_rate; u16 dosr; - u8 ndac; - u8 mdac; + unsigned long ndac_rate; + unsigned long mdac_rate; u8 aosr; - u8 nadc; - u8 madc; + unsigned long nadc_rate; + unsigned long madc_rate; u8 blck_N; };
@@ -307,34 +307,54 @@ static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
static const struct aic32x4_rate_divs aic32x4_divs[] = { /* 8k rate */ - { 12000000, 8000, 57120000, 768, 5, 3, 128, 5, 18, 24 }, - { 24000000, 8000, 57120000, 768, 15, 1, 64, 45, 4, 24 }, - { 25000000, 8000, 32620000, 768, 15, 1, 64, 45, 4, 24 }, + { 12000000, 8000, 57120000, 768, 18432000, 6144000, 128, 18432000, + 1024000, 24 }, + { 24000000, 8000, 57120000, 768, 6144000, 6144000, 64, 2048000, + 512000, 24 }, + { 25000000, 8000, 32620000, 768, 6144000, 6144000, 64, 2048000, + 512000, 24 }, /* 11.025k rate */ - { 12000000, 11025, 44217600, 512, 8, 2, 128, 8, 8, 16 }, - { 24000000, 11025, 44217600, 512, 16, 1, 64, 32, 4, 16 }, + { 12000000, 11025, 44217600, 512, 11289600, 5644800, 128, 11289600, + 1411200, 16 }, + { 24000000, 11025, 44217600, 512, 5644800, 5644800, 64, 2822400, + 705600, 16 }, /* 16k rate */ - { 12000000, 16000, 57120000, 384, 5, 3, 128, 5, 9, 12 }, - { 24000000, 16000, 57120000, 384, 15, 1, 64, 18, 5, 12 }, - { 25000000, 16000, 32620000, 384, 15, 1, 64, 18, 5, 12 }, + { 12000000, 16000, 57120000, 384, 18432000, 6144000, 128, 18432000, + 2048000, 12 }, + { 24000000, 16000, 57120000, 384, 6144000, 6144000, 64, 5120000, + 1024000, 12 }, + { 25000000, 16000, 32620000, 384, 6144000, 6144000, 64, 5120000, + 1024000, 12 }, /* 22.05k rate */ - { 12000000, 22050, 44217600, 256, 4, 4, 128, 4, 8, 8 }, - { 24000000, 22050, 44217600, 256, 16, 1, 64, 16, 4, 8 }, - { 25000000, 22050, 19713750, 256, 16, 1, 64, 16, 4, 8 }, + { 12000000, 22050, 44217600, 256, 22579200, 5644800, 128, 22579200, + 2822400, 8 }, + { 24000000, 22050, 44217600, 256, 5644800, 5644800, 64, 5644800, + 1411200, 8 }, + { 25000000, 22050, 19713750, 256, 5644800, 5644800, 64, 5644800, + 1411200, 8 }, /* 32k rate */ - { 12000000, 32000, 14112000, 192, 2, 7, 64, 2, 21, 6 }, - { 24000000, 32000, 14112000, 192, 7, 2, 64, 7, 6, 6 }, + { 12000000, 32000, 14112000, 192, 43008000, 6144000, 64, 43008000, + 2048000, 6 }, + { 24000000, 32000, 14112000, 192, 12288000, 6144000, 64, 12288000, + 2048000, 6 }, /* 44.1k rate */ - { 12000000, 44100, 44217600, 128, 2, 8, 128, 2, 8, 4 }, - { 24000000, 44100, 44217600, 128, 8, 2, 64, 8, 4, 4 }, - { 25000000, 44100, 19713750, 128, 8, 2, 64, 8, 4, 4 }, + { 12000000, 44100, 44217600, 128, 45158400, 5644800, 128, 45158400, + 5644800, 4 }, + { 24000000, 44100, 44217600, 128, 11289600, 5644800, 64, 11289600, + 2822400, 4 }, + { 25000000, 44100, 19713750, 128, 11289600, 5644800, 64, 11289600, + 2822400, 4 }, /* 48k rate */ - { 12000000, 48000, 18432000, 128, 2, 8, 128, 2, 8, 4 }, - { 24000000, 48000, 18432000, 128, 8, 2, 64, 8, 4, 4 }, - { 25000000, 48000, 75626250, 128, 8, 2, 64, 8, 4, 4 }, + { 12000000, 48000, 18432000, 128, 49152000, 6144000, 128, 49152000, + 6144000, 4 }, + { 24000000, 48000, 18432000, 128, 12288000, 6144000, 64, 12288000, + 3072000, 4 }, + { 25000000, 48000, 75626250, 128, 12288000, 6144000, 64, 12288000, + 3072000, 4 },
/* 96k rate */ - { 25000000, 96000, 75626250, 64, 4, 4, 64, 4, 4, 1 }, + { 25000000, 96000, 75626250, 64, 24576000, 6144000, 64, 24576000, + 6144000, 1 }, };
static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { @@ -705,6 +725,10 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component,
struct clk_bulk_data clocks[] = { { .id = "pll" }, + { .id = "nadc" }, + { .id = "madc" }, + { .id = "ndac" }, + { .id = "mdac" }, };
i = aic32x4_get_divs(parent_rate, sample_rate); @@ -717,33 +741,21 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, if (ret) return ret;
- clk_set_rate(clocks[0].clk, sample_rate); + clk_set_rate(clocks[0].clk, aic32x4_divs[i].pll_rate); + clk_set_rate(clocks[1].clk, aic32x4_divs[i].nadc_rate); + clk_set_rate(clocks[2].clk, aic32x4_divs[i].madc_rate); + clk_set_rate(clocks[3].clk, aic32x4_divs[i].ndac_rate); + clk_set_rate(clocks[4].clk, aic32x4_divs[i].mdac_rate);
/* DAC_MOD_CLK as BDIV_CLKIN */ snd_soc_component_update_bits(component, AIC32X4_IFACE3, AIC32X4_BDIVCLK_MASK, AIC32X4_DACMOD2BCLK << AIC32X4_BDIVCLK_SHIFT);
- /* NDAC divider value */ - snd_soc_component_update_bits(component, AIC32X4_NDAC, - AIC32X4_NDAC_MASK, aic32x4_divs[i].ndac); - - /* MDAC divider value */ - snd_soc_component_update_bits(component, AIC32X4_MDAC, - AIC32X4_MDAC_MASK, aic32x4_divs[i].mdac); - /* DOSR MSB & LSB values */ snd_soc_component_write(component, AIC32X4_DOSRMSB, aic32x4_divs[i].dosr >> 8); snd_soc_component_write(component, AIC32X4_DOSRLSB, (aic32x4_divs[i].dosr & 0xff));
- /* NADC divider value */ - snd_soc_component_update_bits(component, AIC32X4_NADC, - AIC32X4_NADC_MASK, aic32x4_divs[i].nadc); - - /* MADC divider value */ - snd_soc_component_update_bits(component, AIC32X4_MADC, - AIC32X4_MADC_MASK, aic32x4_divs[i].madc); - /* AOSR value */ snd_soc_component_write(component, AIC32X4_AOSR, aic32x4_divs[i].aosr);
@@ -972,6 +984,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component)
struct clk_bulk_data clocks[] = { { .id = "codec_clkin" }, + { .id = "pll" }, };
ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks); diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index e2b65bbba7c2..6ede877b00a0 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -206,6 +206,10 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); #define AIC32X4_RMICPGANIN_IN1L_10K 0x10 #define AIC32X4_RMICPGANIN_CM1R_10K 0x40
+/* Common mask and enable for all of the dividers */ +#define AIC32X4_DIVEN BIT(7) +#define AIC32X4_DIV_MASK GENMASK(6, 0) + /* Clock Limits */ #define AIC32X4_MAX_PLL_CLKIN 20000000
On Mon, Mar 18, 2019 at 08:37:47PM -0700, Annaliese McDermond wrote:
Model and manage DAC/ADC dividers as components in the Core Clock Framework. This should allow us to do some more complex clock management and power control. Also, some of the on-board chip clocks can be exposed to the outside, and this change will make those clocks easier to consume by other parts of the kernel. Signed-off-by: Annaliese McDermond <nh6z@nh6z.net>
Signed-off-by: Annaliese McDermond nh6z@nh6z.net
Something seems to have gone weird with indentation in the commit message here?
Model and manage BDIV divider as components in the Core Clock Framework. This should allow us to do some more complex clock management and power control. Also, some of the on-board chip clocks can be exposed to the outside, and this change will make those clocks easier to consume by other parts of the kernel.
Signed-off-by: Annaliese McDermond nh6z@nh6z.net --- sound/soc/codecs/tlv320aic32x4-clk.c | 36 ++++++++++++++++++ sound/soc/codecs/tlv320aic32x4.c | 56 +++++++++++++--------------- 2 files changed, 62 insertions(+), 30 deletions(-)
diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c index 87103b3878ab..2995ab47cb0f 100644 --- a/sound/soc/codecs/tlv320aic32x4-clk.c +++ b/sound/soc/codecs/tlv320aic32x4-clk.c @@ -351,6 +351,34 @@ static const struct clk_ops aic32x4_div_ops = { .recalc_rate = clk_aic32x4_div_recalc_rate, };
+static int clk_aic32x4_bdiv_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_aic32x4 *mux = to_clk_aic32x4(hw); + + return regmap_update_bits(mux->regmap, AIC32X4_IFACE3, + AIC32X4_BDIVCLK_MASK, index); +} + +static u8 clk_aic32x4_bdiv_get_parent(struct clk_hw *hw) +{ + struct clk_aic32x4 *mux = to_clk_aic32x4(hw); + unsigned int val; + + regmap_read(mux->regmap, AIC32X4_IFACE3, &val); + + return val & AIC32X4_BDIVCLK_MASK; +} + +static const struct clk_ops aic32x4_bdiv_ops = { + .enable = clk_aic32x4_div_enable, + .disable = clk_aic32x4_div_disable, + .set_parent = clk_aic32x4_bdiv_set_parent, + .get_parent = clk_aic32x4_bdiv_get_parent, + .set_rate = clk_aic32x4_div_set_rate, + .round_rate = clk_aic32x4_div_round_rate, + .recalc_rate = clk_aic32x4_div_recalc_rate, +}; + static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { { .name = "pll", @@ -396,6 +424,14 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { .ops = &aic32x4_div_ops, .reg = AIC32X4_MADC, }, + { + .name = "bdiv", + .parent_names = + (const char *[]) { "ndac", "mdac", "nadc", "madc" }, + .num_parents = 4, + .ops = &aic32x4_bdiv_ops, + .reg = AIC32X4_BCLKN, + }, };
static struct clk *aic32x4_register_clk(struct device *dev, diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index aac4c3f335d1..4e170cdc05ce 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -57,7 +57,7 @@ struct aic32x4_rate_divs { u8 aosr; unsigned long nadc_rate; unsigned long madc_rate; - u8 blck_N; + unsigned long bdiv_rate; };
struct aic32x4_priv { @@ -308,53 +308,53 @@ static const struct snd_kcontrol_new aic32x4_snd_controls[] = { static const struct aic32x4_rate_divs aic32x4_divs[] = { /* 8k rate */ { 12000000, 8000, 57120000, 768, 18432000, 6144000, 128, 18432000, - 1024000, 24 }, + 1024000, 256000 }, { 24000000, 8000, 57120000, 768, 6144000, 6144000, 64, 2048000, - 512000, 24 }, + 512000, 256000 }, { 25000000, 8000, 32620000, 768, 6144000, 6144000, 64, 2048000, - 512000, 24 }, + 512000, 256000 }, /* 11.025k rate */ { 12000000, 11025, 44217600, 512, 11289600, 5644800, 128, 11289600, - 1411200, 16 }, + 1411200, 352800 }, { 24000000, 11025, 44217600, 512, 5644800, 5644800, 64, 2822400, - 705600, 16 }, + 705600, 352800 }, /* 16k rate */ { 12000000, 16000, 57120000, 384, 18432000, 6144000, 128, 18432000, - 2048000, 12 }, + 2048000, 512000 }, { 24000000, 16000, 57120000, 384, 6144000, 6144000, 64, 5120000, - 1024000, 12 }, + 1024000, 512000 }, { 25000000, 16000, 32620000, 384, 6144000, 6144000, 64, 5120000, - 1024000, 12 }, + 1024000, 512000 }, /* 22.05k rate */ { 12000000, 22050, 44217600, 256, 22579200, 5644800, 128, 22579200, - 2822400, 8 }, + 2822400, 705600 }, { 24000000, 22050, 44217600, 256, 5644800, 5644800, 64, 5644800, - 1411200, 8 }, + 1411200, 705600 }, { 25000000, 22050, 19713750, 256, 5644800, 5644800, 64, 5644800, - 1411200, 8 }, + 1411200, 705600 }, /* 32k rate */ { 12000000, 32000, 14112000, 192, 43008000, 6144000, 64, 43008000, - 2048000, 6 }, + 2048000, 1024000 }, { 24000000, 32000, 14112000, 192, 12288000, 6144000, 64, 12288000, - 2048000, 6 }, + 2048000, 1024000 }, /* 44.1k rate */ { 12000000, 44100, 44217600, 128, 45158400, 5644800, 128, 45158400, - 5644800, 4 }, + 5644800, 1411200 }, { 24000000, 44100, 44217600, 128, 11289600, 5644800, 64, 11289600, - 2822400, 4 }, + 2822400, 1411200 }, { 25000000, 44100, 19713750, 128, 11289600, 5644800, 64, 11289600, - 2822400, 4 }, + 2822400, 1411200 }, /* 48k rate */ { 12000000, 48000, 18432000, 128, 49152000, 6144000, 128, 49152000, - 6144000, 4 }, + 6144000, 1536000 }, { 24000000, 48000, 18432000, 128, 12288000, 6144000, 64, 12288000, - 3072000, 4 }, + 3072000, 1536000 }, { 25000000, 48000, 75626250, 128, 12288000, 6144000, 64, 12288000, - 3072000, 4 }, + 3072000, 1536000 },
/* 96k rate */ { 25000000, 96000, 75626250, 64, 24576000, 6144000, 64, 24576000, - 6144000, 1 }, + 6144000, 3072000 }, };
static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { @@ -729,6 +729,7 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, { .id = "madc" }, { .id = "ndac" }, { .id = "mdac" }, + { .id = "bdiv" }, };
i = aic32x4_get_divs(parent_rate, sample_rate); @@ -746,11 +747,7 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, clk_set_rate(clocks[2].clk, aic32x4_divs[i].madc_rate); clk_set_rate(clocks[3].clk, aic32x4_divs[i].ndac_rate); clk_set_rate(clocks[4].clk, aic32x4_divs[i].mdac_rate); - - /* DAC_MOD_CLK as BDIV_CLKIN */ - snd_soc_component_update_bits(component, AIC32X4_IFACE3, - AIC32X4_BDIVCLK_MASK, - AIC32X4_DACMOD2BCLK << AIC32X4_BDIVCLK_SHIFT); + clk_set_rate(clocks[5].clk, aic32x4_divs[i].bdiv_rate);
/* DOSR MSB & LSB values */ snd_soc_component_write(component, AIC32X4_DOSRMSB, aic32x4_divs[i].dosr >> 8); @@ -759,10 +756,6 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, /* AOSR value */ snd_soc_component_write(component, AIC32X4_AOSR, aic32x4_divs[i].aosr);
- /* BCLK N divider */ - snd_soc_component_update_bits(component, AIC32X4_BCLKN, - AIC32X4_BCLK_MASK, aic32x4_divs[i].blck_N); - return 0; }
@@ -985,6 +978,8 @@ static int aic32x4_component_probe(struct snd_soc_component *component) struct clk_bulk_data clocks[] = { { .id = "codec_clkin" }, { .id = "pll" }, + { .id = "bdiv" }, + { .id = "mdac" }, };
ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks); @@ -1003,6 +998,7 @@ static int aic32x4_component_probe(struct snd_soc_component *component) aic32x4_setup_gpios(component);
clk_set_parent(clocks[0].clk, clocks[1].clk); + clk_set_parent(clocks[2].clk, clocks[3].clk);
/* Power platform configuration */ if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) {
Control the clock gating to the various clock components to use the CCF. This allows us to prepare_enalbe only 3 clocks and the relationships assigned to them will cause upstream clockss to enable automatically. Additionally we can do this in a single call to the CCF.
Signed-off-by: Annaliese McDermond nh6z@nh6z.net --- sound/soc/codecs/tlv320aic32x4.c | 71 ++++++-------------------------- 1 file changed, 13 insertions(+), 58 deletions(-)
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 4e170cdc05ce..ecf692e346b6 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -818,75 +818,30 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute) static int aic32x4_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { - struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); int ret;
+ struct clk_bulk_data clocks[] = { + { .id = "madc" }, + { .id = "mdac" }, + { .id = "bdiv" }, + }; + + ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks); + if (ret) + return ret; + switch (level) { case SND_SOC_BIAS_ON: - /* Switch on master clock */ - ret = clk_prepare_enable(aic32x4->mclk); + ret = clk_bulk_prepare_enable(ARRAY_SIZE(clocks), clocks); if (ret) { - dev_err(component->dev, "Failed to enable master clock\n"); + dev_err(component->dev, "Failed to enable clocks\n"); return ret; } - - /* Switch on PLL */ - snd_soc_component_update_bits(component, AIC32X4_PLLPR, - AIC32X4_PLLEN, AIC32X4_PLLEN); - - /* Switch on NDAC Divider */ - snd_soc_component_update_bits(component, AIC32X4_NDAC, - AIC32X4_NDACEN, AIC32X4_NDACEN); - - /* Switch on MDAC Divider */ - snd_soc_component_update_bits(component, AIC32X4_MDAC, - AIC32X4_MDACEN, AIC32X4_MDACEN); - - /* Switch on NADC Divider */ - snd_soc_component_update_bits(component, AIC32X4_NADC, - AIC32X4_NADCEN, AIC32X4_NADCEN); - - /* Switch on MADC Divider */ - snd_soc_component_update_bits(component, AIC32X4_MADC, - AIC32X4_MADCEN, AIC32X4_MADCEN); - - /* Switch on BCLK_N Divider */ - snd_soc_component_update_bits(component, AIC32X4_BCLKN, - AIC32X4_BCLKEN, AIC32X4_BCLKEN); break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - /* Initial cold start */ - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) - break; - - /* Switch off BCLK_N Divider */ - snd_soc_component_update_bits(component, AIC32X4_BCLKN, - AIC32X4_BCLKEN, 0); - - /* Switch off MADC Divider */ - snd_soc_component_update_bits(component, AIC32X4_MADC, - AIC32X4_MADCEN, 0); - - /* Switch off NADC Divider */ - snd_soc_component_update_bits(component, AIC32X4_NADC, - AIC32X4_NADCEN, 0); - - /* Switch off MDAC Divider */ - snd_soc_component_update_bits(component, AIC32X4_MDAC, - AIC32X4_MDACEN, 0); - - /* Switch off NDAC Divider */ - snd_soc_component_update_bits(component, AIC32X4_NDAC, - AIC32X4_NDACEN, 0); - - /* Switch off PLL */ - snd_soc_component_update_bits(component, AIC32X4_PLLPR, - AIC32X4_PLLEN, 0); - - /* Switch off master clock */ - clk_disable_unprepare(aic32x4->mclk); + clk_bulk_disable_unprepare(ARRAY_SIZE(clocks), clocks); break; case SND_SOC_BIAS_OFF: break;
Move these to separate helper functions. This looks cleaner and fits better with the new clock setting in CCF.
Signed-off-by: Annaliese McDermond nh6z@nh6z.net --- sound/soc/codecs/tlv320aic32x4.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-)
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index ecf692e346b6..1d347adbcc30 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -716,6 +716,20 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) return 0; }
+static int aic32x4_set_aosr(struct snd_soc_component *component, u8 aosr) +{ + return snd_soc_component_write(component, AIC32X4_AOSR, aosr); +} + +static int aic32x4_set_dosr(struct snd_soc_component *component, u16 dosr) +{ + snd_soc_component_write(component, AIC32X4_DOSRMSB, dosr >> 8); + snd_soc_component_write(component, AIC32X4_DOSRLSB, + (dosr & 0xff)); + + return 0; +} + static int aic32x4_setup_clocks(struct snd_soc_component *component, unsigned int sample_rate, unsigned int parent_rate) @@ -748,13 +762,8 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, clk_set_rate(clocks[3].clk, aic32x4_divs[i].ndac_rate); clk_set_rate(clocks[4].clk, aic32x4_divs[i].mdac_rate); clk_set_rate(clocks[5].clk, aic32x4_divs[i].bdiv_rate); - - /* DOSR MSB & LSB values */ - snd_soc_component_write(component, AIC32X4_DOSRMSB, aic32x4_divs[i].dosr >> 8); - snd_soc_component_write(component, AIC32X4_DOSRLSB, (aic32x4_divs[i].dosr & 0xff)); - - /* AOSR value */ - snd_soc_component_write(component, AIC32X4_AOSR, aic32x4_divs[i].aosr); + aic32x4_set_aosr(component, aic32x4_divs[i].aosr); + aic32x4_set_dosr(component, aic32x4_divs[i].dosr);
return 0; }
The existing code uses a static lookup table to determine the settings of the various clock devices on board the chip. This is limiting in a couple of ways. First, this doesn't allow for any master clock rates other than the three that have been precalculated. Additionally, new sample rates are difficult to add to the table. Witness that the chip is capable of 192000 Hz sampling, but it is not provided by this driver. Last, if the driver is clocked by something that isn't a crystal, the upstream clock may not be able to achieve exactly the rate requested in the driver. This will mean that clocking will be slightly off for the sampling clock or that it won't work at all.
This patch determines the settings for all of the clocks at runtime considering the real conditions of the clocks in the system. The rules for the clocks are in TI's SLAA557 application guide on pages 37, 51 and 77.
Signed-off-by: Annaliese McDermond nh6z@nh6z.net --- sound/soc/codecs/tlv320aic32x4.c | 182 ++++++++++++++----------------- sound/soc/codecs/tlv320aic32x4.h | 4 +- 2 files changed, 87 insertions(+), 99 deletions(-)
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 1d347adbcc30..b8b97b211d46 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -47,19 +47,6 @@
#include "tlv320aic32x4.h"
-struct aic32x4_rate_divs { - u32 mclk; - u32 rate; - unsigned long pll_rate; - u16 dosr; - unsigned long ndac_rate; - unsigned long mdac_rate; - u8 aosr; - unsigned long nadc_rate; - unsigned long madc_rate; - unsigned long bdiv_rate; -}; - struct aic32x4_priv { struct regmap *regmap; u32 sysclk; @@ -305,58 +292,6 @@ static const struct snd_kcontrol_new aic32x4_snd_controls[] = { 0, 0x0F, 0), };
-static const struct aic32x4_rate_divs aic32x4_divs[] = { - /* 8k rate */ - { 12000000, 8000, 57120000, 768, 18432000, 6144000, 128, 18432000, - 1024000, 256000 }, - { 24000000, 8000, 57120000, 768, 6144000, 6144000, 64, 2048000, - 512000, 256000 }, - { 25000000, 8000, 32620000, 768, 6144000, 6144000, 64, 2048000, - 512000, 256000 }, - /* 11.025k rate */ - { 12000000, 11025, 44217600, 512, 11289600, 5644800, 128, 11289600, - 1411200, 352800 }, - { 24000000, 11025, 44217600, 512, 5644800, 5644800, 64, 2822400, - 705600, 352800 }, - /* 16k rate */ - { 12000000, 16000, 57120000, 384, 18432000, 6144000, 128, 18432000, - 2048000, 512000 }, - { 24000000, 16000, 57120000, 384, 6144000, 6144000, 64, 5120000, - 1024000, 512000 }, - { 25000000, 16000, 32620000, 384, 6144000, 6144000, 64, 5120000, - 1024000, 512000 }, - /* 22.05k rate */ - { 12000000, 22050, 44217600, 256, 22579200, 5644800, 128, 22579200, - 2822400, 705600 }, - { 24000000, 22050, 44217600, 256, 5644800, 5644800, 64, 5644800, - 1411200, 705600 }, - { 25000000, 22050, 19713750, 256, 5644800, 5644800, 64, 5644800, - 1411200, 705600 }, - /* 32k rate */ - { 12000000, 32000, 14112000, 192, 43008000, 6144000, 64, 43008000, - 2048000, 1024000 }, - { 24000000, 32000, 14112000, 192, 12288000, 6144000, 64, 12288000, - 2048000, 1024000 }, - /* 44.1k rate */ - { 12000000, 44100, 44217600, 128, 45158400, 5644800, 128, 45158400, - 5644800, 1411200 }, - { 24000000, 44100, 44217600, 128, 11289600, 5644800, 64, 11289600, - 2822400, 1411200 }, - { 25000000, 44100, 19713750, 128, 11289600, 5644800, 64, 11289600, - 2822400, 1411200 }, - /* 48k rate */ - { 12000000, 48000, 18432000, 128, 49152000, 6144000, 128, 49152000, - 6144000, 1536000 }, - { 24000000, 48000, 18432000, 128, 12288000, 6144000, 64, 12288000, - 3072000, 1536000 }, - { 25000000, 48000, 75626250, 128, 12288000, 6144000, 64, 12288000, - 3072000, 1536000 }, - - /* 96k rate */ - { 25000000, 96000, 75626250, 64, 24576000, 6144000, 64, 24576000, - 6144000, 3072000 }, -}; - static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0), SOC_DAPM_SINGLE("IN1_L Switch", AIC32X4_HPLROUTE, 2, 1, 0), @@ -628,20 +563,6 @@ const struct regmap_config aic32x4_regmap_config = { }; EXPORT_SYMBOL(aic32x4_regmap_config);
-static inline int aic32x4_get_divs(int mclk, int rate) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(aic32x4_divs); i++) { - if ((aic32x4_divs[i].rate == rate) - && (aic32x4_divs[i].mclk == mclk)) { - return i; - } - } - printk(KERN_ERR "aic32x4: master clock and sample rate is not supported\n"); - return -EINVAL; -} - static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { @@ -731,11 +652,17 @@ static int aic32x4_set_dosr(struct snd_soc_component *component, u16 dosr) }
static int aic32x4_setup_clocks(struct snd_soc_component *component, - unsigned int sample_rate, - unsigned int parent_rate) + unsigned int sample_rate) { - int i; + u8 aosr; + u16 dosr; + u8 adc_resource_class, dac_resource_class; + u8 madc, nadc, mdac, ndac, max_nadc, min_mdac, max_ndac; + u8 dosr_increment; + u16 max_dosr, min_dosr; + unsigned long mclk_rate, adc_clock_rate, dac_clock_rate; int ret; + struct clk *mclk;
struct clk_bulk_data clocks[] = { { .id = "pll" }, @@ -745,28 +672,87 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, { .id = "mdac" }, { .id = "bdiv" }, }; - - i = aic32x4_get_divs(parent_rate, sample_rate); - if (i < 0) { - printk(KERN_ERR "aic32x4: sampling rate not supported\n"); - return i; - } - ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks); if (ret) return ret;
- clk_set_rate(clocks[0].clk, aic32x4_divs[i].pll_rate); - clk_set_rate(clocks[1].clk, aic32x4_divs[i].nadc_rate); - clk_set_rate(clocks[2].clk, aic32x4_divs[i].madc_rate); - clk_set_rate(clocks[3].clk, aic32x4_divs[i].ndac_rate); - clk_set_rate(clocks[4].clk, aic32x4_divs[i].mdac_rate); - clk_set_rate(clocks[5].clk, aic32x4_divs[i].bdiv_rate); - aic32x4_set_aosr(component, aic32x4_divs[i].aosr); - aic32x4_set_dosr(component, aic32x4_divs[i].dosr); + mclk = clk_get_parent(clocks[1].clk); + mclk_rate = clk_get_rate(mclk); + + if (sample_rate <= 48000) { + aosr = 128; + adc_resource_class = 6; + dac_resource_class = 8; + dosr_increment = 8; + } else if (sample_rate <= 96000) { + aosr = 64; + adc_resource_class = 6; + dac_resource_class = 8; + dosr_increment = 4; + } else if (sample_rate == 192000) { + aosr = 32; + adc_resource_class = 3; + dac_resource_class = 4; + dosr_increment = 2; + } else { + dev_err(component->dev, "Sampling rate not supported\n"); + return -EINVAL; + }
+ madc = DIV_ROUND_UP((32 * adc_resource_class), aosr); + max_dosr = (AIC32X4_MAX_DOSR_FREQ / sample_rate / dosr_increment) * + dosr_increment; + min_dosr = (AIC32X4_MIN_DOSR_FREQ / sample_rate / dosr_increment) * + dosr_increment; + max_nadc = AIC32X4_MAX_CODEC_CLKIN_FREQ / (madc * aosr * sample_rate); + + for (nadc = max_nadc; nadc > 0; --nadc) { + adc_clock_rate = nadc * madc * aosr * sample_rate; + for (dosr = max_dosr; dosr >= min_dosr; + dosr -= dosr_increment) { + min_mdac = DIV_ROUND_UP((32 * dac_resource_class), dosr); + max_ndac = AIC32X4_MAX_CODEC_CLKIN_FREQ / + (min_mdac * dosr * sample_rate); + for (mdac = min_mdac; mdac <= 128; ++mdac) { + for (ndac = max_ndac; ndac > 0; --ndac) { + dac_clock_rate = ndac * mdac * dosr * + sample_rate; + if (dac_clock_rate == adc_clock_rate) { + if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0) + continue; + + clk_set_rate(clocks[0].clk, + dac_clock_rate); + + clk_set_rate(clocks[1].clk, + sample_rate * aosr * + madc); + clk_set_rate(clocks[2].clk, + sample_rate * aosr); + aic32x4_set_aosr(component, + aosr); + + clk_set_rate(clocks[3].clk, + sample_rate * dosr * + mdac); + clk_set_rate(clocks[4].clk, + sample_rate * dosr); + aic32x4_set_dosr(component, + dosr); + + clk_set_rate(clocks[5].clk, + sample_rate * 32); return 0; } + } + } + } + } + + dev_err(component->dev, + "Could not set clocks to support sample rate.\n"); + return -EINVAL; +}
static int aic32x4_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, @@ -777,7 +763,7 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, u8 iface1_reg = 0; u8 dacsetup_reg = 0;
- aic32x4_setup_clocks(component, params_rate(params), aic32x4->sysclk); + aic32x4_setup_clocks(component, params_rate(params));
switch (params_width(params)) { case 16: diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index 6ede877b00a0..88205bc97198 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -211,7 +211,9 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); #define AIC32X4_DIV_MASK GENMASK(6, 0)
/* Clock Limits */ +#define AIC32X4_MAX_DOSR_FREQ 6200000 +#define AIC32X4_MIN_DOSR_FREQ 2800000 +#define AIC32X4_MAX_CODEC_CLKIN_FREQ 110000000 #define AIC32X4_MAX_PLL_CLKIN 20000000
- #endif /* _TLV320AIC32X4_H */
The clocks are double-disabled when they are first coming up because they are already off when it first moves into STANDBY. Check if the state is already off and leave things alone if it is.
Signed-off-by: Annaliese McDermond nh6z@nh6z.net --- sound/soc/codecs/tlv320aic32x4.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index b8b97b211d46..10f0e07889b7 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -814,6 +814,8 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { int ret; + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component);
struct clk_bulk_data clocks[] = { { .id = "madc" }, @@ -836,6 +838,7 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: + if (dapm->bias_level != SND_SOC_BIAS_OFF) clk_bulk_disable_unprepare(ARRAY_SIZE(clocks), clocks); break; case SND_SOC_BIAS_OFF:
On Mon, Mar 18, 2019 at 08:37:52PM -0700, Annaliese McDermond wrote:
The clocks are double-disabled when they are first coming up because they are already off when it first moves into STANDBY. Check if the state is already off and leave things alone if it is.
This looks like a bug fix for the existing code prior to the series - changes like this should be put at the start of the series so that they can be more easily sent as fixes without having to wait for the next merge window.
Sysclk is not used by anything anymore. Remove support for it. All that information now comes from the clock tree.
Signed-off-by: Annaliese McDermond nh6z@nh6z.net --- sound/soc/codecs/tlv320aic32x4.c | 19 ------------------- 1 file changed, 19 deletions(-)
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 10f0e07889b7..27babe48d5bc 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -49,7 +49,6 @@
struct aic32x4_priv { struct regmap *regmap; - u32 sysclk; u32 power_cfg; u32 micpga_routing; bool swapdacs; @@ -563,23 +562,6 @@ const struct regmap_config aic32x4_regmap_config = { }; EXPORT_SYMBOL(aic32x4_regmap_config);
-static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, - int clk_id, unsigned int freq, int dir) -{ - struct snd_soc_component *component = codec_dai->component; - struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); - - switch (freq) { - case 12000000: - case 24000000: - case 25000000: - aic32x4->sysclk = freq; - return 0; - } - printk(KERN_ERR "aic32x4: invalid frequency to set DAI system clock\n"); - return -EINVAL; -} - static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_component *component = codec_dai->component; @@ -855,7 +837,6 @@ static const struct snd_soc_dai_ops aic32x4_ops = { .hw_params = aic32x4_hw_params, .digital_mute = aic32x4_mute, .set_fmt = aic32x4_set_dai_fmt, - .set_sysclk = aic32x4_set_dai_sysclk, };
static struct snd_soc_dai_driver aic32x4_dai = {
On Mon, Mar 18, 2019 at 08:37:53PM -0700, Annaliese McDermond wrote:
Sysclk is not used by anything anymore. Remove support for it. All that information now comes from the clock tree.
Have you checked for existing users here? If the operation is removed existing users will get errors, not all of them will be prepared for that.
On Mar 19, 2019, at 8:37 AM, Mark Brown broonie@kernel.org wrote:
On Mon, Mar 18, 2019 at 08:37:53PM -0700, Annaliese McDermond wrote:
Sysclk is not used by anything anymore. Remove support for it. All that information now comes from the clock tree.
Have you checked for existing users here? If the operation is removed existing users will get errors, not all of them will be prepared for that.
I haven’t checked with existing users. A better implementation may be that we just use clk_set_rate on whatever the sysclk is to set the upstream rate. That way we can preserve compatibility.
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
-- Annaliese McDermond nh6z@nh6z.net
On Tue, Mar 19, 2019 at 09:18:01AM -0700, Annaliese McDermond wrote:
Have you checked for existing users here? If the operation is removed existing users will get errors, not all of them will be prepared for that.
I haven’t checked with existing users. A better implementation may be that we just use clk_set_rate on whatever the sysclk is to set the upstream rate. That way we can preserve compatibility.
Yes, that'd be the simplest thing.
mclk is not used by anything anymore. Remove support for it. All that information now comes from the clock tree.
Signed-off-by: Annaliese McDermond nh6z@nh6z.net --- sound/soc/codecs/tlv320aic32x4.c | 7 ------- 1 file changed, 7 deletions(-)
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 27babe48d5bc..2940cb2e5a4a 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -53,7 +53,6 @@ struct aic32x4_priv { u32 micpga_routing; bool swapdacs; int rstn_gpio; - struct clk *mclk; const char *mclk_name;
struct regulator *supply_ldo; @@ -1160,12 +1159,6 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap) aic32x4->mclk_name = "mclk"; }
- aic32x4->mclk = devm_clk_get(dev, "mclk"); - if (IS_ERR(aic32x4->mclk)) { - dev_err(dev, "Failed getting the mclk. The current implementation does not support the usage of this codec without mclk\n"); - return PTR_ERR(aic32x4->mclk); - } - ret = aic32x4_register_clocks(dev, aic32x4->mclk_name); if (ret) return ret;
Different processing blocks are required for different sampling rates and power parameters. Set the processing blocks based on this information.
Signed-off-by: Annaliese McDermond nh6z@nh6z.net --- sound/soc/codecs/tlv320aic32x4.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 2940cb2e5a4a..3a42a08d2a7a 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -632,6 +632,18 @@ static int aic32x4_set_dosr(struct snd_soc_component *component, u16 dosr) return 0; }
+static int aic32x4_set_processing_blocks(struct snd_soc_component *component, + u8 r_block, u8 p_block) +{ + if (r_block > 18 || p_block > 25) + return -EINVAL; + + snd_soc_component_write(component, AIC32X4_ADCSPB, r_block); + snd_soc_component_write(component, AIC32X4_DACSPB, p_block); + + return 0; +} + static int aic32x4_setup_clocks(struct snd_soc_component *component, unsigned int sample_rate) { @@ -665,16 +677,19 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, adc_resource_class = 6; dac_resource_class = 8; dosr_increment = 8; + aic32x4_set_processing_blocks(component, 1, 1); } else if (sample_rate <= 96000) { aosr = 64; adc_resource_class = 6; dac_resource_class = 8; dosr_increment = 4; + aic32x4_set_processing_blocks(component, 1, 9); } else if (sample_rate == 192000) { aosr = 32; adc_resource_class = 3; dac_resource_class = 4; dosr_increment = 2; + aic32x4_set_processing_blocks(component, 13, 19); } else { dev_err(component->dev, "Sampling rate not supported\n"); return -EINVAL;
On Mon, Mar 18, 2019 at 08:37:55PM -0700, Annaliese McDermond wrote:
Different processing blocks are required for different sampling rates and power parameters. Set the processing blocks based on this information.
Is this another bug fix or does it depend on the rest of the series somehow?
On Mar 19, 2019, at 8:37 AM, Mark Brown broonie@kernel.org wrote:
On Mon, Mar 18, 2019 at 08:37:55PM -0700, Annaliese McDermond wrote:
Different processing blocks are required for different sampling rates and power parameters. Set the processing blocks based on this information.
Is this another bug fix or does it depend on the rest of the series somehow?
It’s not really a bug fix. The codec has a variety of “processing blocks” that enable different filters (there’s an onboard FIR, Biquad, etc.) and such. The big thing they change with regard to how the driver is written now is that they change the decimation filter used. The documentation specifies to use certain filters for certain sample rates. This hasn’t mattered very much in the past because the default filters (P1 and R1) are the ones recommended for 48kHz and below. This is particularly important for 192kHz operation because it won’t work with P1 and R1. So, it’s not really a bug fix. It’s part of the road to enabling 192kHz operation.
-- Annaliese McDermond nh6z@nh6z.net
On Tue, Mar 19, 2019 at 09:24:00AM -0700, Annaliese McDermond wrote:
It’s not really a bug fix. The codec has a variety of “processing blocks” that enable different filters (there’s an onboard FIR, Biquad, etc.) and such. The big thing they change with regard to how the driver is written now is that they change the decimation filter used. The documentation specifies to use certain filters for certain sample rates. This hasn’t mattered very much in the past because the default filters (P1 and R1) are the ones recommended for 48kHz and below. This is particularly important for 192kHz operation because it won’t work with P1 and R1. So, it’s not really a bug fix. It’s part of the road to enabling 192kHz operation.
It does sound like a bug fix for higher sample rates (96kHz is supported?) even if it's a fairly niche case.
The clocking and processing blocks are now properly set up to support 192000 sample rates. Allow drivers to ask for that.
Signed-off-by: Annaliese McDermond nh6z@nh6z.net --- sound/soc/codecs/tlv320aic32x4.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 3a42a08d2a7a..3edf712d66e2 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -843,7 +843,7 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component, return 0; }
-#define AIC32X4_RATES SNDRV_PCM_RATE_8000_96000 +#define AIC32X4_RATES SNDRV_PCM_RATE_8000_192000 #define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
participants (2)
-
Annaliese McDermond
-
Mark Brown