#include <linux/module.h>
#include <linux/regmap.h>
+#include <linux/gcd.h>
#include "rl6231.h"
/**
{19200000, 24576000, 3, 30, 3, false},
};
+static unsigned int find_best_div(unsigned int in,
+ unsigned int max, unsigned int div)
+{
+ unsigned int d;
+
+ if (in <= max)
+ return 1;
+
+ d = in / max;
+ if (in % max)
+ d++;
+
+ while (div % d != 0)
+ d++;
+
+
+ return d;
+}
+
/**
* rl6231_pll_calc - Calcualte PLL M/N/K code.
* @freq_in: external clock provided to codec.
const unsigned int freq_out, struct rl6231_pll_code *pll_code)
{
int max_n = RL6231_PLL_N_MAX, max_m = RL6231_PLL_M_MAX;
- int i, k, red, n_t, pll_out, in_t, out_t;
- int n = 0, m = 0, m_t = 0;
- int red_t = abs(freq_out - freq_in);
+ int i, k, n_t;
+ int k_t, min_k, max_k, n = 0, m = 0, m_t = 0;
+ unsigned int red, pll_out, in_t, out_t, div, div_t;
+ unsigned int red_t = abs(freq_out - freq_in);
+ unsigned int f_in, f_out, f_max;
bool bypass = false;
if (RL6231_PLL_INP_MAX < freq_in || RL6231_PLL_INP_MIN > freq_in)
}
}
- k = 100000000 / freq_out - 2;
- if (k > RL6231_PLL_K_MAX)
- k = RL6231_PLL_K_MAX;
- for (n_t = 0; n_t <= max_n; n_t++) {
- in_t = freq_in / (k + 2);
- pll_out = freq_out / (n_t + 2);
- if (in_t < 0)
- continue;
- if (in_t == pll_out) {
- bypass = true;
- n = n_t;
- goto code_find;
- }
- red = abs(in_t - pll_out);
- if (red < red_t) {
- bypass = true;
- n = n_t;
- m = m_t;
- if (red == 0)
+ min_k = 80000000 / freq_out - 2;
+ max_k = 150000000 / freq_out - 2;
+ if (max_k > RL6231_PLL_K_MAX)
+ max_k = RL6231_PLL_K_MAX;
+ if (min_k > RL6231_PLL_K_MAX)
+ min_k = max_k = RL6231_PLL_K_MAX;
+ div_t = gcd(freq_in, freq_out);
+ f_max = 0xffffffff / RL6231_PLL_N_MAX;
+ div = find_best_div(freq_in, f_max, div_t);
+ f_in = freq_in / div;
+ f_out = freq_out / div;
+ k = min_k;
+ for (k_t = min_k; k_t <= max_k; k_t++) {
+ for (n_t = 0; n_t <= max_n; n_t++) {
+ in_t = f_in * (n_t + 2);
+ pll_out = f_out * (k_t + 2);
+ if (in_t == pll_out) {
+ bypass = true;
+ n = n_t;
+ k = k_t;
goto code_find;
- red_t = red;
- }
- for (m_t = 0; m_t <= max_m; m_t++) {
- out_t = in_t / (m_t + 2);
- red = abs(out_t - pll_out);
+ }
+ out_t = in_t / (k_t + 2);
+ red = abs(f_out - out_t);
if (red < red_t) {
- bypass = false;
+ bypass = true;
n = n_t;
- m = m_t;
+ m = 0;
+ k = k_t;
if (red == 0)
goto code_find;
red_t = red;
}
+ for (m_t = 0; m_t <= max_m; m_t++) {
+ out_t = in_t / ((m_t + 2) * (k_t + 2));
+ red = abs(f_out - out_t);
+ if (red < red_t) {
+ bypass = false;
+ n = n_t;
+ m = m_t;
+ k = k_t;
+ if (red == 0)
+ goto code_find;
+ red_t = red;
+ }
+ }
}
}
pr_debug("Only get approximation about PLL\n");
return 0;
}
+static int rt5514_calibration(struct rt5514_priv *rt5514, bool on)
+{
+ if (on) {
+ regmap_write(rt5514->regmap, RT5514_ANA_CTRL_PLL3, 0x0000000a);
+ regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, 0xf,
+ 0xa);
+ regmap_update_bits(rt5514->regmap, RT5514_PWR_ANA1, 0x301,
+ 0x301);
+ regmap_write(rt5514->regmap, RT5514_PLL3_CALIB_CTRL4,
+ 0x80000000 | rt5514->pll3_cal_value);
+ regmap_write(rt5514->regmap, RT5514_PLL3_CALIB_CTRL1,
+ 0x8bb80800);
+ regmap_update_bits(rt5514->regmap, RT5514_PLL3_CALIB_CTRL5,
+ 0xc0000000, 0x80000000);
+ regmap_update_bits(rt5514->regmap, RT5514_PLL3_CALIB_CTRL5,
+ 0xc0000000, 0xc0000000);
+ } else {
+ regmap_update_bits(rt5514->regmap, RT5514_PLL3_CALIB_CTRL5,
+ 0xc0000000, 0x40000000);
+ regmap_update_bits(rt5514->regmap, RT5514_PWR_ANA1, 0x301, 0);
+ regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, 0xf,
+ 0x4);
+ }
+
+ return 0;
+}
+
static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
struct snd_soc_codec *codec = rt5514->codec;
const struct firmware *fw = NULL;
+ u8 buf[8];
if (ucontrol->value.integer.value[0] == rt5514->dsp_enabled)
return 0;
rt5514->dsp_enabled = ucontrol->value.integer.value[0];
if (rt5514->dsp_enabled) {
+ if (rt5514->pdata.dsp_calib_clk_name &&
+ !IS_ERR(rt5514->dsp_calib_clk)) {
+ if (clk_set_rate(rt5514->dsp_calib_clk,
+ rt5514->pdata.dsp_calib_clk_rate))
+ dev_err(codec->dev,
+ "Can't set rate for mclk");
+
+ if (clk_prepare_enable(rt5514->dsp_calib_clk))
+ dev_err(codec->dev,
+ "Can't enable dsp_calib_clk");
+
+ rt5514_calibration(rt5514, true);
+
+ msleep(20);
+#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI)
+ rt5514_spi_burst_read(RT5514_PLL3_CALIB_CTRL6 |
+ RT5514_DSP_MAPPING,
+ (u8 *)&buf, sizeof(buf));
+#else
+ dev_err(codec->dev, "There is no SPI driver for"
+ " loading the firmware\n");
+#endif
+ rt5514->pll3_cal_value = buf[0] | buf[1] << 8 |
+ buf[2] << 16 | buf[3] << 24;
+
+ rt5514_calibration(rt5514, false);
+ clk_disable_unprepare(rt5514->dsp_calib_clk);
+ }
+
rt5514_enable_dsp_prepare(rt5514);
request_firmware(&fw, RT5514_FIRMWARE1, codec->dev);
/* DSP run */
regmap_write(rt5514->i2c_regmap, 0x18002f00,
0x00055148);
+
+ if (rt5514->pdata.dsp_calib_clk_name &&
+ !IS_ERR(rt5514->dsp_calib_clk)) {
+ msleep(20);
+
+ regmap_write(rt5514->i2c_regmap, 0x1800211c,
+ rt5514->pll3_cal_value);
+ regmap_write(rt5514->i2c_regmap, 0x18002124,
+ 0x00220012);
+ regmap_write(rt5514->i2c_regmap, 0x18002124,
+ 0x80220042);
+ regmap_write(rt5514->i2c_regmap, 0x18002124,
+ 0xe0220042);
+ }
} else {
regmap_multi_reg_write(rt5514->i2c_regmap,
rt5514_i2c_patch, ARRAY_SIZE(rt5514_i2c_patch));
static int rt5514_probe(struct snd_soc_codec *codec)
{
struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ struct platform_device *pdev = container_of(codec->dev,
+ struct platform_device, dev);
rt5514->mclk = devm_clk_get(codec->dev, "mclk");
if (PTR_ERR(rt5514->mclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
+ if (rt5514->pdata.dsp_calib_clk_name) {
+ rt5514->dsp_calib_clk = devm_clk_get(&pdev->dev,
+ rt5514->pdata.dsp_calib_clk_name);
+ if (PTR_ERR(rt5514->dsp_calib_clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ }
+
rt5514->codec = codec;
+ rt5514->pll3_cal_value = 0x0078b000;
return 0;
}
{
device_property_read_u32(dev, "realtek,dmic-init-delay-ms",
&rt5514->pdata.dmic_init_delay);
+ device_property_read_string(dev, "realtek,dsp-calib-clk-name",
+ &rt5514->pdata.dsp_calib_clk_name);
+ device_property_read_u32(dev, "realtek,dsp-calib-clk-rate",
+ &rt5514->pdata.dsp_calib_clk_rate);
return 0;
}