static int fsl_mqs_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct fsl_mqs *mqs_priv = snd_soc_codec_get_drvdata(codec); int div, res; mqs_priv->mclk_rate = clk_get_rate(mqs_priv->mclk); mqs_priv->bclk = snd_soc_params_to_bclk(params); mqs_priv->lrclk = params_rate(params); /* * mclk_rate / (oversample(32,64) * FS * 2 * divider ) = repeat_rate; * if repeat_rate is 8, mqs can achieve better quality. * oversample rate is fix to 32 currently. */ div = mqs_priv->mclk_rate / (32 * 2 * mqs_priv->lrclk * 8); res = mqs_priv->mclk_rate % (32 * 2 * mqs_priv->lrclk * 8); if (res == 0 && div > 0 && div <= 256) { regmap_update_bits(mqs_priv->gpr, IOMUXC_GPR2, IMX6SX_GPR2_MQS_CLK_DIV_MASK, (div-1) << IMX6SX_GPR2_MQS_CLK_DIV_SHIFT); regmap_update_bits(mqs_priv->gpr, IOMUXC_GPR2, IMX6SX_GPR2_MQS_OVERSAMPLE_MASK, 0 << IMX6SX_GPR2_MQS_OVERSAMPLE_SHIFT); } else dev_err(&mqs_priv->pdev->dev, "can't get proper divider\n"); return 0; }
static int arizona_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona *arizona = priv->arizona; int base = dai->driver->base; const int *rates; int i, ret, val; int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1]; int bclk, lrclk, wl, frame, bclk_target; if (params_rate(params) % 8000) rates = &arizona_44k1_bclk_rates[0]; else rates = &arizona_48k_bclk_rates[0]; bclk_target = snd_soc_params_to_bclk(params); if (chan_limit && chan_limit < params_channels(params)) { arizona_aif_dbg(dai, "Limiting to %d channels\n", chan_limit); bclk_target /= params_channels(params); bclk_target *= chan_limit; } /* Force stereo for I2S mode */ val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT); if (params_channels(params) == 1 && (val & ARIZONA_AIF1_FMT_MASK)) { arizona_aif_dbg(dai, "Forcing stereo mode\n"); bclk_target *= 2; } for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) { if (rates[i] >= bclk_target && rates[i] % params_rate(params) == 0) { bclk = i; break; } } if (i == ARRAY_SIZE(arizona_44k1_bclk_rates)) { arizona_aif_err(dai, "Unsupported sample rate %dHz\n", params_rate(params)); return -EINVAL; } lrclk = rates[bclk] / params_rate(params); arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n", rates[bclk], rates[bclk] / lrclk); wl = snd_pcm_format_width(params_format(params)); frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl; ret = arizona_hw_params_rate(substream, params, dai); if (ret != 0) return ret; snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL, ARIZONA_AIF1_BCLK_FREQ_MASK, bclk); snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_BCLK_RATE, ARIZONA_AIF1TX_BCPF_MASK, lrclk); snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_BCLK_RATE, ARIZONA_AIF1RX_BCPF_MASK, lrclk); snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_1, ARIZONA_AIF1TX_WL_MASK | ARIZONA_AIF1TX_SLOT_LEN_MASK, frame); snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_2, ARIZONA_AIF1RX_WL_MASK | ARIZONA_AIF1RX_SLOT_LEN_MASK, frame); return 0; }
static int pcm512x_set_dividers(struct snd_soc_dai *dai, struct snd_pcm_hw_params *params) { struct device *dev = dai->dev; struct snd_soc_codec *codec = dai->codec; struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); unsigned long pllin_rate = 0; unsigned long pll_rate; unsigned long sck_rate; unsigned long mck_rate; unsigned long bclk_rate; unsigned long sample_rate; unsigned long osr_rate; unsigned long dacsrc_rate; int bclk_div; int lrclk_div; int dsp_div; int dac_div; unsigned long dac_rate; int ncp_div; int osr_div; int ret; int idac; int fssp; int gpio; lrclk_div = snd_soc_params_to_frame_size(params); if (lrclk_div == 0) { dev_err(dev, "No LRCLK?\n"); return -EINVAL; } if (!pcm512x->pll_out) { sck_rate = clk_get_rate(pcm512x->sclk); bclk_div = params->rate_den * 64 / lrclk_div; bclk_rate = DIV_ROUND_CLOSEST(sck_rate, bclk_div); mck_rate = sck_rate; } else { ret = snd_soc_params_to_bclk(params); if (ret < 0) { dev_err(dev, "Failed to find suitable BCLK: %d\n", ret); return ret; } if (ret == 0) { dev_err(dev, "No BCLK?\n"); return -EINVAL; } bclk_rate = ret; pllin_rate = clk_get_rate(pcm512x->sclk); sck_rate = pcm512x_find_sck(dai, bclk_rate); if (!sck_rate) return -EINVAL; pll_rate = 4 * sck_rate; ret = pcm512x_find_pll_coeff(dai, pllin_rate, pll_rate); if (ret != 0) return ret; ret = regmap_write(pcm512x->regmap, PCM512x_PLL_COEFF_0, pcm512x->pll_p - 1); if (ret != 0) { dev_err(dev, "Failed to write PLL P: %d\n", ret); return ret; } ret = regmap_write(pcm512x->regmap, PCM512x_PLL_COEFF_1, pcm512x->pll_j); if (ret != 0) { dev_err(dev, "Failed to write PLL J: %d\n", ret); return ret; } ret = regmap_write(pcm512x->regmap, PCM512x_PLL_COEFF_2, pcm512x->pll_d >> 8); if (ret != 0) { dev_err(dev, "Failed to write PLL D msb: %d\n", ret); return ret; } ret = regmap_write(pcm512x->regmap, PCM512x_PLL_COEFF_3, pcm512x->pll_d & 0xff); if (ret != 0) { dev_err(dev, "Failed to write PLL D lsb: %d\n", ret); return ret; } ret = regmap_write(pcm512x->regmap, PCM512x_PLL_COEFF_4, pcm512x->pll_r - 1); if (ret != 0) { dev_err(dev, "Failed to write PLL R: %d\n", ret); return ret; } mck_rate = pcm512x->real_pll; bclk_div = DIV_ROUND_CLOSEST(sck_rate, bclk_rate); } if (bclk_div > 128) { dev_err(dev, "Failed to find BCLK divider\n"); return -EINVAL; } /* the actual rate */ sample_rate = sck_rate / bclk_div / lrclk_div; osr_rate = 16 * sample_rate; /* run DSP no faster than 50 MHz */ dsp_div = mck_rate > pcm512x_dsp_max(pcm512x) ? 2 : 1; dac_rate = pcm512x_pllin_dac_rate(dai, osr_rate, pllin_rate); if (dac_rate) { /* the desired clock rate is "compatible" with the pll input * clock, so use that clock as dac input instead of the pll * output clock since the pll will introduce jitter and thus * noise. */ dev_dbg(dev, "using pll input as dac input\n"); ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF, PCM512x_SDAC, PCM512x_SDAC_GPIO); if (ret != 0) { dev_err(codec->dev, "Failed to set gpio as dacref: %d\n", ret); return ret; } gpio = PCM512x_GREF_GPIO1 + pcm512x->pll_in - 1; ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_DACIN, PCM512x_GREF, gpio); if (ret != 0) { dev_err(codec->dev, "Failed to set gpio %d as dacin: %d\n", pcm512x->pll_in, ret); return ret; } dacsrc_rate = pllin_rate; } else { /* run DAC no faster than 6144000 Hz */ unsigned long dac_mul = pcm512x_dac_max(pcm512x, 6144000) / osr_rate; unsigned long sck_mul = sck_rate / osr_rate; for (; dac_mul; dac_mul--) { if (!(sck_mul % dac_mul)) break; } if (!dac_mul) { dev_err(dev, "Failed to find DAC rate\n"); return -EINVAL; } dac_rate = dac_mul * osr_rate; dev_dbg(dev, "dac_rate %lu sample_rate %lu\n", dac_rate, sample_rate); ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF, PCM512x_SDAC, PCM512x_SDAC_SCK); if (ret != 0) { dev_err(codec->dev, "Failed to set sck as dacref: %d\n", ret); return ret; } dacsrc_rate = sck_rate; } osr_div = DIV_ROUND_CLOSEST(dac_rate, osr_rate); if (osr_div > 128) { dev_err(dev, "Failed to find OSR divider\n"); return -EINVAL; } dac_div = DIV_ROUND_CLOSEST(dacsrc_rate, dac_rate); if (dac_div > 128) { dev_err(dev, "Failed to find DAC divider\n"); return -EINVAL; } dac_rate = dacsrc_rate / dac_div; ncp_div = DIV_ROUND_CLOSEST(dac_rate, pcm512x_ncp_target(pcm512x, dac_rate)); if (ncp_div > 128 || dac_rate / ncp_div > 2048000) { /* run NCP no faster than 2048000 Hz, but why? */ ncp_div = DIV_ROUND_UP(dac_rate, 2048000); if (ncp_div > 128) { dev_err(dev, "Failed to find NCP divider\n"); return -EINVAL; } } idac = mck_rate / (dsp_div * sample_rate); ret = regmap_write(pcm512x->regmap, PCM512x_DSP_CLKDIV, dsp_div - 1); if (ret != 0) { dev_err(dev, "Failed to write DSP divider: %d\n", ret); return ret; } ret = regmap_write(pcm512x->regmap, PCM512x_DAC_CLKDIV, dac_div - 1); if (ret != 0) { dev_err(dev, "Failed to write DAC divider: %d\n", ret); return ret; } ret = regmap_write(pcm512x->regmap, PCM512x_NCP_CLKDIV, ncp_div - 1); if (ret != 0) { dev_err(dev, "Failed to write NCP divider: %d\n", ret); return ret; } ret = regmap_write(pcm512x->regmap, PCM512x_OSR_CLKDIV, osr_div - 1); if (ret != 0) { dev_err(dev, "Failed to write OSR divider: %d\n", ret); return ret; } ret = regmap_write(pcm512x->regmap, PCM512x_MASTER_CLKDIV_1, bclk_div - 1); if (ret != 0) { dev_err(dev, "Failed to write BCLK divider: %d\n", ret); return ret; } ret = regmap_write(pcm512x->regmap, PCM512x_MASTER_CLKDIV_2, lrclk_div - 1); if (ret != 0) { dev_err(dev, "Failed to write LRCLK divider: %d\n", ret); return ret; } ret = regmap_write(pcm512x->regmap, PCM512x_IDAC_1, idac >> 8); if (ret != 0) { dev_err(dev, "Failed to write IDAC msb divider: %d\n", ret); return ret; } ret = regmap_write(pcm512x->regmap, PCM512x_IDAC_2, idac & 0xff); if (ret != 0) { dev_err(dev, "Failed to write IDAC lsb divider: %d\n", ret); return ret; } if (sample_rate <= pcm512x_dac_max(pcm512x, 48000)) fssp = PCM512x_FSSP_48KHZ; else if (sample_rate <= pcm512x_dac_max(pcm512x, 96000)) fssp = PCM512x_FSSP_96KHZ; else if (sample_rate <= pcm512x_dac_max(pcm512x, 192000)) fssp = PCM512x_FSSP_192KHZ; else fssp = PCM512x_FSSP_384KHZ; ret = regmap_update_bits(pcm512x->regmap, PCM512x_FS_SPEED_MODE, PCM512x_FSSP, fssp); if (ret != 0) { dev_err(codec->dev, "Failed to set fs speed: %d\n", ret); return ret; } dev_dbg(codec->dev, "DSP divider %d\n", dsp_div); dev_dbg(codec->dev, "DAC divider %d\n", dac_div); dev_dbg(codec->dev, "NCP divider %d\n", ncp_div); dev_dbg(codec->dev, "OSR divider %d\n", osr_div); dev_dbg(codec->dev, "BCK divider %d\n", bclk_div); dev_dbg(codec->dev, "LRCK divider %d\n", lrclk_div); dev_dbg(codec->dev, "IDAC %d\n", idac); dev_dbg(codec->dev, "1<<FSSP %d\n", 1 << fssp); return 0; }
static int wm8985_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { int i; struct snd_soc_codec *codec; struct wm8985_priv *wm8985; u16 blen, srate_idx; unsigned int tmp; int srate_best; codec = dai->codec; wm8985 = snd_soc_codec_get_drvdata(codec); wm8985->bclk = snd_soc_params_to_bclk(params); if ((int)wm8985->bclk < 0) return wm8985->bclk; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: blen = 0x0; break; case SNDRV_PCM_FORMAT_S20_3LE: blen = 0x1; break; case SNDRV_PCM_FORMAT_S24_LE: blen = 0x2; break; case SNDRV_PCM_FORMAT_S32_LE: blen = 0x3; break; default: dev_err(dai->dev, "Unsupported word length %u\n", params_format(params)); return -EINVAL; } snd_soc_update_bits(codec, WM8985_AUDIO_INTERFACE, WM8985_WL_MASK, blen << WM8985_WL_SHIFT); /* * match to the nearest possible sample rate and rely * on the array index to configure the SR register */ srate_idx = 0; srate_best = abs(srates[0] - params_rate(params)); for (i = 1; i < ARRAY_SIZE(srates); ++i) { if (abs(srates[i] - params_rate(params)) >= srate_best) continue; srate_idx = i; srate_best = abs(srates[i] - params_rate(params)); } dev_dbg(dai->dev, "Selected SRATE = %d\n", srates[srate_idx]); snd_soc_update_bits(codec, WM8985_ADDITIONAL_CONTROL, WM8985_SR_MASK, srate_idx << WM8985_SR_SHIFT); dev_dbg(dai->dev, "Target BCLK = %uHz\n", wm8985->bclk); dev_dbg(dai->dev, "SYSCLK = %uHz\n", wm8985->sysclk); for (i = 0; i < ARRAY_SIZE(fs_ratios); ++i) { if (wm8985->sysclk / params_rate(params) == fs_ratios[i].ratio) break; } if (i == ARRAY_SIZE(fs_ratios)) { dev_err(dai->dev, "Unable to configure MCLK ratio %u/%u\n", wm8985->sysclk, params_rate(params)); return -EINVAL; } dev_dbg(dai->dev, "MCLK ratio = %dfs\n", fs_ratios[i].ratio); snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL, WM8985_MCLKDIV_MASK, i << WM8985_MCLKDIV_SHIFT); /* select the appropriate bclk divider */ tmp = (wm8985->sysclk / fs_ratios[i].div) * 10; for (i = 0; i < ARRAY_SIZE(bclk_divs); ++i) { if (wm8985->bclk == tmp / bclk_divs[i]) break; } if (i == ARRAY_SIZE(bclk_divs)) { dev_err(dai->dev, "No matching BCLK divider found\n"); return -EINVAL; } dev_dbg(dai->dev, "BCLK div = %d\n", i); snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL, WM8985_BCLKDIV_MASK, i << WM8985_BCLKDIV_SHIFT); return 0; }
static int arizona_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; unsigned int val; int base = dai->driver->base; const int *rates; int i, ret, bclk_target; int bclk, lrclk, wl, frame, sr_val; //printk("Arizona: hw params start\n"); if (params_rate(params) % 8000) rates = &arizona_44k1_bclk_rates[0]; else rates = &arizona_48k_bclk_rates[0]; /* Force BCLK to stereo for I2S */ bclk_target = snd_soc_params_to_bclk(params); val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT); if (val & ARIZONA_AIF1_FMT_MASK && params_channels(params) == 1) { arizona_aif_err(dai, "Forcing stereo mode\n"); bclk_target *= 2; } for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) { if (rates[i] >= bclk_target && rates[i] % params_rate(params) == 0) { bclk = i; break; } } if (i == ARRAY_SIZE(arizona_44k1_bclk_rates)) { arizona_aif_err(dai, "Unsupported sample rate %dHz\n", params_rate(params)); return -EINVAL; } for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) if (arizona_sr_vals[i] == params_rate(params)) break; if (i == ARRAY_SIZE(arizona_sr_vals)) { arizona_aif_err(dai, "Unsupported sample rate %dHz\n", params_rate(params)); return -EINVAL; } sr_val = i; lrclk = rates[bclk] / params_rate(params); arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n", rates[bclk], rates[bclk] / lrclk); wl = snd_pcm_format_width(params_format(params)); frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl; /* * We will need to be more flexible than this in future, * currently we use a single sample rate for SYSCLK. */ switch (dai_priv->clk) { case ARIZONA_CLK_SYSCLK: /* SR2 is forced to 8kHz */ if (params_rate(params) != 8000) { snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, ARIZONA_SAMPLE_RATE_1_MASK, sr_val); snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, ARIZONA_AIF1_RATE_MASK, 0); } else { snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_2, ARIZONA_SAMPLE_RATE_2_MASK, sr_val); snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, ARIZONA_AIF1_RATE_MASK, 1); } break; case ARIZONA_CLK_ASYNCCLK: snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1, ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val); snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, ARIZONA_AIF1_RATE_MASK, 8); break; default: arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); return -EINVAL; } snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL, ARIZONA_AIF1_BCLK_FREQ_MASK, bclk); snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_BCLK_RATE, ARIZONA_AIF1TX_BCPF_MASK, lrclk); snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_BCLK_RATE, ARIZONA_AIF1RX_BCPF_MASK, lrclk); snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_1, ARIZONA_AIF1TX_WL_MASK | ARIZONA_AIF1TX_SLOT_LEN_MASK, frame); snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_2, ARIZONA_AIF1RX_WL_MASK | ARIZONA_AIF1RX_SLOT_LEN_MASK, frame); // HLL will add this in //ret=wm2000_check_poll(); //printk("Arizona: hw params start 1\n"); return 0; //ret; }
static int arizona_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; int base = dai->driver->base; const int *rates; int i; int bclk, lrclk, wl, frame, sr_val; if (params_rate(params) % 8000) rates = &arizona_44k1_bclk_rates[0]; else rates = &arizona_48k_bclk_rates[0]; for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) { if (rates[i] >= snd_soc_params_to_bclk(params) && rates[i] % params_rate(params) == 0) { bclk = i; break; } } if (i == ARRAY_SIZE(arizona_44k1_bclk_rates)) { arizona_aif_err(dai, "Unsupported sample rate %dHz\n", params_rate(params)); return -EINVAL; } for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) if (arizona_sr_vals[i] == params_rate(params)) break; if (i == ARRAY_SIZE(arizona_sr_vals)) { arizona_aif_err(dai, "Unsupported sample rate %dHz\n", params_rate(params)); return -EINVAL; } sr_val = i; lrclk = snd_soc_params_to_bclk(params) / params_rate(params); arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n", rates[bclk], rates[bclk] / lrclk); wl = snd_pcm_format_width(params_format(params)); frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl; /* * We will need to be more flexible than this in future, * currently we use a single sample rate for SYSCLK. */ switch (dai_priv->clk) { case ARIZONA_CLK_SYSCLK: snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, ARIZONA_SAMPLE_RATE_1_MASK, sr_val); snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, ARIZONA_AIF1_RATE_MASK, 0); break; case ARIZONA_CLK_ASYNCCLK: snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1, ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val); snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, ARIZONA_AIF1_RATE_MASK, 8); break; default: arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); return -EINVAL; } snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL, ARIZONA_AIF1_BCLK_FREQ_MASK, bclk); snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_BCLK_RATE, ARIZONA_AIF1TX_BCPF_MASK, lrclk); snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_BCLK_RATE, ARIZONA_AIF1RX_BCPF_MASK, lrclk); snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_1, ARIZONA_AIF1TX_WL_MASK | ARIZONA_AIF1TX_SLOT_LEN_MASK, frame); snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_2, ARIZONA_AIF1RX_WL_MASK | ARIZONA_AIF1RX_SLOT_LEN_MASK, frame); return 0; }
static int wm8983_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { int i; struct snd_soc_component *component = dai->component; struct wm8983_priv *wm8983 = snd_soc_component_get_drvdata(component); u16 blen, srate_idx; u32 tmp; int srate_best; int ret; ret = snd_soc_params_to_bclk(params); if (ret < 0) { dev_err(component->dev, "Failed to convert params to bclk: %d\n", ret); return ret; } wm8983->bclk = ret; switch (params_width(params)) { case 16: blen = 0x0; break; case 20: blen = 0x1; break; case 24: blen = 0x2; break; case 32: blen = 0x3; break; default: dev_err(dai->dev, "Unsupported word length %u\n", params_width(params)); return -EINVAL; } snd_soc_component_update_bits(component, WM8983_AUDIO_INTERFACE, WM8983_WL_MASK, blen << WM8983_WL_SHIFT); /* * match to the nearest possible sample rate and rely * on the array index to configure the SR register */ srate_idx = 0; srate_best = abs(srates[0] - params_rate(params)); for (i = 1; i < ARRAY_SIZE(srates); ++i) { if (abs(srates[i] - params_rate(params)) >= srate_best) continue; srate_idx = i; srate_best = abs(srates[i] - params_rate(params)); } dev_dbg(dai->dev, "Selected SRATE = %d\n", srates[srate_idx]); snd_soc_component_update_bits(component, WM8983_ADDITIONAL_CONTROL, WM8983_SR_MASK, srate_idx << WM8983_SR_SHIFT); dev_dbg(dai->dev, "Target BCLK = %uHz\n", wm8983->bclk); dev_dbg(dai->dev, "SYSCLK = %uHz\n", wm8983->sysclk); for (i = 0; i < ARRAY_SIZE(fs_ratios); ++i) { if (wm8983->sysclk / params_rate(params) == fs_ratios[i].ratio) break; } if (i == ARRAY_SIZE(fs_ratios)) { dev_err(dai->dev, "Unable to configure MCLK ratio %u/%u\n", wm8983->sysclk, params_rate(params)); return -EINVAL; } dev_dbg(dai->dev, "MCLK ratio = %dfs\n", fs_ratios[i].ratio); snd_soc_component_update_bits(component, WM8983_CLOCK_GEN_CONTROL, WM8983_MCLKDIV_MASK, i << WM8983_MCLKDIV_SHIFT); /* select the appropriate bclk divider */ tmp = (wm8983->sysclk / fs_ratios[i].div) * 10; for (i = 0; i < ARRAY_SIZE(bclk_divs); ++i) { if (wm8983->bclk == tmp / bclk_divs[i]) break; } if (i == ARRAY_SIZE(bclk_divs)) { dev_err(dai->dev, "No matching BCLK divider found\n"); return -EINVAL; } dev_dbg(dai->dev, "BCLK div = %d\n", i); snd_soc_component_update_bits(component, WM8983_CLOCK_GEN_CONTROL, WM8983_BCLKDIV_MASK, i << WM8983_BCLKDIV_SHIFT); return 0; }