diff options
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r-- | sound/soc/codecs/Kconfig | 10 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 4 | ||||
-rw-r--r-- | sound/soc/codecs/ak4642.c | 4 | ||||
-rw-r--r-- | sound/soc/codecs/arizona.c | 32 | ||||
-rw-r--r-- | sound/soc/codecs/cs42l56.c | 2 | ||||
-rw-r--r-- | sound/soc/codecs/cs47l24.c | 46 | ||||
-rw-r--r-- | sound/soc/codecs/da7213.c | 99 | ||||
-rw-r--r-- | sound/soc/codecs/da7213.h | 35 | ||||
-rw-r--r-- | sound/soc/codecs/da7218.c | 32 | ||||
-rw-r--r-- | sound/soc/codecs/da7218.h | 21 | ||||
-rw-r--r-- | sound/soc/codecs/da7219.c | 38 | ||||
-rw-r--r-- | sound/soc/codecs/da7219.h | 20 | ||||
-rw-r--r-- | sound/soc/codecs/es8328.c | 194 | ||||
-rw-r--r-- | sound/soc/codecs/es8328.h | 23 | ||||
-rw-r--r-- | sound/soc/codecs/hdac_hdmi.c | 195 | ||||
-rw-r--r-- | sound/soc/codecs/hdmi-codec.c | 432 | ||||
-rw-r--r-- | sound/soc/codecs/pcm5102a.c | 69 | ||||
-rw-r--r-- | sound/soc/codecs/rt298.c | 19 | ||||
-rw-r--r-- | sound/soc/codecs/rt5645.c | 16 | ||||
-rw-r--r-- | sound/soc/codecs/wm5102.c | 4 | ||||
-rw-r--r-- | sound/soc/codecs/wm5110.c | 6 | ||||
-rw-r--r-- | sound/soc/codecs/wm_adsp.c | 292 | ||||
-rw-r--r-- | sound/soc/codecs/wm_adsp.h | 1 |
23 files changed, 1352 insertions, 242 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 7ef3a0c16478..b3afae990e39 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -88,12 +88,14 @@ config SND_SOC_ALL_CODECS select SND_SOC_MC13783 if MFD_MC13XXX select SND_SOC_ML26124 if I2C select SND_SOC_NAU8825 if I2C + select SND_SOC_HDMI_CODEC select SND_SOC_PCM1681 if I2C select SND_SOC_PCM179X_I2C if I2C select SND_SOC_PCM179X_SPI if SPI_MASTER select SND_SOC_PCM3008 select SND_SOC_PCM3168A_I2C if I2C select SND_SOC_PCM3168A_SPI if SPI_MASTER + select SND_SOC_PCM5102A select SND_SOC_PCM512x_I2C if I2C select SND_SOC_PCM512x_SPI if SPI_MASTER select SND_SOC_RT286 if I2C @@ -477,6 +479,11 @@ config SND_SOC_BT_SCO config SND_SOC_DMIC tristate +config SND_SOC_HDMI_CODEC + tristate + select SND_PCM_ELD + select SND_PCM_IEC958 + config SND_SOC_ES8328 tristate "Everest Semi ES8328 CODEC" @@ -575,6 +582,9 @@ config SND_SOC_PCM3168A_SPI select SND_SOC_PCM3168A select REGMAP_SPI +config SND_SOC_PCM5102A + tristate + config SND_SOC_PCM512x tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 185a712a7fe7..b7b99416537f 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -81,6 +81,7 @@ snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o snd-soc-nau8825-objs := nau8825.o +snd-soc-hdmi-codec-objs := hdmi-codec.o snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm179x-codec-objs := pcm179x.o snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o @@ -89,6 +90,7 @@ snd-soc-pcm3008-objs := pcm3008.o snd-soc-pcm3168a-objs := pcm3168a.o snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o +snd-soc-pcm5102a-objs := pcm5102a.o snd-soc-pcm512x-objs := pcm512x.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o @@ -290,6 +292,7 @@ obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o +obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o obj-$(CONFIG_SND_SOC_PCM179X_I2C) += snd-soc-pcm179x-i2c.o @@ -298,6 +301,7 @@ obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o obj-$(CONFIG_SND_SOC_PCM3168A_SPI) += snd-soc-pcm3168a-spi.o +obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index cda27c22812a..1ee8506c06c7 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -608,9 +608,7 @@ static struct clk *ak4642_of_parse_mcko(struct device *dev) of_property_read_string(np, "clock-output-names", &clk_name); - clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name, - (parent_clk_name) ? 0 : CLK_IS_ROOT, - rate); + clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name, 0, rate); if (!IS_ERR(clk)) of_clk_add_provider(np, of_clk_src_simple_get, clk); diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 83959312f7a0..664a8c044ffb 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -221,6 +221,8 @@ int arizona_init_spk(struct snd_soc_codec *codec) switch (arizona->type) { case WM8997: + case CS47L24: + case WM1831: break; default: ret = snd_soc_dapm_new_controls(dapm, &arizona_spkr, 1); @@ -1134,7 +1136,6 @@ int arizona_anc_ev(struct snd_soc_dapm_widget *w, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - unsigned int mask = 0x3 << w->shift; unsigned int val; switch (event) { @@ -1148,7 +1149,7 @@ int arizona_anc_ev(struct snd_soc_dapm_widget *w, return 0; } - snd_soc_update_bits(codec, ARIZONA_CLOCK_CONTROL, mask, val); + snd_soc_write(codec, ARIZONA_CLOCK_CONTROL, val); return 0; } @@ -2047,7 +2048,21 @@ static int arizona_calc_fratio(struct arizona_fll *fll, init_ratio, Fref, refdiv); while (div <= ARIZONA_FLL_MAX_REFDIV) { - for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO; + /* start from init_ratio because this may already give a + * fractional N.K + */ + for (ratio = init_ratio; ratio > 0; ratio--) { + if (target % (ratio * Fref)) { + cfg->refdiv = refdiv; + cfg->fratio = ratio - 1; + arizona_fll_dbg(fll, + "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n", + Fref, refdiv, div, ratio); + return ratio; + } + } + + for (ratio = init_ratio + 1; ratio <= ARIZONA_FLL_MAX_FRATIO; ratio++) { if ((ARIZONA_FLL_VCO_CORNER / 2) / (fll->vco_mult * ratio) < Fref) { @@ -2073,17 +2088,6 @@ static int arizona_calc_fratio(struct arizona_fll *fll, } } - for (ratio = init_ratio - 1; ratio > 0; ratio--) { - if (target % (ratio * Fref)) { - cfg->refdiv = refdiv; - cfg->fratio = ratio - 1; - arizona_fll_dbg(fll, - "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n", - Fref, refdiv, div, ratio); - return ratio; - } - } - div *= 2; Fref /= 2; refdiv++; diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index 7cd5f769bb61..eec1ff853b98 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -56,7 +56,7 @@ struct cs42l56_private { u8 iface; u8 iface_fmt; u8 iface_inv; -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +#if IS_ENABLED(CONFIG_INPUT) struct input_dev *beep; struct work_struct beep_work; int beep_rate; diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index 00e9b6fc1b5c..5ec5a682d186 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -807,6 +807,9 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = { { "IN2L PGA", NULL, "IN2L" }, { "IN2R PGA", NULL, "IN2R" }, + { "Audio Trace DSP", NULL, "DSP2" }, + { "Audio Trace DSP", NULL, "SYSCLK" }, + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), @@ -1016,6 +1019,27 @@ static struct snd_soc_dai_driver cs47l24_dai[] = { .formats = CS47L24_FORMATS, }, }, + { + .name = "cs47l24-cpu-trace", + .capture = { + .stream_name = "Audio Trace CPU", + .channels_min = 1, + .channels_max = 6, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .compress_new = snd_soc_new_compress, + }, + { + .name = "cs47l24-dsp-trace", + .capture = { + .stream_name = "Audio Trace DSP", + .channels_min = 1, + .channels_max = 6, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + }, }; static int cs47l24_open(struct snd_compr_stream *stream) @@ -1027,6 +1051,8 @@ static int cs47l24_open(struct snd_compr_stream *stream) if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-voicectrl") == 0) { n_adsp = 2; + } else if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-trace") == 0) { + n_adsp = 1; } else { dev_err(arizona->dev, "No suitable compressed stream for DAI '%s'\n", @@ -1041,10 +1067,16 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data) { struct cs47l24_priv *priv = data; struct arizona *arizona = priv->core.arizona; - int ret; + int serviced = 0; + int i, ret; - ret = wm_adsp_compr_handle_irq(&priv->core.adsp[2]); - if (ret == -ENODEV) { + for (i = 1; i <= 2; ++i) { + ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]); + if (ret != -ENODEV) + serviced++; + } + + if (!serviced) { dev_err(arizona->dev, "Spurious compressed data IRQ\n"); return IRQ_NONE; } @@ -1160,6 +1192,7 @@ static struct snd_compr_ops cs47l24_compr_ops = { static struct snd_soc_platform_driver cs47l24_compr_platform = { .compr_ops = &cs47l24_compr_ops, }; + static int cs47l24_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); @@ -1228,9 +1261,9 @@ static int cs47l24_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to register platform: %d\n", ret); return ret; } + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24, cs47l24_dai, ARRAY_SIZE(cs47l24_dai)); - if (ret < 0) { dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); snd_soc_unregister_platform(&pdev->dev); @@ -1241,10 +1274,15 @@ static int cs47l24_probe(struct platform_device *pdev) static int cs47l24_remove(struct platform_device *pdev) { + struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev); + snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_codec(&pdev->dev); pm_runtime_disable(&pdev->dev); + wm_adsp2_remove(&cs47l24->core.adsp[1]); + wm_adsp2_remove(&cs47l24->core.adsp[2]); + return 0; } diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 7278f93460c1..e5527bc570ae 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -726,6 +726,68 @@ static const struct snd_kcontrol_new da7213_dapm_mixoutr_controls[] = { /* + * DAPM Events + */ + +static int da7213_dai_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); + u8 pll_ctrl, pll_status; + int i = 0; + bool srm_lock = false; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable DAI clks for master mode */ + if (da7213->master) + snd_soc_update_bits(codec, DA7213_DAI_CLK_MODE, + DA7213_DAI_CLK_EN_MASK, + DA7213_DAI_CLK_EN_MASK); + + /* PC synchronised to DAI */ + snd_soc_update_bits(codec, DA7213_PC_COUNT, + DA7213_PC_FREERUN_MASK, 0); + + /* Slave mode, if SRM not enabled no need for status checks */ + pll_ctrl = snd_soc_read(codec, DA7213_PLL_CTRL); + if (!(pll_ctrl & DA7213_PLL_SRM_EN)) + return 0; + + /* Check SRM has locked */ + do { + pll_status = snd_soc_read(codec, DA7213_PLL_STATUS); + if (pll_status & DA7219_PLL_SRM_LOCK) { + srm_lock = true; + } else { + ++i; + msleep(50); + } + } while ((i < DA7213_SRM_CHECK_RETRIES) & (!srm_lock)); + + if (!srm_lock) + dev_warn(codec->dev, "SRM failed to lock\n"); + + return 0; + case SND_SOC_DAPM_POST_PMD: + /* PC free-running */ + snd_soc_update_bits(codec, DA7213_PC_COUNT, + DA7213_PC_FREERUN_MASK, + DA7213_PC_FREERUN_MASK); + + /* Disable DAI clks if in master mode */ + if (da7213->master) + snd_soc_update_bits(codec, DA7213_DAI_CLK_MODE, + DA7213_DAI_CLK_EN_MASK, 0); + return 0; + default: + return -EINVAL; + } +} + + +/* * DAPM widgets */ @@ -736,7 +798,8 @@ static const struct snd_soc_dapm_widget da7213_dapm_widgets[] = { /* Use a supply here as this controls both input & output DAIs */ SND_SOC_DAPM_SUPPLY("DAI", DA7213_DAI_CTRL, DA7213_DAI_EN_SHIFT, - DA7213_NO_INVERT, NULL, 0), + DA7213_NO_INVERT, da7213_dai_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), /* * Input @@ -1143,11 +1206,9 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) /* Set master/slave mode */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: - dai_clk_mode |= DA7213_DAI_CLK_EN_MASTER_MODE; da7213->master = true; break; case SND_SOC_DAIFMT_CBS_CFS: - dai_clk_mode |= DA7213_DAI_CLK_EN_SLAVE_MODE; da7213->master = false; break; default: @@ -1281,28 +1342,28 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, pll_ctrl = 0; /* Workout input divider based on MCLK rate */ - if ((da7213->mclk_rate == 32768) && (source == DA7213_SYSCLK_PLL)) { + if (da7213->mclk_rate == 32768) { /* 32KHz PLL Mode */ - indiv_bits = DA7213_PLL_INDIV_10_20_MHZ; - indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL; + indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ; + indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL; freq_ref = 3750000; pll_ctrl |= DA7213_PLL_32K_MODE; } else { /* 5 - 54MHz MCLK */ if (da7213->mclk_rate < 5000000) { goto pll_err; - } else if (da7213->mclk_rate <= 10000000) { - indiv_bits = DA7213_PLL_INDIV_5_10_MHZ; - indiv = DA7213_PLL_INDIV_5_10_MHZ_VAL; - } else if (da7213->mclk_rate <= 20000000) { - indiv_bits = DA7213_PLL_INDIV_10_20_MHZ; - indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL; - } else if (da7213->mclk_rate <= 40000000) { - indiv_bits = DA7213_PLL_INDIV_20_40_MHZ; - indiv = DA7213_PLL_INDIV_20_40_MHZ_VAL; + } else if (da7213->mclk_rate <= 9000000) { + indiv_bits = DA7213_PLL_INDIV_5_TO_9_MHZ; + indiv = DA7213_PLL_INDIV_5_TO_9_MHZ_VAL; + } else if (da7213->mclk_rate <= 18000000) { + indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ; + indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL; + } else if (da7213->mclk_rate <= 36000000) { + indiv_bits = DA7213_PLL_INDIV_18_TO_36_MHZ; + indiv = DA7213_PLL_INDIV_18_TO_36_MHZ_VAL; } else if (da7213->mclk_rate <= 54000000) { - indiv_bits = DA7213_PLL_INDIV_40_54_MHZ; - indiv = DA7213_PLL_INDIV_40_54_MHZ_VAL; + indiv_bits = DA7213_PLL_INDIV_36_TO_54_MHZ; + indiv = DA7213_PLL_INDIV_36_TO_54_MHZ_VAL; } else { goto pll_err; } @@ -1547,6 +1608,10 @@ static int da7213_probe(struct snd_soc_codec *codec) /* Default to using SRM for slave mode */ da7213->srm_en = true; + /* Default PC counter to free-running */ + snd_soc_update_bits(codec, DA7213_PC_COUNT, DA7213_PC_FREERUN_MASK, + DA7213_PC_FREERUN_MASK); + /* Enable all Gain Ramps */ snd_soc_update_bits(codec, DA7213_AUX_L_CTRL, DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h index 030fd691b076..fbb7a356a501 100644 --- a/sound/soc/codecs/da7213.h +++ b/sound/soc/codecs/da7213.h @@ -142,6 +142,9 @@ * Bit fields */ +/* DA7213_PLL_STATUS = 0x03 */ +#define DA7219_PLL_SRM_LOCK (0x1 << 1) + /* DA7213_SR = 0x22 */ #define DA7213_SR_8000 (0x1 << 0) #define DA7213_SR_11025 (0x2 << 0) @@ -160,10 +163,10 @@ #define DA7213_VMID_EN (0x1 << 7) /* DA7213_PLL_CTRL = 0x27 */ -#define DA7213_PLL_INDIV_5_10_MHZ (0x0 << 2) -#define DA7213_PLL_INDIV_10_20_MHZ (0x1 << 2) -#define DA7213_PLL_INDIV_20_40_MHZ (0x2 << 2) -#define DA7213_PLL_INDIV_40_54_MHZ (0x3 << 2) +#define DA7213_PLL_INDIV_5_TO_9_MHZ (0x0 << 2) +#define DA7213_PLL_INDIV_9_TO_18_MHZ (0x1 << 2) +#define DA7213_PLL_INDIV_18_TO_36_MHZ (0x2 << 2) +#define DA7213_PLL_INDIV_36_TO_54_MHZ (0x3 << 2) #define DA7213_PLL_INDIV_MASK (0x3 << 2) #define DA7213_PLL_MCLK_SQR_EN (0x1 << 4) #define DA7213_PLL_32K_MODE (0x1 << 5) @@ -178,8 +181,6 @@ #define DA7213_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0) #define DA7213_DAI_CLK_POL_INV (0x1 << 2) #define DA7213_DAI_WCLK_POL_INV (0x1 << 3) -#define DA7213_DAI_CLK_EN_SLAVE_MODE (0x0 << 7) -#define DA7213_DAI_CLK_EN_MASTER_MODE (0x1 << 7) #define DA7213_DAI_CLK_EN_MASK (0x1 << 7) /* DA7213_DAI_CTRL = 0x29 */ @@ -412,6 +413,9 @@ #define DA7213_DMIC_CLK_RATE_SHIFT 2 #define DA7213_DMIC_CLK_RATE_MASK (0x1 << 2) +/* DA7213_PC_COUNT = 0x94 */ +#define DA7213_PC_FREERUN_MASK (0x1 << 0) + /* DA7213_DIG_CTRL = 0x99 */ #define DA7213_DAC_L_INV_SHIFT 3 #define DA7213_DAC_R_INV_SHIFT 7 @@ -495,15 +499,16 @@ #define DA7213_ALC_AVG_ITERATIONS 5 /* PLL related */ -#define DA7213_SYSCLK_MCLK 0 -#define DA7213_SYSCLK_PLL 1 -#define DA7213_PLL_FREQ_OUT_90316800 90316800 -#define DA7213_PLL_FREQ_OUT_98304000 98304000 -#define DA7213_PLL_FREQ_OUT_94310400 94310400 -#define DA7213_PLL_INDIV_5_10_MHZ_VAL 2 -#define DA7213_PLL_INDIV_10_20_MHZ_VAL 4 -#define DA7213_PLL_INDIV_20_40_MHZ_VAL 8 -#define DA7213_PLL_INDIV_40_54_MHZ_VAL 16 +#define DA7213_SYSCLK_MCLK 0 +#define DA7213_SYSCLK_PLL 1 +#define DA7213_PLL_FREQ_OUT_90316800 90316800 +#define DA7213_PLL_FREQ_OUT_98304000 98304000 +#define DA7213_PLL_FREQ_OUT_94310400 94310400 +#define DA7213_PLL_INDIV_5_TO_9_MHZ_VAL 2 +#define DA7213_PLL_INDIV_9_TO_18_MHZ_VAL 4 +#define DA7213_PLL_INDIV_18_TO_36_MHZ_VAL 8 +#define DA7213_PLL_INDIV_36_TO_54_MHZ_VAL 16 +#define DA7213_SRM_CHECK_RETRIES 8 enum da7213_clk_src { DA7213_CLKSRC_MCLK = 0, diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c index 93575f251866..99ce23e113bf 100644 --- a/sound/soc/codecs/da7218.c +++ b/sound/soc/codecs/da7218.c @@ -1868,27 +1868,27 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, /* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */ if (da7218->mclk_rate == 32768) { - indiv_bits = DA7218_PLL_INDIV_2_5_MHZ; - indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL; + indiv_bits = DA7218_PLL_INDIV_9_TO_18_MHZ; + indiv = DA7218_PLL_INDIV_9_TO_18_MHZ_VAL; } else if (da7218->mclk_rate < 2000000) { dev_err(codec->dev, "PLL input clock %d below valid range\n", da7218->mclk_rate); return -EINVAL; - } else if (da7218->mclk_rate <= 5000000) { - indiv_bits = DA7218_PLL_INDIV_2_5_MHZ; - indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL; - } else if (da7218->mclk_rate <= 10000000) { - indiv_bits = DA7218_PLL_INDIV_5_10_MHZ; - indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL; - } else if (da7218->mclk_rate <= 20000000) { - indiv_bits = DA7218_PLL_INDIV_10_20_MHZ; - indiv = DA7218_PLL_INDIV_10_20_MHZ_VAL; - } else if (da7218->mclk_rate <= 40000000) { - indiv_bits = DA7218_PLL_INDIV_20_40_MHZ; - indiv = DA7218_PLL_INDIV_20_40_MHZ_VAL; + } else if (da7218->mclk_rate <= 4500000) { + indiv_bits = DA7218_PLL_INDIV_2_TO_4_5_MHZ; + indiv = DA7218_PLL_INDIV_2_TO_4_5_MHZ_VAL; + } else if (da7218->mclk_rate <= 9000000) { + indiv_bits = DA7218_PLL_INDIV_4_5_TO_9_MHZ; + indiv = DA7218_PLL_INDIV_4_5_TO_9_MHZ_VAL; + } else if (da7218->mclk_rate <= 18000000) { + indiv_bits = DA7218_PLL_INDIV_9_TO_18_MHZ; + indiv = DA7218_PLL_INDIV_9_TO_18_MHZ_VAL; + } else if (da7218->mclk_rate <= 36000000) { + indiv_bits = DA7218_PLL_INDIV_18_TO_36_MHZ; + indiv = DA7218_PLL_INDIV_18_TO_36_MHZ_VAL; } else if (da7218->mclk_rate <= 54000000) { - indiv_bits = DA7218_PLL_INDIV_40_54_MHZ; - indiv = DA7218_PLL_INDIV_40_54_MHZ_VAL; + indiv_bits = DA7218_PLL_INDIV_36_TO_54_MHZ; + indiv = DA7218_PLL_INDIV_36_TO_54_MHZ_VAL; } else { dev_err(codec->dev, "PLL input clock %d above valid range\n", da7218->mclk_rate); diff --git a/sound/soc/codecs/da7218.h b/sound/soc/codecs/da7218.h index c2c59049a2ad..477cd37723cf 100644 --- a/sound/soc/codecs/da7218.h +++ b/sound/soc/codecs/da7218.h @@ -876,15 +876,11 @@ /* DA7218_PLL_CTRL = 0x91 */ #define DA7218_PLL_INDIV_SHIFT 0 #define DA7218_PLL_INDIV_MASK (0x7 << 0) -#define DA7218_PLL_INDIV_2_5_MHZ (0x0 << 0) -#define DA7218_PLL_INDIV_5_10_MHZ (0x1 << 0) -#define DA7218_PLL_INDIV_10_20_MHZ (0x2 << 0) -#define DA7218_PLL_INDIV_20_40_MHZ (0x3 << 0) -#define DA7218_PLL_INDIV_40_54_MHZ (0x4 << 0) -#define DA7218_PLL_INDIV_2_10_MHZ_VAL 2 -#define DA7218_PLL_INDIV_10_20_MHZ_VAL 4 -#define DA7218_PLL_INDIV_20_40_MHZ_VAL 8 -#define DA7218_PLL_INDIV_40_54_MHZ_VAL 16 +#define DA7218_PLL_INDIV_2_TO_4_5_MHZ (0x0 << 0) +#define DA7218_PLL_INDIV_4_5_TO_9_MHZ (0x1 << 0) +#define DA7218_PLL_INDIV_9_TO_18_MHZ (0x2 << 0) +#define DA7218_PLL_INDIV_18_TO_36_MHZ (0x3 << 0) +#define DA7218_PLL_INDIV_36_TO_54_MHZ (0x4 << 0) #define DA7218_PLL_MCLK_SQR_EN_SHIFT 4 #define DA7218_PLL_MCLK_SQR_EN_MASK (0x1 << 4) #define DA7218_PLL_MODE_SHIFT 6 @@ -1336,6 +1332,13 @@ #define DA7218_PLL_FREQ_OUT_90316 90316800 #define DA7218_PLL_FREQ_OUT_98304 98304000 +/* PLL Frequency Dividers */ +#define DA7218_PLL_INDIV_2_TO_4_5_MHZ_VAL 1 +#define DA7218_PLL_INDIV_4_5_TO_9_MHZ_VAL 2 +#define DA7218_PLL_INDIV_9_TO_18_MHZ_VAL 4 +#define DA7218_PLL_INDIV_18_TO_36_MHZ_VAL 8 +#define DA7218_PLL_INDIV_36_TO_54_MHZ_VAL 16 + /* ALC Calibration */ #define DA7218_ALC_CALIB_DELAY_MIN 2500 #define DA7218_ALC_CALIB_DELAY_MAX 5000 diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 81c0708b85c1..5c93899f1f0e 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -11,6 +11,7 @@ * option) any later version. */ +#include <linux/acpi.h> #include <linux/clk.h> #include <linux/i2c.h> #include <linux/of_device.h> @@ -1025,7 +1026,7 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq)) return 0; - if (((freq < 2000000) && (freq != 32768)) || (freq > 54000000)) { + if ((freq < 2000000) || (freq > 54000000)) { dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", freq); return -EINVAL; @@ -1079,21 +1080,21 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, dev_err(codec->dev, "PLL input clock %d below valid range\n", da7219->mclk_rate); return -EINVAL; - } else if (da7219->mclk_rate <= 5000000) { - indiv_bits = DA7219_PLL_INDIV_2_5_MHZ; - indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL; - } else if (da7219->mclk_rate <= 10000000) { - indiv_bits = DA7219_PLL_INDIV_5_10_MHZ; - indiv = DA7219_PLL_INDIV_5_10_MHZ_VAL; - } else if (da7219->mclk_rate <= 20000000) { - indiv_bits = DA7219_PLL_INDIV_10_20_MHZ; - indiv = DA7219_PLL_INDIV_10_20_MHZ_VAL; - } else if (da7219->mclk_rate <= 40000000) { - indiv_bits = DA7219_PLL_INDIV_20_40_MHZ; - indiv = DA7219_PLL_INDIV_20_40_MHZ_VAL; + } else if (da7219->mclk_rate <= 4500000) { + indiv_bits = DA7219_PLL_INDIV_2_TO_4_5_MHZ; + indiv = DA7219_PLL_INDIV_2_TO_4_5_MHZ_VAL; + } else if (da7219->mclk_rate <= 9000000) { + indiv_bits = DA7219_PLL_INDIV_4_5_TO_9_MHZ; + indiv = DA7219_PLL_INDIV_4_5_TO_9_MHZ_VAL; + } else if (da7219->mclk_rate <= 18000000) { + indiv_bits = DA7219_PLL_INDIV_9_TO_18_MHZ; + indiv = DA7219_PLL_INDIV_9_TO_18_MHZ_VAL; + } else if (da7219->mclk_rate <= 36000000) { + indiv_bits = DA7219_PLL_INDIV_18_TO_36_MHZ; + indiv = DA7219_PLL_INDIV_18_TO_36_MHZ_VAL; } else if (da7219->mclk_rate <= 54000000) { - indiv_bits = DA7219_PLL_INDIV_40_54_MHZ; - indiv = DA7219_PLL_INDIV_40_54_MHZ_VAL; + indiv_bits = DA7219_PLL_INDIV_36_TO_54_MHZ; + indiv = DA7219_PLL_INDIV_36_TO_54_MHZ_VAL; } else { dev_err(codec->dev, "PLL input clock %d above valid range\n", da7219->mclk_rate); @@ -1426,6 +1427,12 @@ static const struct of_device_id da7219_of_match[] = { }; MODULE_DEVICE_TABLE(of, da7219_of_match); +static const struct acpi_device_id da7219_acpi_match[] = { + { .id = "DLGS7219", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, da7219_acpi_match); + static enum da7219_micbias_voltage da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val) { @@ -1955,6 +1962,7 @@ static struct i2c_driver da7219_i2c_driver = { .driver = { .name = "da7219", .of_match_table = of_match_ptr(da7219_of_match), + .acpi_match_table = ACPI_PTR(da7219_acpi_match), }, .probe = da7219_i2c_probe, .remove = da7219_i2c_remove, diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h index 5a787e738084..ff2a2f02ce40 100644 --- a/sound/soc/codecs/da7219.h +++ b/sound/soc/codecs/da7219.h @@ -194,11 +194,11 @@ /* DA7219_PLL_CTRL = 0x20 */ #define DA7219_PLL_INDIV_SHIFT 2 #define DA7219_PLL_INDIV_MASK (0x7 << 2) -#define DA7219_PLL_INDIV_2_5_MHZ (0x0 << 2) -#define DA7219_PLL_INDIV_5_10_MHZ (0x1 << 2) -#define DA7219_PLL_INDIV_10_20_MHZ (0x2 << 2) -#define DA7219_PLL_INDIV_20_40_MHZ (0x3 << 2) -#define DA7219_PLL_INDIV_40_54_MHZ (0x4 << 2) +#define DA7219_PLL_INDIV_2_TO_4_5_MHZ (0x0 << 2) +#define DA7219_PLL_INDIV_4_5_TO_9_MHZ (0x1 << 2) +#define DA7219_PLL_INDIV_9_TO_18_MHZ (0x2 << 2) +#define DA7219_PLL_INDIV_18_TO_36_MHZ (0x3 << 2) +#define DA7219_PLL_INDIV_36_TO_54_MHZ (0x4 << 2) #define DA7219_PLL_MCLK_SQR_EN_SHIFT 5 #define DA7219_PLL_MCLK_SQR_EN_MASK (0x1 << 5) #define DA7219_PLL_MODE_SHIFT 6 @@ -761,11 +761,11 @@ #define DA7219_PLL_FREQ_OUT_98304 98304000 /* PLL Frequency Dividers */ -#define DA7219_PLL_INDIV_2_5_MHZ_VAL 1 -#define DA7219_PLL_INDIV_5_10_MHZ_VAL 2 -#define DA7219_PLL_INDIV_10_20_MHZ_VAL 4 -#define DA7219_PLL_INDIV_20_40_MHZ_VAL 8 -#define DA7219_PLL_INDIV_40_54_MHZ_VAL 16 +#define DA7219_PLL_INDIV_2_TO_4_5_MHZ_VAL 1 +#define DA7219_PLL_INDIV_4_5_TO_9_MHZ_VAL 2 +#define DA7219_PLL_INDIV_9_TO_18_MHZ_VAL 4 +#define DA7219_PLL_INDIV_18_TO_36_MHZ_VAL 8 +#define DA7219_PLL_INDIV_36_TO_54_MHZ_VAL 16 /* SRM */ #define DA7219_SRM_CHECK_RETRIES 8 diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index afa6c5db9dcc..2086d7107622 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -26,18 +26,30 @@ #include <sound/tlv.h> #include "es8328.h" -#define ES8328_SYSCLK_RATE_1X 11289600 -#define ES8328_SYSCLK_RATE_2X 22579200 +static const unsigned int rates_12288[] = { + 8000, 12000, 16000, 24000, 32000, 48000, 96000, +}; -/* Run the codec at 22.5792 or 11.2896 MHz to support these rates */ -static struct { - int rate; - u8 ratio; -} mclk_ratios[] = { - { 8000, 9 }, - {11025, 7 }, - {22050, 4 }, - {44100, 2 }, +static const int ratios_12288[] = { + 10, 7, 6, 4, 3, 2, 0, +}; + +static const struct snd_pcm_hw_constraint_list constraints_12288 = { + .count = ARRAY_SIZE(rates_12288), + .list = rates_12288, +}; + +static const unsigned int rates_11289[] = { + 8018, 11025, 22050, 44100, 88200, +}; + +static const int ratios_11289[] = { + 9, 7, 4, 2, 0, +}; + +static const struct snd_pcm_hw_constraint_list constraints_11289 = { + .count = ARRAY_SIZE(rates_11289), + .list = rates_11289, }; /* regulator supplies for sgtl5000, VDDD is an optional external supply */ @@ -57,16 +69,28 @@ static const char * const supply_names[ES8328_SUPPLY_NUM] = { "HPVDD", }; -#define ES8328_RATES (SNDRV_PCM_RATE_44100 | \ +#define ES8328_RATES (SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_32000 | \ SNDRV_PCM_RATE_22050 | \ - SNDRV_PCM_RATE_11025) -#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_11025 | \ + SNDRV_PCM_RATE_8000) +#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) struct es8328_priv { struct regmap *regmap; struct clk *clk; int playback_fs; bool deemph; + int mclkdiv2; + const struct snd_pcm_hw_constraint_list *sysclk_constraints; + const int *mclk_ratios; struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM]; }; @@ -439,54 +463,131 @@ static int es8328_mute(struct snd_soc_dai *dai, int mute) mute ? ES8328_DACCONTROL3_DACMUTE : 0); } +static int es8328_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); + + if (es8328->sysclk_constraints) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + es8328->sysclk_constraints); + + return 0; +} + static int es8328_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 es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); - int clk_rate; int i; int reg; - u8 ratio; + int wl; + int ratio; + + if (!es8328->sysclk_constraints) { + dev_err(codec->dev, "No MCLK configured\n"); + return -EINVAL; + } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) reg = ES8328_DACCONTROL2; else reg = ES8328_ADCCONTROL5; - clk_rate = clk_get_rate(es8328->clk); + for (i = 0; i < es8328->sysclk_constraints->count; i++) + if (es8328->sysclk_constraints->list[i] == params_rate(params)) + break; - if ((clk_rate != ES8328_SYSCLK_RATE_1X) && - (clk_rate != ES8328_SYSCLK_RATE_2X)) { - dev_err(codec->dev, - "%s: clock is running at %d Hz, not %d or %d Hz\n", - __func__, clk_rate, - ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X); + if (i == es8328->sysclk_constraints->count) { + dev_err(codec->dev, "LRCLK %d unsupported with current clock\n", + params_rate(params)); return -EINVAL; } - /* find master mode MCLK to sampling frequency ratio */ - ratio = mclk_ratios[0].rate; - for (i = 1; i < ARRAY_SIZE(mclk_ratios); i++) - if (params_rate(params) <= mclk_ratios[i].rate) - ratio = mclk_ratios[i].ratio; + ratio = es8328->mclk_ratios[i]; + snd_soc_update_bits(codec, ES8328_MASTERMODE, + ES8328_MASTERMODE_MCLKDIV2, + es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0); + + switch (params_width(params)) { + case 16: + wl = 3; + break; + case 18: + wl = 2; + break; + case 20: + wl = 1; + break; + case 24: + wl = 0; + break; + case 32: + wl = 4; + break; + default: + return -EINVAL; + } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_update_bits(codec, ES8328_DACCONTROL1, + ES8328_DACCONTROL1_DACWL_MASK, + wl << ES8328_DACCONTROL1_DACWL_SHIFT); + es8328->playback_fs = params_rate(params); es8328_set_deemph(codec); - } + } else + snd_soc_update_bits(codec, ES8328_ADCCONTROL4, + ES8328_ADCCONTROL4_ADCWL_MASK, + wl << ES8328_ADCCONTROL4_ADCWL_SHIFT); return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio); } +static int es8328_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); + int mclkdiv2 = 0; + + switch (freq) { + case 0: + es8328->sysclk_constraints = NULL; + es8328->mclk_ratios = NULL; + break; + case 22579200: + mclkdiv2 = 1; + /* fallthru */ + case 11289600: + es8328->sysclk_constraints = &constraints_11289; + es8328->mclk_ratios = ratios_11289; + break; + case 24576000: + mclkdiv2 = 1; + /* fallthru */ + case 12288000: + es8328->sysclk_constraints = &constraints_12288; + es8328->mclk_ratios = ratios_12288; + break; + default: + return -EINVAL; + } + + es8328->mclkdiv2 = mclkdiv2; + return 0; +} + static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; - struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); - int clk_rate; - u8 mode = ES8328_DACCONTROL1_DACWL_16; + u8 dac_mode = 0; + u8 adc_mode = 0; /* set master/slave audio interface */ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM) @@ -495,13 +596,16 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - mode |= ES8328_DACCONTROL1_DACFORMAT_I2S; + dac_mode |= ES8328_DACCONTROL1_DACFORMAT_I2S; + adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_I2S; break; case SND_SOC_DAIFMT_RIGHT_J: - mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST; + dac_mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST; + adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_RJUST; break; case SND_SOC_DAIFMT_LEFT_J: - mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST; + dac_mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST; + adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_LJUST; break; default: return -EINVAL; @@ -511,18 +615,14 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) return -EINVAL; - snd_soc_write(codec, ES8328_DACCONTROL1, mode); - snd_soc_write(codec, ES8328_ADCCONTROL4, mode); + snd_soc_update_bits(codec, ES8328_DACCONTROL1, + ES8328_DACCONTROL1_DACFORMAT_MASK, dac_mode); + snd_soc_update_bits(codec, ES8328_ADCCONTROL4, + ES8328_ADCCONTROL4_ADCFORMAT_MASK, adc_mode); /* Master serial port mode, with BCLK generated automatically */ - clk_rate = clk_get_rate(es8328->clk); - if (clk_rate == ES8328_SYSCLK_RATE_1X) - snd_soc_write(codec, ES8328_MASTERMODE, - ES8328_MASTERMODE_MSC); - else - snd_soc_write(codec, ES8328_MASTERMODE, - ES8328_MASTERMODE_MCLKDIV2 | - ES8328_MASTERMODE_MSC); + snd_soc_update_bits(codec, ES8328_MASTERMODE, + ES8328_MASTERMODE_MSC, ES8328_MASTERMODE_MSC); return 0; } @@ -579,8 +679,10 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec, } static const struct snd_soc_dai_ops es8328_dai_ops = { + .startup = es8328_startup, .hw_params = es8328_hw_params, .digital_mute = es8328_mute, + .set_sysclk = es8328_set_sysclk, .set_fmt = es8328_set_dai_fmt, }; @@ -601,6 +703,7 @@ static struct snd_soc_dai_driver es8328_dai = { .formats = ES8328_FORMATS, }, .ops = &es8328_dai_ops, + .symmetric_rates = 1, }; static int es8328_suspend(struct snd_soc_codec *codec) @@ -708,6 +811,7 @@ const struct regmap_config es8328_regmap_config = { .val_bits = 8, .max_register = ES8328_REG_MAX, .cache_type = REGCACHE_RBTREE, + .use_single_rw = true, }; EXPORT_SYMBOL_GPL(es8328_regmap_config); diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h index 156c748c89c7..1a736e72a929 100644 --- a/sound/soc/codecs/es8328.h +++ b/sound/soc/codecs/es8328.h @@ -22,7 +22,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap); #define ES8328_CONTROL1_VMIDSEL_50k (1 << 0) #define ES8328_CONTROL1_VMIDSEL_500k (2 << 0) #define ES8328_CONTROL1_VMIDSEL_5k (3 << 0) -#define ES8328_CONTROL1_VMIDSEL_MASK (7 << 0) +#define ES8328_CONTROL1_VMIDSEL_MASK (3 << 0) #define ES8328_CONTROL1_ENREF (1 << 2) #define ES8328_CONTROL1_SEQEN (1 << 3) #define ES8328_CONTROL1_SAMEFS (1 << 4) @@ -84,7 +84,20 @@ int es8328_probe(struct device *dev, struct regmap *regmap); #define ES8328_ADCCONTROL1 0x09 #define ES8328_ADCCONTROL2 0x0a #define ES8328_ADCCONTROL3 0x0b + #define ES8328_ADCCONTROL4 0x0c +#define ES8328_ADCCONTROL4_ADCFORMAT_MASK (3 << 0) +#define ES8328_ADCCONTROL4_ADCFORMAT_I2S (0 << 0) +#define ES8328_ADCCONTROL4_ADCFORMAT_LJUST (1 << 0) +#define ES8328_ADCCONTROL4_ADCFORMAT_RJUST (2 << 0) +#define ES8328_ADCCONTROL4_ADCFORMAT_PCM (3 << 0) +#define ES8328_ADCCONTROL4_ADCWL_SHIFT 2 +#define ES8328_ADCCONTROL4_ADCWL_MASK (7 << 2) +#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_NORMAL (0 << 5) +#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_INV (1 << 5) +#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK2 (0 << 5) +#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK1 (1 << 5) + #define ES8328_ADCCONTROL5 0x0d #define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0) @@ -109,15 +122,13 @@ int es8328_probe(struct device *dev, struct regmap *regmap); #define ES8328_ADCCONTROL14 0x16 #define ES8328_DACCONTROL1 0x17 +#define ES8328_DACCONTROL1_DACFORMAT_MASK (3 << 1) #define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1) #define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1) #define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1) #define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1) -#define ES8328_DACCONTROL1_DACWL_24 (0 << 3) -#define ES8328_DACCONTROL1_DACWL_20 (1 << 3) -#define ES8328_DACCONTROL1_DACWL_18 (2 << 3) -#define ES8328_DACCONTROL1_DACWL_16 (3 << 3) -#define ES8328_DACCONTROL1_DACWL_32 (4 << 3) +#define ES8328_DACCONTROL1_DACWL_SHIFT 3 +#define ES8328_DACCONTROL1_DACWL_MASK (7 << 3) #define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6) #define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6) #define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6) diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index aaa038ffc8a5..37332976ccbb 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -29,6 +29,7 @@ #include <sound/hdaudio_ext.h> #include <sound/hda_i915.h> #include <sound/pcm_drm_eld.h> +#include <sound/hda_chmap.h> #include "../../hda/local.h" #include "hdac_hdmi.h" @@ -60,11 +61,17 @@ struct hdac_hdmi_cvt { struct hdac_hdmi_cvt_params params; }; +/* Currently only spk_alloc, more to be added */ +struct hdac_hdmi_parsed_eld { + u8 spk_alloc; +}; + struct hdac_hdmi_eld { bool monitor_present; bool eld_valid; int eld_size; char eld_buffer[ELD_MAX_SIZE]; + struct hdac_hdmi_parsed_eld info; }; struct hdac_hdmi_pin { @@ -76,6 +83,10 @@ struct hdac_hdmi_pin { struct hdac_ext_device *edev; int repoll_count; struct delayed_work work; + struct mutex lock; + bool chmap_set; + unsigned char chmap[8]; /* ALSA API channel-map */ + int channels; /* current number of channels */ }; struct hdac_hdmi_pcm { @@ -100,8 +111,22 @@ struct hdac_hdmi_priv { int num_pin; int num_cvt; struct mutex pin_mutex; + struct hdac_chmap chmap; }; +static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi, + int pcm_idx) +{ + struct hdac_hdmi_pcm *pcm; + + list_for_each_entry(pcm, &hdmi->pcm_list, head) { + if (pcm->pcm_id == pcm_idx) + return pcm; + } + + return NULL; +} + static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev) { struct hdac_device *hdac = dev_to_hdac_dev(dev); @@ -278,26 +303,31 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, int i; const u8 *eld_buf; u8 conn_type; - int channels = 2; + int channels, ca; list_for_each_entry(pin, &hdmi->pin_list, head) { if (pin->nid == pin_nid) break; } + ca = snd_hdac_channel_allocation(&hdac->hdac, pin->eld.info.spk_alloc, + pin->channels, pin->chmap_set, true, pin->chmap); + + channels = snd_hdac_get_active_channels(ca); + hdmi->chmap.ops.set_channel_count(&hdac->hdac, cvt_nid, channels); + + snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca, + pin->channels, pin->chmap, pin->chmap_set); + eld_buf = pin->eld.eld_buffer; conn_type = drm_eld_get_conn_type(eld_buf); - /* setup channel count */ - snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, channels - 1); - switch (conn_type) { case DRM_ELD_CONN_TYPE_HDMI: hdmi_audio_infoframe_init(&frame); - /* Default stereo for now */ frame.channels = channels; + frame.channel_allocation = ca; ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); if (ret < 0) @@ -311,7 +341,7 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, dp_ai.len = 0x1b; dp_ai.ver = 0x11 << 2; dp_ai.CC02_CT47 = channels - 1; - dp_ai.CA = 0; + dp_ai.CA = ca; dip = (u8 *)&dp_ai; break; @@ -370,17 +400,23 @@ static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream, struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); struct hdac_hdmi_priv *hdmi = hdac->private_data; struct hdac_hdmi_dai_pin_map *dai_map; + struct hdac_hdmi_pin *pin; struct hdac_ext_dma_params *dd; int ret; dai_map = &hdmi->dai_map[dai->id]; + pin = dai_map->pin; dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream); dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n", dd->stream_tag, dd->format); + mutex_lock(&pin->lock); + pin->channels = substream->runtime->channels; + ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid, dai_map->pin->nid); + mutex_unlock(&pin->lock); if (ret < 0) return ret; @@ -640,6 +676,12 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + mutex_lock(&dai_map->pin->lock); + dai_map->pin->chmap_set = false; + memset(dai_map->pin->chmap, 0, sizeof(dai_map->pin->chmap)); + dai_map->pin->channels = 0; + mutex_unlock(&dai_map->pin->lock); + dai_map->pin = NULL; } } @@ -647,10 +689,19 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, static int hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt) { + unsigned int chans; + struct hdac_ext_device *edev = to_ehdac_device(hdac); + struct hdac_hdmi_priv *hdmi = edev->private_data; int err; - /* Only stereo supported as of now */ - cvt->params.channels_min = cvt->params.channels_max = 2; + chans = get_wcaps(hdac, cvt->nid); + chans = get_wcaps_channels(chans); + + cvt->params.channels_min = 2; + + cvt->params.channels_max = chans; + if (chans > hdmi->chmap.channels_max) + hdmi->chmap.channels_max = chans; err = snd_hdac_query_supported_pcm(hdac, cvt->nid, &cvt->params.rates, @@ -1008,6 +1059,12 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid) return hdac_hdmi_query_cvt_params(&edev->hdac, cvt); } +static void hdac_hdmi_parse_eld(struct hdac_ext_device *edev, + struct hdac_hdmi_pin *pin) +{ + pin->eld.info.spk_alloc = pin->eld.eld_buffer[DRM_ELD_SPEAKER]; +} + static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll) { struct hdac_ext_device *edev = pin->edev; @@ -1065,6 +1122,7 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll) snd_jack_report(pcm->jack, SND_JACK_AVOUT); } + hdac_hdmi_parse_eld(edev, pin); print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET, pin->eld.eld_buffer, pin->eld.eld_size); @@ -1123,6 +1181,7 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) hdmi->num_pin++; pin->edev = edev; + mutex_init(&pin->lock); INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld); return 0; @@ -1342,6 +1401,19 @@ static struct i915_audio_component_audio_ops aops = { .pin_eld_notify = hdac_hdmi_eld_notify_cb, }; +static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card, + int device) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (rtd->pcm && (rtd->pcm->device == device)) + return rtd->pcm; + } + + return NULL; +} + int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device) { char jack_name[NAME_SIZE]; @@ -1351,6 +1423,8 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device) snd_soc_component_get_dapm(&codec->component); struct hdac_hdmi_priv *hdmi = edev->private_data; struct hdac_hdmi_pcm *pcm; + struct snd_pcm *snd_pcm; + int err; /* * this is a new PCM device, create new pcm and @@ -1362,6 +1436,18 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device) pcm->pcm_id = device; pcm->cvt = hdmi->dai_map[dai->id].cvt; + snd_pcm = hdac_hdmi_get_pcm_from_id(dai->component->card, device); + if (snd_pcm) { + err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap); + if (err < 0) { + dev_err(&edev->hdac.dev, + "chmap control add failed with err: %d for pcm: %d\n", + err, device); + kfree(pcm); + return err; + } + } + list_add_tail(&pcm->head, &hdmi->pcm_list); sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device); @@ -1378,10 +1464,18 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec) struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(&codec->component); struct hdac_hdmi_pin *pin; + struct hdac_ext_link *hlink = NULL; int ret; edev->scodec = codec; + /* + * hold the ref while we probe, also no need to drop the ref on + * exit, we call pm_runtime_suspend() so that will do for us + */ + hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev)); + snd_hdac_ext_bus_link_get(edev->ebus, hlink); + ret = create_fill_widget_route_map(dapm); if (ret < 0) return ret; @@ -1475,19 +1569,83 @@ static struct snd_soc_codec_driver hdmi_hda_codec = { .idle_bias_off = true, }; +static void hdac_hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap) +{ + struct hdac_ext_device *edev = to_ehdac_device(hdac); + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); + struct hdac_hdmi_pin *pin = pcm->pin; + + /* chmap is already set to 0 in caller */ + if (!pin) + return; + + memcpy(chmap, pin->chmap, ARRAY_SIZE(pin->chmap)); +} + +static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap, int prepared) +{ + struct hdac_ext_device *edev = to_ehdac_device(hdac); + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); + struct hdac_hdmi_pin *pin = pcm->pin; + + mutex_lock(&pin->lock); + pin->chmap_set = true; + memcpy(pin->chmap, chmap, ARRAY_SIZE(pin->chmap)); + if (prepared) + hdac_hdmi_setup_audio_infoframe(edev, pcm->cvt->nid, pin->nid); + mutex_unlock(&pin->lock); +} + +static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) +{ + struct hdac_ext_device *edev = to_ehdac_device(hdac); + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); + struct hdac_hdmi_pin *pin = pcm->pin; + + return pin ? true:false; +} + +static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) +{ + struct hdac_ext_device *edev = to_ehdac_device(hdac); + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); + struct hdac_hdmi_pin *pin = pcm->pin; + + if (!pin || !pin->eld.eld_valid) + return 0; + + return pin->eld.info.spk_alloc; +} + static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) { struct hdac_device *codec = &edev->hdac; struct hdac_hdmi_priv *hdmi_priv; struct snd_soc_dai_driver *hdmi_dais = NULL; + struct hdac_ext_link *hlink = NULL; int num_dais = 0; int ret = 0; + /* hold the ref while we probe */ + hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev)); + snd_hdac_ext_bus_link_get(edev->ebus, hlink); + hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL); if (hdmi_priv == NULL) return -ENOMEM; edev->private_data = hdmi_priv; + snd_hdac_register_chmap_ops(codec, &hdmi_priv->chmap); + hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap; + hdmi_priv->chmap.ops.set_chmap = hdac_hdmi_set_chmap; + hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached; + hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc; dev_set_drvdata(&codec->dev, edev); @@ -1516,8 +1674,12 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) } /* ASoC specific initialization */ - return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec, - hdmi_dais, num_dais); + ret = snd_soc_register_codec(&codec->dev, &hdmi_hda_codec, + hdmi_dais, num_dais); + + snd_hdac_ext_bus_link_put(edev->ebus, hlink); + + return ret; } static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) @@ -1556,6 +1718,9 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) struct hdac_ext_device *edev = to_hda_ext_device(dev); struct hdac_device *hdac = &edev->hdac; struct hdac_bus *bus = hdac->bus; + unsigned long timeout; + struct hdac_ext_bus *ebus = hbus_to_ebus(bus); + struct hdac_ext_link *hlink = NULL; int err; dev_dbg(dev, "Enter: %s\n", __func__); @@ -1579,6 +1744,9 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) return err; } + hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev)); + snd_hdac_ext_bus_link_put(ebus, hlink); + return 0; } @@ -1587,6 +1755,8 @@ static int hdac_hdmi_runtime_resume(struct device *dev) struct hdac_ext_device *edev = to_hda_ext_device(dev); struct hdac_device *hdac = &edev->hdac; struct hdac_bus *bus = hdac->bus; + struct hdac_ext_bus *ebus = hbus_to_ebus(bus); + struct hdac_ext_link *hlink = NULL; int err; dev_dbg(dev, "Enter: %s\n", __func__); @@ -1595,6 +1765,9 @@ static int hdac_hdmi_runtime_resume(struct device *dev) if (!bus) return 0; + hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev)); + snd_hdac_ext_bus_link_get(ebus, hlink); + err = snd_hdac_display_power(bus, true); if (err < 0) { dev_err(bus->dev, "Cannot turn on display power on i915\n"); diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c new file mode 100644 index 000000000000..8e36e883e453 --- /dev/null +++ b/sound/soc/codecs/hdmi-codec.c @@ -0,0 +1,432 @@ +/* + * ALSA SoC codec for HDMI encoder drivers + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Jyri Sarha <jsarha@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include <linux/module.h> +#include <linux/string.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/pcm_drm_eld.h> +#include <sound/hdmi-codec.h> +#include <sound/pcm_iec958.h> + +#include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */ + +struct hdmi_codec_priv { + struct hdmi_codec_pdata hcd; + struct snd_soc_dai_driver *daidrv; + struct hdmi_codec_daifmt daifmt[2]; + struct mutex current_stream_lock; + struct snd_pcm_substream *current_stream; + struct snd_pcm_hw_constraint_list ratec; + uint8_t eld[MAX_ELD_BYTES]; +}; + +static const struct snd_soc_dapm_widget hdmi_widgets[] = { + SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route hdmi_routes[] = { + { "TX", NULL, "Playback" }, +}; + +enum { + DAI_ID_I2S = 0, + DAI_ID_SPDIF, +}; + +static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(hcp->eld); + + return 0; +} + +static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + + memcpy(ucontrol->value.bytes.data, hcp->eld, sizeof(hcp->eld)); + + return 0; +} + +static const struct snd_kcontrol_new hdmi_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "ELD", + .info = hdmi_eld_ctl_info, + .get = hdmi_eld_ctl_get, + }, +}; + +static int hdmi_codec_new_stream(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + mutex_lock(&hcp->current_stream_lock); + if (!hcp->current_stream) { + hcp->current_stream = substream; + } else if (hcp->current_stream != substream) { + dev_err(dai->dev, "Only one simultaneous stream supported!\n"); + ret = -EINVAL; + } + mutex_unlock(&hcp->current_stream_lock); + + return ret; +} + +static int hdmi_codec_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + dev_dbg(dai->dev, "%s()\n", __func__); + + ret = hdmi_codec_new_stream(substream, dai); + if (ret) + return ret; + + if (hcp->hcd.ops->audio_startup) { + ret = hcp->hcd.ops->audio_startup(dai->dev->parent); + if (ret) { + mutex_lock(&hcp->current_stream_lock); + hcp->current_stream = NULL; + mutex_unlock(&hcp->current_stream_lock); + return ret; + } + } + + if (hcp->hcd.ops->get_eld) { + ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->eld, + sizeof(hcp->eld)); + + if (!ret) { + ret = snd_pcm_hw_constraint_eld(substream->runtime, + hcp->eld); + if (ret) + return ret; + } + } + return 0; +} + +static void hdmi_codec_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + + dev_dbg(dai->dev, "%s()\n", __func__); + + WARN_ON(hcp->current_stream != substream); + + hcp->hcd.ops->audio_shutdown(dai->dev->parent); + + mutex_lock(&hcp->current_stream_lock); + hcp->current_stream = NULL; + mutex_unlock(&hcp->current_stream_lock); +} + +static int hdmi_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + struct hdmi_codec_params hp = { + .iec = { + .status = { 0 }, + .subcode = { 0 }, + .pad = 0, + .dig_subframe = { 0 }, + } + }; + int ret; + + dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__, + params_width(params), params_rate(params), + params_channels(params)); + + if (params_width(params) > 24) + params->msbits = 24; + + ret = snd_pcm_create_iec958_consumer_hw_params(params, hp.iec.status, + sizeof(hp.iec.status)); + if (ret < 0) { + dev_err(dai->dev, "Creating IEC958 channel status failed %d\n", + ret); + return ret; + } + + ret = hdmi_codec_new_stream(substream, dai); + if (ret) + return ret; + + hdmi_audio_infoframe_init(&hp.cea); + hp.cea.channels = params_channels(params); + hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; + hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; + hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; + + hp.sample_width = params_width(params); + hp.sample_rate = params_rate(params); + hp.channels = params_channels(params); + + return hcp->hcd.ops->hw_params(dai->dev->parent, &hcp->daifmt[dai->id], + &hp); +} + +static int hdmi_codec_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + struct hdmi_codec_daifmt cf = { 0 }; + int ret = 0; + + dev_dbg(dai->dev, "%s()\n", __func__); + + if (dai->id == DAI_ID_SPDIF) { + cf.fmt = HDMI_SPDIF; + } else { + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + cf.bit_clk_master = 1; + cf.frame_clk_master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFM: + cf.frame_clk_master = 1; + break; + case SND_SOC_DAIFMT_CBM_CFS: + cf.bit_clk_master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + cf.frame_clk_inv = 1; + break; + case SND_SOC_DAIFMT_IB_NF: + cf.bit_clk_inv = 1; + break; + case SND_SOC_DAIFMT_IB_IF: + cf.frame_clk_inv = 1; + cf.bit_clk_inv = 1; + break; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + cf.fmt = HDMI_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + cf.fmt = HDMI_DSP_A; + break; + case SND_SOC_DAIFMT_DSP_B: + cf.fmt = HDMI_DSP_B; + break; + case SND_SOC_DAIFMT_RIGHT_J: + cf.fmt = HDMI_RIGHT_J; + break; + case SND_SOC_DAIFMT_LEFT_J: + cf.fmt = HDMI_LEFT_J; + break; + case SND_SOC_DAIFMT_AC97: + cf.fmt = HDMI_AC97; + break; + default: + dev_err(dai->dev, "Invalid DAI interface format\n"); + return -EINVAL; + } + } + + hcp->daifmt[dai->id] = cf; + + return ret; +} + +static int hdmi_codec_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + + dev_dbg(dai->dev, "%s()\n", __func__); + + if (hcp->hcd.ops->digital_mute) + return hcp->hcd.ops->digital_mute(dai->dev->parent, mute); + + return 0; +} + +static const struct snd_soc_dai_ops hdmi_dai_ops = { + .startup = hdmi_codec_startup, + .shutdown = hdmi_codec_shutdown, + .hw_params = hdmi_codec_hw_params, + .set_fmt = hdmi_codec_set_fmt, + .digital_mute = hdmi_codec_digital_mute, +}; + + +#define HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + +#define SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE) + +/* + * This list is only for formats allowed on the I2S bus. So there is + * some formats listed that are not supported by HDMI interface. For + * instance allowing the 32-bit formats enables 24-precision with CPU + * DAIs that do not support 24-bit formats. If the extra formats cause + * problems, we should add the video side driver an option to disable + * them. + */ +#define I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE) + +static struct snd_soc_dai_driver hdmi_i2s_dai = { + .name = "i2s-hifi", + .id = DAI_ID_I2S, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = HDMI_RATES, + .formats = I2S_FORMATS, + .sig_bits = 24, + }, + .ops = &hdmi_dai_ops, +}; + +static const struct snd_soc_dai_driver hdmi_spdif_dai = { + .name = "spdif-hifi", + .id = DAI_ID_SPDIF, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = HDMI_RATES, + .formats = SPDIF_FORMATS, + }, + .ops = &hdmi_dai_ops, +}; + +static struct snd_soc_codec_driver hdmi_codec = { + .controls = hdmi_controls, + .num_controls = ARRAY_SIZE(hdmi_controls), + .dapm_widgets = hdmi_widgets, + .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), + .dapm_routes = hdmi_routes, + .num_dapm_routes = ARRAY_SIZE(hdmi_routes), +}; + +static int hdmi_codec_probe(struct platform_device *pdev) +{ + struct hdmi_codec_pdata *hcd = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct hdmi_codec_priv *hcp; + int dai_count, i = 0; + int ret; + + dev_dbg(dev, "%s()\n", __func__); + + if (!hcd) { + dev_err(dev, "%s: No plalform data\n", __func__); + return -EINVAL; + } + + dai_count = hcd->i2s + hcd->spdif; + if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params || + !hcd->ops->audio_shutdown) { + dev_err(dev, "%s: Invalid parameters\n", __func__); + return -EINVAL; + } + + hcp = devm_kzalloc(dev, sizeof(*hcp), GFP_KERNEL); + if (!hcp) + return -ENOMEM; + + hcp->hcd = *hcd; + mutex_init(&hcp->current_stream_lock); + + hcp->daidrv = devm_kzalloc(dev, dai_count * sizeof(*hcp->daidrv), + GFP_KERNEL); + if (!hcp->daidrv) + return -ENOMEM; + + if (hcd->i2s) { + hcp->daidrv[i] = hdmi_i2s_dai; + hcp->daidrv[i].playback.channels_max = + hcd->max_i2s_channels; + i++; + } + + if (hcd->spdif) + hcp->daidrv[i] = hdmi_spdif_dai; + + ret = snd_soc_register_codec(dev, &hdmi_codec, hcp->daidrv, + dai_count); + if (ret) { + dev_err(dev, "%s: snd_soc_register_codec() failed (%d)\n", + __func__, ret); + return ret; + } + + dev_set_drvdata(dev, hcp); + return 0; +} + +static int hdmi_codec_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver hdmi_codec_driver = { + .driver = { + .name = HDMI_CODEC_DRV_NAME, + }, + .probe = hdmi_codec_probe, + .remove = hdmi_codec_remove, +}; + +module_platform_driver(hdmi_codec_driver); + +MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>"); +MODULE_DESCRIPTION("HDMI Audio Codec Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" HDMI_CODEC_DRV_NAME); diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c new file mode 100644 index 000000000000..ed515677409b --- /dev/null +++ b/sound/soc/codecs/pcm5102a.c @@ -0,0 +1,69 @@ +/* + * Driver for the PCM5102A codec + * + * Author: Florian Meier <florian.meier@koalo.de> + * Copyright 2013 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <sound/soc.h> + +static struct snd_soc_dai_driver pcm5102a_dai = { + .name = "pcm5102a-hifi", + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE + }, +}; + +static struct snd_soc_codec_driver soc_codec_dev_pcm5102a; + +static int pcm5102a_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm5102a, + &pcm5102a_dai, 1); +} + +static int pcm5102a_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static const struct of_device_id pcm5102a_of_match[] = { + { .compatible = "ti,pcm5102a", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm5102a_of_match); + +static struct platform_driver pcm5102a_codec_driver = { + .probe = pcm5102a_probe, + .remove = pcm5102a_remove, + .driver = { + .name = "pcm5102a-codec", + .owner = THIS_MODULE, + .of_match_table = pcm5102a_of_match, + }, +}; + +module_platform_driver(pcm5102a_codec_driver); + +MODULE_DESCRIPTION("ASoC PCM5102A codec driver"); +MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index f0e6c06e89ac..a1aaffc20862 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -17,6 +17,7 @@ #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> +#include <linux/dmi.h> #include <linux/acpi.h> #include <sound/core.h> #include <sound/pcm.h> @@ -1132,6 +1133,17 @@ static const struct acpi_device_id rt298_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, rt298_acpi_match); +static const struct dmi_system_id force_combo_jack_table[] = { + { + .ident = "Intel Broxton P", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp"), + DMI_MATCH(DMI_PRODUCT_NAME, "Broxton P") + } + }, + { } +}; + static int rt298_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1184,11 +1196,16 @@ static int rt298_i2c_probe(struct i2c_client *i2c, /* enable jack combo mode on supported devices */ acpiid = acpi_match_device(dev->driver->acpi_match_table, dev); - if (acpiid) { + if (acpiid && acpiid->driver_data) { rt298->pdata = *(struct rt298_platform_data *) acpiid->driver_data; } + if (dmi_check_system(force_combo_jack_table)) { + rt298->pdata.cbj_en = true; + rt298->pdata.gpio2_en = false; + } + /* VREF Charging */ regmap_update_bits(rt298->regmap, 0x04, 0x80, 0x80); regmap_update_bits(rt298->regmap, 0x1b, 0x860, 0x860); diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 7af5e7380d61..3c6594da6c9c 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3286,10 +3286,8 @@ static void rt5645_jack_detect_work(struct work_struct *work) if (btn_type == 0)/* button release */ report = rt5645->jack_type; else { - if (rt5645->pdata.jd_invert) { - mod_timer(&rt5645->btn_check_timer, - msecs_to_jiffies(100)); - } + mod_timer(&rt5645->btn_check_timer, + msecs_to_jiffies(100)); } break; @@ -3557,6 +3555,12 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = { DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), }, }, + { + .ident = "Google Setzer", + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Setzer"), + }, + }, { } }; @@ -3810,9 +3814,9 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, if (rt5645->pdata.jd_invert) { regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV); - setup_timer(&rt5645->btn_check_timer, - rt5645_btn_check_callback, (unsigned long)rt5645); } + setup_timer(&rt5645->btn_check_timer, + rt5645_btn_check_callback, (unsigned long)rt5645); INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work); INIT_DELAYED_WORK(&rt5645->rcclock_work, rt5645_rcclock_work); diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 1bae17ee8817..da60e3fe5ee7 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -2098,10 +2098,14 @@ static int wm5102_probe(struct platform_device *pdev) static int wm5102_remove(struct platform_device *pdev) { + struct wm5102_priv *wm5102 = platform_get_drvdata(pdev); + snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_codec(&pdev->dev); pm_runtime_disable(&pdev->dev); + wm_adsp2_remove(&wm5102->core.adsp[0]); + return 0; } diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 2728ac545ffe..b5820e4d5471 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -2437,10 +2437,16 @@ static int wm5110_probe(struct platform_device *pdev) static int wm5110_remove(struct platform_device *pdev) { + struct wm5110_priv *wm5110 = platform_get_drvdata(pdev); + int i; + snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_codec(&pdev->dev); pm_runtime_disable(&pdev->dev); + for (i = 0; i < WM5110_NUM_ADSP; i++) + wm_adsp2_remove(&wm5110->core.adsp[i]); + return 0; } diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index d3b1cb15e7f0..a07bd7c2c587 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -160,6 +160,8 @@ #define ADSP2_RAM_RDY_SHIFT 0 #define ADSP2_RAM_RDY_WIDTH 1 +#define ADSP_MAX_STD_CTRL_SIZE 512 + struct wm_adsp_buf { struct list_head list; void *buf; @@ -271,8 +273,11 @@ struct wm_adsp_buffer { __be32 words_written[2]; /* total words written (64 bit) */ }; +struct wm_adsp_compr; + struct wm_adsp_compr_buf { struct wm_adsp *dsp; + struct wm_adsp_compr *compr; struct wm_adsp_buffer_region *regions; u32 host_buf_ptr; @@ -435,6 +440,7 @@ struct wm_coeff_ctl { size_t len; unsigned int set:1; struct snd_kcontrol *kcontrol; + struct soc_bytes_ext bytes_ext; unsigned int flags; }; @@ -711,10 +717,17 @@ static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) be16_to_cpu(scratch[3])); } +static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) +{ + return container_of(ext, struct wm_coeff_ctl, bytes_ext); +} + static int wm_coeff_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { - struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; uinfo->count = ctl->len; @@ -763,7 +776,9 @@ static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, static int wm_coeff_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { - struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); char *p = ucontrol->value.bytes.data; int ret = 0; @@ -780,6 +795,29 @@ static int wm_coeff_put(struct snd_kcontrol *kctl, return ret; } +static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, + const unsigned int __user *bytes, unsigned int size) +{ + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + int ret = 0; + + mutex_lock(&ctl->dsp->pwr_lock); + + if (copy_from_user(ctl->cache, bytes, size)) { + ret = -EFAULT; + } else { + ctl->set = 1; + if (ctl->enabled) + ret = wm_coeff_write_control(ctl, ctl->cache, size); + } + + mutex_unlock(&ctl->dsp->pwr_lock); + + return ret; +} + static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, void *buf, size_t len) { @@ -822,7 +860,9 @@ static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, static int wm_coeff_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { - struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); char *p = ucontrol->value.bytes.data; int ret = 0; @@ -845,12 +885,72 @@ static int wm_coeff_get(struct snd_kcontrol *kctl, return ret; } +static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, + unsigned int __user *bytes, unsigned int size) +{ + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + int ret = 0; + + mutex_lock(&ctl->dsp->pwr_lock); + + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { + if (ctl->enabled) + ret = wm_coeff_read_control(ctl, ctl->cache, size); + else + ret = -EPERM; + } else { + if (!ctl->flags && ctl->enabled) + ret = wm_coeff_read_control(ctl, ctl->cache, size); + } + + if (!ret && copy_to_user(bytes, ctl->cache, size)) + ret = -EFAULT; + + mutex_unlock(&ctl->dsp->pwr_lock); + + return ret; +} + struct wmfw_ctl_work { struct wm_adsp *dsp; struct wm_coeff_ctl *ctl; struct work_struct work; }; +static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) +{ + unsigned int out, rd, wr, vol; + + if (len > ADSP_MAX_STD_CTRL_SIZE) { + rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; + wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; + vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; + + out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + } else { + rd = SNDRV_CTL_ELEM_ACCESS_READ; + wr = SNDRV_CTL_ELEM_ACCESS_WRITE; + vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; + + out = 0; + } + + if (in) { + if (in & WMFW_CTL_FLAG_READABLE) + out |= rd; + if (in & WMFW_CTL_FLAG_WRITEABLE) + out |= wr; + if (in & WMFW_CTL_FLAG_VOLATILE) + out |= vol; + } else { + out |= rd | wr | vol; + } + + return out; +} + static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) { struct snd_kcontrol_new *kcontrol; @@ -868,19 +968,15 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) kcontrol->info = wm_coeff_info; kcontrol->get = wm_coeff_get; kcontrol->put = wm_coeff_put; - kcontrol->private_value = (unsigned long)ctl; + kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kcontrol->tlv.c = snd_soc_bytes_tlv_callback; + kcontrol->private_value = (unsigned long)&ctl->bytes_ext; - if (ctl->flags) { - if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE) - kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE; - if (ctl->flags & WMFW_CTL_FLAG_READABLE) - kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ; - if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) - kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE; - } else { - kcontrol->access = SNDRV_CTL_ELEM_ACCESS_READWRITE; - kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE; - } + ctl->bytes_ext.max = ctl->len; + ctl->bytes_ext.get = wm_coeff_tlv_get; + ctl->bytes_ext.put = wm_coeff_tlv_put; + + kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1); if (ret < 0) @@ -944,6 +1040,13 @@ static void wm_adsp_ctl_work(struct work_struct *work) kfree(ctl_work); } +static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl) +{ + kfree(ctl->cache); + kfree(ctl->name); + kfree(ctl); +} + static int wm_adsp_create_control(struct wm_adsp *dsp, const struct wm_adsp_alg_region *alg_region, unsigned int offset, unsigned int len, @@ -1032,11 +1135,6 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, ctl->flags = flags; ctl->offset = offset; - if (len > 512) { - adsp_warn(dsp, "Truncating control %s from %d\n", - ctl->name, len); - len = 512; - } ctl->len = len; ctl->cache = kzalloc(ctl->len, GFP_KERNEL); if (!ctl->cache) { @@ -1564,6 +1662,19 @@ static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, return alg_region; } +static void wm_adsp_free_alg_regions(struct wm_adsp *dsp) +{ + struct wm_adsp_alg_region *alg_region; + + while (!list_empty(&dsp->alg_regions)) { + alg_region = list_first_entry(&dsp->alg_regions, + struct wm_adsp_alg_region, + list); + list_del(&alg_region->list); + kfree(alg_region); + } +} + static int wm_adsp1_setup_algs(struct wm_adsp *dsp) { struct wmfw_adsp1_id_hdr adsp1_id; @@ -1994,7 +2105,6 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; - struct wm_adsp_alg_region *alg_region; struct wm_coeff_ctl *ctl; int ret; unsigned int val; @@ -2074,13 +2184,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, list_for_each_entry(ctl, &dsp->ctl_list, list) ctl->enabled = 0; - while (!list_empty(&dsp->alg_regions)) { - alg_region = list_first_entry(&dsp->alg_regions, - struct wm_adsp_alg_region, - list); - list_del(&alg_region->list); - kfree(alg_region); - } + + wm_adsp_free_alg_regions(dsp); break; default: @@ -2222,7 +2327,6 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; - struct wm_adsp_alg_region *alg_region; struct wm_coeff_ctl *ctl; int ret; @@ -2240,9 +2344,13 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; + mutex_lock(&dsp->pwr_lock); + if (wm_adsp_fw[dsp->fw].num_caps != 0) ret = wm_adsp_buffer_init(dsp); + mutex_unlock(&dsp->pwr_lock); + break; case SND_SOC_DAPM_PRE_PMD: @@ -2269,13 +2377,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, list_for_each_entry(ctl, &dsp->ctl_list, list) ctl->enabled = 0; - while (!list_empty(&dsp->alg_regions)) { - alg_region = list_first_entry(&dsp->alg_regions, - struct wm_adsp_alg_region, - list); - list_del(&alg_region->list); - kfree(alg_region); - } + wm_adsp_free_alg_regions(dsp); if (wm_adsp_fw[dsp->fw].num_caps != 0) wm_adsp_buffer_free(dsp); @@ -2340,6 +2442,54 @@ int wm_adsp2_init(struct wm_adsp *dsp) } EXPORT_SYMBOL_GPL(wm_adsp2_init); +void wm_adsp2_remove(struct wm_adsp *dsp) +{ + struct wm_coeff_ctl *ctl; + + while (!list_empty(&dsp->ctl_list)) { + ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl, + list); + list_del(&ctl->list); + wm_adsp_free_ctl_blk(ctl); + } +} +EXPORT_SYMBOL_GPL(wm_adsp2_remove); + +static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) +{ + return compr->buf != NULL; +} + +static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) +{ + /* + * Note this will be more complex once each DSP can support multiple + * streams + */ + if (!compr->dsp->buffer) + return -EINVAL; + + compr->buf = compr->dsp->buffer; + compr->buf->compr = compr; + + return 0; +} + +static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) +{ + if (!compr) + return; + + /* Wake the poll so it can see buffer is no longer attached */ + if (compr->stream) + snd_compr_fragment_elapsed(compr->stream); + + if (wm_adsp_compr_attached(compr)) { + compr->buf->compr = NULL; + compr->buf = NULL; + } +} + int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) { struct wm_adsp_compr *compr; @@ -2393,6 +2543,7 @@ int wm_adsp_compr_free(struct snd_compr_stream *stream) mutex_lock(&dsp->pwr_lock); + wm_adsp_compr_detach(compr); dsp->compr = NULL; kfree(compr->raw_buf); @@ -2689,6 +2840,8 @@ err_buffer: static int wm_adsp_buffer_free(struct wm_adsp *dsp) { if (dsp->buffer) { + wm_adsp_compr_detach(dsp->buffer->compr); + kfree(dsp->buffer->regions); kfree(dsp->buffer); @@ -2698,25 +2851,6 @@ static int wm_adsp_buffer_free(struct wm_adsp *dsp) return 0; } -static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) -{ - return compr->buf != NULL; -} - -static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) -{ - /* - * Note this will be more complex once each DSP can support multiple - * streams - */ - if (!compr->dsp->buffer) - return -EINVAL; - - compr->buf = compr->dsp->buffer; - - return 0; -} - int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) { struct wm_adsp_compr *compr = stream->runtime->private_data; @@ -2805,21 +2939,41 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) avail += wm_adsp_buffer_size(buf); adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n", - buf->read_index, write_index, avail); + buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); buf->avail = avail; return 0; } +static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) +{ + int ret; + + ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); + if (ret < 0) { + adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret); + return ret; + } + if (buf->error != 0) { + adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error); + return -EIO; + } + + return 0; +} + int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) { - struct wm_adsp_compr_buf *buf = dsp->buffer; - struct wm_adsp_compr *compr = dsp->compr; + struct wm_adsp_compr_buf *buf; + struct wm_adsp_compr *compr; int ret = 0; mutex_lock(&dsp->pwr_lock); + buf = dsp->buffer; + compr = dsp->compr; + if (!buf) { ret = -ENODEV; goto out; @@ -2827,16 +2981,9 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) adsp_dbg(dsp, "Handling buffer IRQ\n"); - ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); - if (ret < 0) { - adsp_err(dsp, "Failed to check buffer error: %d\n", ret); - goto out; - } - if (buf->error != 0) { - adsp_err(dsp, "Buffer error occurred: %d\n", buf->error); - ret = -EIO; - goto out; - } + ret = wm_adsp_buffer_get_error(buf); + if (ret < 0) + goto out_notify; /* Wake poll to report error */ ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), &buf->irq_count); @@ -2851,6 +2998,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) goto out; } +out_notify: if (compr && compr->stream) snd_compr_fragment_elapsed(compr->stream); @@ -2879,14 +3027,16 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream, struct snd_compr_tstamp *tstamp) { struct wm_adsp_compr *compr = stream->runtime->private_data; - struct wm_adsp_compr_buf *buf = compr->buf; struct wm_adsp *dsp = compr->dsp; + struct wm_adsp_compr_buf *buf; int ret = 0; adsp_dbg(dsp, "Pointer request\n"); mutex_lock(&dsp->pwr_lock); + buf = compr->buf; + if (!compr->buf) { ret = -ENXIO; goto out; @@ -2909,6 +3059,10 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream, * DSP to inform us once a whole fragment is available. */ if (buf->avail < wm_adsp_compr_frag_words(compr)) { + ret = wm_adsp_buffer_get_error(buf); + if (ret < 0) + goto out; + ret = wm_adsp_buffer_reenable_irq(buf); if (ret < 0) { adsp_err(dsp, diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index b61cb57e600f..feb61e2c4bb4 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -92,6 +92,7 @@ extern const struct snd_kcontrol_new wm_adsp_fw_controls[]; int wm_adsp1_init(struct wm_adsp *dsp); int wm_adsp2_init(struct wm_adsp *dsp); +void wm_adsp2_remove(struct wm_adsp *dsp); int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec); int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec); int wm_adsp1_event(struct snd_soc_dapm_widget *w, |