diff options
Diffstat (limited to 'sound/soc')
144 files changed, 19537 insertions, 3917 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 7ff7d88e46dd..a012b2655e84 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -50,6 +50,7 @@ source "sound/soc/jz4740/Kconfig" source "sound/soc/nuc900/Kconfig" source "sound/soc/omap/Kconfig" source "sound/soc/kirkwood/Kconfig" +source "sound/soc/img/Kconfig" source "sound/soc/intel/Kconfig" source "sound/soc/mediatek/Kconfig" source "sound/soc/mxs/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 8eb06db32fa0..78625fae78d6 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_SND_SOC) += davinci/ obj-$(CONFIG_SND_SOC) += dwc/ obj-$(CONFIG_SND_SOC) += fsl/ obj-$(CONFIG_SND_SOC) += jz4740/ +obj-$(CONFIG_SND_SOC) += img/ obj-$(CONFIG_SND_SOC) += intel/ obj-$(CONFIG_SND_SOC) += mediatek/ obj-$(CONFIG_SND_SOC) += mxs/ diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 2d30464b81ce..06e099e802df 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -68,4 +68,13 @@ config SND_ATMEL_SOC_CLASSD help Say Y if you want to add support for Atmel ASoC driver for boards using CLASSD. + +config SND_ATMEL_SOC_PDMIC + tristate "Atmel ASoC driver for boards using PDMIC" + depends on OF && (ARCH_AT91 || COMPILE_TEST) + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO + help + Say Y if you want to add support for Atmel ASoC driver for boards using + PDMIC. endif diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index f6f7db428216..a2b127bd9c87 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -12,8 +12,10 @@ snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o snd-atmel-soc-wm8904-objs := atmel_wm8904.o snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o snd-atmel-soc-classd-objs := atmel-classd.o +snd-atmel-soc-pdmic-objs := atmel-pdmic.o obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o obj-$(CONFIG_SND_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o +obj-$(CONFIG_SND_ATMEL_SOC_PDMIC) += snd-atmel-soc-pdmic.o diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c index 8276675730ef..6107de9c538b 100644 --- a/sound/soc/atmel/atmel-classd.c +++ b/sound/soc/atmel/atmel-classd.c @@ -106,7 +106,7 @@ static const struct snd_pcm_hardware atmel_classd_hw = { .rates = ATMEL_CLASSD_RATES, .rate_min = 8000, .rate_max = 96000, - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .buffer_bytes_max = 64 * 1024, .period_bytes_min = 256, @@ -145,7 +145,7 @@ static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = { static struct snd_soc_dai_driver atmel_classd_cpu_dai = { .playback = { - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = ATMEL_CLASSD_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, @@ -171,9 +171,13 @@ atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream, return -EINVAL; } + if (params_channels(params) == 1) + slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + else + slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slave_config->direction = DMA_MEM_TO_DEV; slave_config->dst_addr = dd->phy_base + CLASSD_THR; - slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; slave_config->dst_maxburst = 1; slave_config->src_maxburst = 1; slave_config->device_fc = false; @@ -486,7 +490,7 @@ static struct snd_soc_dai_driver atmel_classd_codec_dai = { .name = ATMEL_CLASSD_CODEC_DAI_NAME, .playback = { .stream_name = "Playback", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = ATMEL_CLASSD_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE, @@ -636,8 +640,10 @@ static int atmel_classd_probe(struct platform_device *pdev) /* register sound card */ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); - if (!card) - return -ENOMEM; + if (!card) { + ret = -ENOMEM; + goto unregister_codec; + } snd_soc_card_set_drvdata(card, dd); platform_set_drvdata(pdev, card); @@ -645,16 +651,20 @@ static int atmel_classd_probe(struct platform_device *pdev) ret = atmel_classd_asoc_card_init(dev, card); if (ret) { dev_err(dev, "failed to init sound card\n"); - return ret; + goto unregister_codec; } ret = devm_snd_soc_register_card(dev, card); if (ret) { dev_err(dev, "failed to register sound card: %d\n", ret); - return ret; + goto unregister_codec; } return 0; + +unregister_codec: + snd_soc_unregister_codec(dev); + return ret; } static int atmel_classd_remove(struct platform_device *pdev) diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c new file mode 100644 index 000000000000..aee4787a0b89 --- /dev/null +++ b/sound/soc/atmel/atmel-pdmic.c @@ -0,0 +1,738 @@ +/* Atmel PDMIC driver + * + * Copyright (C) 2015 Atmel + * + * Author: Songjun Wu <songjun.wu@atmel.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 or later + * as published by the Free Software Foundation. + */ + +#include <linux/of.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/dmaengine_pcm.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> +#include "atmel-pdmic.h" + +struct atmel_pdmic_pdata { + u32 mic_min_freq; + u32 mic_max_freq; + s32 mic_offset; + const char *card_name; +}; + +struct atmel_pdmic { + dma_addr_t phy_base; + struct regmap *regmap; + struct clk *pclk; + struct clk *gclk; + int irq; + struct snd_pcm_substream *substream; + const struct atmel_pdmic_pdata *pdata; +}; + +static const struct of_device_id atmel_pdmic_of_match[] = { + { + .compatible = "atmel,sama5d2-pdmic", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, atmel_pdmic_of_match); + +#define PDMIC_OFFSET_MAX_VAL S16_MAX +#define PDMIC_OFFSET_MIN_VAL S16_MIN + +static struct atmel_pdmic_pdata *atmel_pdmic_dt_init(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct atmel_pdmic_pdata *pdata; + + if (!np) { + dev_err(dev, "device node not found\n"); + return ERR_PTR(-EINVAL); + } + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + if (of_property_read_string(np, "atmel,model", &pdata->card_name)) + pdata->card_name = "PDMIC"; + + if (of_property_read_u32(np, "atmel,mic-min-freq", + &pdata->mic_min_freq)) { + dev_err(dev, "failed to get mic-min-freq\n"); + return ERR_PTR(-EINVAL); + } + + if (of_property_read_u32(np, "atmel,mic-max-freq", + &pdata->mic_max_freq)) { + dev_err(dev, "failed to get mic-max-freq\n"); + return ERR_PTR(-EINVAL); + } + + if (pdata->mic_max_freq < pdata->mic_min_freq) { + dev_err(dev, + "mic-max-freq should not less than mic-min-freq\n"); + return ERR_PTR(-EINVAL); + } + + if (of_property_read_s32(np, "atmel,mic-offset", &pdata->mic_offset)) + pdata->mic_offset = 0; + + if (pdata->mic_offset > PDMIC_OFFSET_MAX_VAL) { + dev_warn(dev, + "mic-offset value %d is larger than the max value %d, the max value is specified\n", + pdata->mic_offset, PDMIC_OFFSET_MAX_VAL); + pdata->mic_offset = PDMIC_OFFSET_MAX_VAL; + } else if (pdata->mic_offset < PDMIC_OFFSET_MIN_VAL) { + dev_warn(dev, + "mic-offset value %d is less than the min value %d, the min value is specified\n", + pdata->mic_offset, PDMIC_OFFSET_MIN_VAL); + pdata->mic_offset = PDMIC_OFFSET_MIN_VAL; + } + + return pdata; +} + +/* cpu dai component */ +static int atmel_pdmic_cpu_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card); + int ret; + + ret = clk_prepare_enable(dd->gclk); + if (ret) + return ret; + + ret = clk_prepare_enable(dd->pclk); + if (ret) + return ret; + + /* Clear all bits in the Control Register(PDMIC_CR) */ + regmap_write(dd->regmap, PDMIC_CR, 0); + + dd->substream = substream; + + /* Enable the overrun error interrupt */ + regmap_write(dd->regmap, PDMIC_IER, PDMIC_IER_OVRE); + + return 0; +} + +static void atmel_pdmic_cpu_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card); + + /* Disable the overrun error interrupt */ + regmap_write(dd->regmap, PDMIC_IDR, PDMIC_IDR_OVRE); + + clk_disable_unprepare(dd->gclk); + clk_disable_unprepare(dd->pclk); +} + +static int atmel_pdmic_cpu_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card); + u32 val; + + /* Clean the PDMIC Converted Data Register */ + return regmap_read(dd->regmap, PDMIC_CDR, &val); +} + +static const struct snd_soc_dai_ops atmel_pdmic_cpu_dai_ops = { + .startup = atmel_pdmic_cpu_dai_startup, + .shutdown = atmel_pdmic_cpu_dai_shutdown, + .prepare = atmel_pdmic_cpu_dai_prepare, +}; + +#define ATMEL_PDMIC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver atmel_pdmic_cpu_dai = { + .capture = { + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = ATMEL_PDMIC_FORMATS,}, + .ops = &atmel_pdmic_cpu_dai_ops, +}; + +static const struct snd_soc_component_driver atmel_pdmic_cpu_dai_component = { + .name = "atmel-pdmic", +}; + +/* platform */ +#define ATMEL_PDMIC_MAX_BUF_SIZE (64 * 1024) +#define ATMEL_PDMIC_PREALLOC_BUF_SIZE ATMEL_PDMIC_MAX_BUF_SIZE + +static const struct snd_pcm_hardware atmel_pdmic_hw = { + .info = SNDRV_PCM_INFO_MMAP + | SNDRV_PCM_INFO_MMAP_VALID + | SNDRV_PCM_INFO_INTERLEAVED + | SNDRV_PCM_INFO_RESUME + | SNDRV_PCM_INFO_PAUSE, + .formats = ATMEL_PDMIC_FORMATS, + .buffer_bytes_max = ATMEL_PDMIC_MAX_BUF_SIZE, + .period_bytes_min = 256, + .period_bytes_max = 32 * 1024, + .periods_min = 2, + .periods_max = 256, +}; + +static int +atmel_pdmic_platform_configure_dma(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct dma_slave_config *slave_config) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card); + int ret; + + ret = snd_hwparams_to_dma_slave_config(substream, params, + slave_config); + if (ret) { + dev_err(rtd->platform->dev, + "hw params to dma slave configure failed\n"); + return ret; + } + + slave_config->src_addr = dd->phy_base + PDMIC_CDR; + slave_config->src_maxburst = 1; + slave_config->dst_maxburst = 1; + + return 0; +} + +static const struct snd_dmaengine_pcm_config +atmel_pdmic_dmaengine_pcm_config = { + .prepare_slave_config = atmel_pdmic_platform_configure_dma, + .pcm_hardware = &atmel_pdmic_hw, + .prealloc_buffer_size = ATMEL_PDMIC_PREALLOC_BUF_SIZE, +}; + +/* codec */ +/* Mic Gain = dgain * 2^(-scale) */ +struct mic_gain { + unsigned int dgain; + unsigned int scale; +}; + +/* range from -90 dB to 90 dB */ +static const struct mic_gain mic_gain_table[] = { +{ 1, 15}, { 1, 14}, /* -90, -84 dB */ +{ 3, 15}, { 1, 13}, { 3, 14}, { 1, 12}, /* -81, -78, -75, -72 dB */ +{ 5, 14}, { 13, 15}, /* -70, -68 dB */ +{ 9, 14}, { 21, 15}, { 23, 15}, { 13, 14}, /* -65 ~ -62 dB */ +{ 29, 15}, { 33, 15}, { 37, 15}, { 41, 15}, /* -61 ~ -58 dB */ +{ 23, 14}, { 13, 13}, { 58, 15}, { 65, 15}, /* -57 ~ -54 dB */ +{ 73, 15}, { 41, 14}, { 23, 13}, { 13, 12}, /* -53 ~ -50 dB */ +{ 29, 13}, { 65, 14}, { 73, 14}, { 41, 13}, /* -49 ~ -46 dB */ +{ 23, 12}, { 207, 15}, { 29, 12}, { 65, 13}, /* -45 ~ -42 dB */ +{ 73, 13}, { 41, 12}, { 23, 11}, { 413, 15}, /* -41 ~ -38 dB */ +{ 463, 15}, { 519, 15}, { 583, 15}, { 327, 14}, /* -37 ~ -34 dB */ +{ 367, 14}, { 823, 15}, { 231, 13}, { 1036, 15}, /* -33 ~ -30 dB */ +{ 1163, 15}, { 1305, 15}, { 183, 12}, { 1642, 15}, /* -29 ~ -26 dB */ +{ 1843, 15}, { 2068, 15}, { 145, 11}, { 2603, 15}, /* -25 ~ -22 dB */ +{ 365, 12}, { 3277, 15}, { 3677, 15}, { 4125, 15}, /* -21 ~ -18 dB */ +{ 4629, 15}, { 5193, 15}, { 5827, 15}, { 3269, 14}, /* -17 ~ -14 dB */ +{ 917, 12}, { 8231, 15}, { 9235, 15}, { 5181, 14}, /* -13 ~ -10 dB */ +{11627, 15}, {13045, 15}, {14637, 15}, {16423, 15}, /* -9 ~ -6 dB */ +{18427, 15}, {20675, 15}, { 5799, 13}, {26029, 15}, /* -5 ~ -2 dB */ +{ 7301, 13}, { 1, 0}, {18383, 14}, {10313, 13}, /* -1 ~ 2 dB */ +{23143, 14}, {25967, 14}, {29135, 14}, {16345, 13}, /* 3 ~ 6 dB */ +{ 4585, 11}, {20577, 13}, { 1443, 9}, {25905, 13}, /* 7 ~ 10 dB */ +{14533, 12}, { 8153, 11}, { 2287, 9}, {20529, 12}, /* 11 ~ 14 dB */ +{11517, 11}, { 6461, 10}, {28997, 12}, { 4067, 9}, /* 15 ~ 18 dB */ +{18253, 11}, { 10, 0}, {22979, 11}, {25783, 11}, /* 19 ~ 22 dB */ +{28929, 11}, {32459, 11}, { 9105, 9}, {20431, 10}, /* 23 ~ 26 dB */ +{22925, 10}, {12861, 9}, { 7215, 8}, {16191, 9}, /* 27 ~ 30 dB */ +{ 9083, 8}, {20383, 9}, {11435, 8}, { 6145, 7}, /* 31 ~ 34 dB */ +{ 3599, 6}, {32305, 9}, {18123, 8}, {20335, 8}, /* 35 ~ 38 dB */ +{ 713, 3}, { 100, 0}, { 7181, 6}, { 8057, 6}, /* 39 ~ 42 dB */ +{ 565, 2}, {20287, 7}, {11381, 6}, {25539, 7}, /* 43 ~ 46 dB */ +{ 1791, 3}, { 4019, 4}, { 9019, 5}, {20239, 6}, /* 47 ~ 50 dB */ +{ 5677, 4}, {25479, 6}, { 7147, 4}, { 8019, 4}, /* 51 ~ 54 dB */ +{17995, 5}, {20191, 5}, {11327, 4}, {12709, 4}, /* 55 ~ 58 dB */ +{ 3565, 2}, { 1000, 0}, { 1122, 0}, { 1259, 0}, /* 59 ~ 62 dB */ +{ 2825, 1}, {12679, 3}, { 7113, 2}, { 7981, 2}, /* 63 ~ 66 dB */ +{ 8955, 2}, {20095, 3}, {22547, 3}, {12649, 2}, /* 67 ~ 70 dB */ +{28385, 3}, { 3981, 0}, {17867, 2}, {20047, 2}, /* 71 ~ 74 dB */ +{11247, 1}, {12619, 1}, {14159, 1}, {31773, 2}, /* 75 ~ 78 dB */ +{17825, 1}, {10000, 0}, {11220, 0}, {12589, 0}, /* 79 ~ 82 dB */ +{28251, 1}, {15849, 0}, {17783, 0}, {19953, 0}, /* 83 ~ 86 dB */ +{22387, 0}, {25119, 0}, {28184, 0}, {31623, 0}, /* 87 ~ 90 dB */ +}; + +static const DECLARE_TLV_DB_RANGE(mic_gain_tlv, + 0, 1, TLV_DB_SCALE_ITEM(-9000, 600, 0), + 2, 5, TLV_DB_SCALE_ITEM(-8100, 300, 0), + 6, 7, TLV_DB_SCALE_ITEM(-7000, 200, 0), + 8, ARRAY_SIZE(mic_gain_table)-1, TLV_DB_SCALE_ITEM(-6500, 100, 0), +); + +int pdmic_get_mic_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + unsigned int dgain_val, scale_val; + int i; + + dgain_val = (snd_soc_read(codec, PDMIC_DSPR1) & PDMIC_DSPR1_DGAIN_MASK) + >> PDMIC_DSPR1_DGAIN_SHIFT; + + scale_val = (snd_soc_read(codec, PDMIC_DSPR0) & PDMIC_DSPR0_SCALE_MASK) + >> PDMIC_DSPR0_SCALE_SHIFT; + + for (i = 0; i < ARRAY_SIZE(mic_gain_table); i++) { + if ((mic_gain_table[i].dgain == dgain_val) && + (mic_gain_table[i].scale == scale_val)) + ucontrol->value.integer.value[0] = i; + } + + return 0; +} + +static int pdmic_put_mic_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int max = mc->max; + unsigned int val; + int ret; + + val = ucontrol->value.integer.value[0]; + + if (val > max) + return -EINVAL; + + ret = snd_soc_update_bits(codec, PDMIC_DSPR1, PDMIC_DSPR1_DGAIN_MASK, + mic_gain_table[val].dgain << PDMIC_DSPR1_DGAIN_SHIFT); + if (ret < 0) + return ret; + + ret = snd_soc_update_bits(codec, PDMIC_DSPR0, PDMIC_DSPR0_SCALE_MASK, + mic_gain_table[val].scale << PDMIC_DSPR0_SCALE_SHIFT); + if (ret < 0) + return ret; + + return 0; +} + +static const struct snd_kcontrol_new atmel_pdmic_snd_controls[] = { +SOC_SINGLE_EXT_TLV("Mic Capture Volume", PDMIC_DSPR1, PDMIC_DSPR1_DGAIN_SHIFT, + ARRAY_SIZE(mic_gain_table)-1, 0, + pdmic_get_mic_volsw, pdmic_put_mic_volsw, mic_gain_tlv), + +SOC_SINGLE("High Pass Filter Switch", PDMIC_DSPR0, + PDMIC_DSPR0_HPFBYP_SHIFT, 1, 1), + +SOC_SINGLE("SINCC Filter Switch", PDMIC_DSPR0, PDMIC_DSPR0_SINBYP_SHIFT, 1, 1), +}; + +static int atmel_pdmic_codec_probe(struct snd_soc_codec *codec) +{ + struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec); + struct atmel_pdmic *dd = snd_soc_card_get_drvdata(card); + + snd_soc_update_bits(codec, PDMIC_DSPR1, PDMIC_DSPR1_OFFSET_MASK, + (u32)(dd->pdata->mic_offset << PDMIC_DSPR1_OFFSET_SHIFT)); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_pdmic = { + .probe = atmel_pdmic_codec_probe, + .controls = atmel_pdmic_snd_controls, + .num_controls = ARRAY_SIZE(atmel_pdmic_snd_controls), +}; + +/* codec dai component */ +#define PDMIC_MR_PRESCAL_MAX_VAL 127 + +static int +atmel_pdmic_codec_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_codec *codec = codec_dai->codec; + unsigned int rate_min = substream->runtime->hw.rate_min; + unsigned int rate_max = substream->runtime->hw.rate_max; + int fs = params_rate(params); + int bits = params_width(params); + unsigned long pclk_rate, gclk_rate; + unsigned int f_pdmic; + u32 mr_val, dspr0_val, pclk_prescal, gclk_prescal; + + if (params_channels(params) != 1) { + dev_err(codec->dev, + "only supports one channel\n"); + return -EINVAL; + } + + if ((fs < rate_min) || (fs > rate_max)) { + dev_err(codec->dev, + "sample rate is %dHz, min rate is %dHz, max rate is %dHz\n", + fs, rate_min, rate_max); + + return -EINVAL; + } + + switch (bits) { + case 16: + dspr0_val = (PDMIC_DSPR0_SIZE_16_BITS + << PDMIC_DSPR0_SIZE_SHIFT); + break; + case 32: + dspr0_val = (PDMIC_DSPR0_SIZE_32_BITS + << PDMIC_DSPR0_SIZE_SHIFT); + break; + default: + return -EINVAL; + } + + if ((fs << 7) > (rate_max << 6)) { + f_pdmic = fs << 6; + dspr0_val |= PDMIC_DSPR0_OSR_64 << PDMIC_DSPR0_OSR_SHIFT; + } else { + f_pdmic = fs << 7; + dspr0_val |= PDMIC_DSPR0_OSR_128 << PDMIC_DSPR0_OSR_SHIFT; + } + + pclk_rate = clk_get_rate(dd->pclk); + gclk_rate = clk_get_rate(dd->gclk); + + /* PRESCAL = SELCK/(2*f_pdmic) - 1*/ + pclk_prescal = (u32)(pclk_rate/(f_pdmic << 1)) - 1; + gclk_prescal = (u32)(gclk_rate/(f_pdmic << 1)) - 1; + + if ((pclk_prescal > PDMIC_MR_PRESCAL_MAX_VAL) || + (gclk_rate/((gclk_prescal + 1) << 1) < + pclk_rate/((pclk_prescal + 1) << 1))) { + mr_val = gclk_prescal << PDMIC_MR_PRESCAL_SHIFT; + mr_val |= PDMIC_MR_CLKS_GCK << PDMIC_MR_CLKS_SHIFT; + } else { + mr_val = pclk_prescal << PDMIC_MR_PRESCAL_SHIFT; + mr_val |= PDMIC_MR_CLKS_PCK << PDMIC_MR_CLKS_SHIFT; + } + + snd_soc_update_bits(codec, PDMIC_MR, + PDMIC_MR_PRESCAL_MASK | PDMIC_MR_CLKS_MASK, mr_val); + + snd_soc_update_bits(codec, PDMIC_DSPR0, + PDMIC_DSPR0_OSR_MASK | PDMIC_DSPR0_SIZE_MASK, dspr0_val); + + return 0; +} + +static int atmel_pdmic_codec_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_codec *codec = codec_dai->codec; + + snd_soc_update_bits(codec, PDMIC_CR, PDMIC_CR_ENPDM_MASK, + PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT); + + return 0; +} + +static int atmel_pdmic_codec_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *codec_dai) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u32 val; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + val = PDMIC_CR_ENPDM_EN << PDMIC_CR_ENPDM_SHIFT; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + val = PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, PDMIC_CR, PDMIC_CR_ENPDM_MASK, val); + + return 0; +} + +static const struct snd_soc_dai_ops atmel_pdmic_codec_dai_ops = { + .hw_params = atmel_pdmic_codec_dai_hw_params, + .prepare = atmel_pdmic_codec_dai_prepare, + .trigger = atmel_pdmic_codec_dai_trigger, +}; + +#define ATMEL_PDMIC_CODEC_DAI_NAME "atmel-pdmic-hifi" + +static struct snd_soc_dai_driver atmel_pdmic_codec_dai = { + .name = ATMEL_PDMIC_CODEC_DAI_NAME, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = ATMEL_PDMIC_FORMATS, + }, + .ops = &atmel_pdmic_codec_dai_ops, +}; + +/* ASoC sound card */ +static int atmel_pdmic_asoc_card_init(struct device *dev, + struct snd_soc_card *card) +{ + struct snd_soc_dai_link *dai_link; + struct atmel_pdmic *dd = snd_soc_card_get_drvdata(card); + + dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL); + if (!dai_link) + return -ENOMEM; + + dai_link->name = "PDMIC"; + dai_link->stream_name = "PDMIC PCM"; + dai_link->codec_dai_name = ATMEL_PDMIC_CODEC_DAI_NAME; + dai_link->cpu_dai_name = dev_name(dev); + dai_link->codec_name = dev_name(dev); + dai_link->platform_name = dev_name(dev); + + card->dai_link = dai_link; + card->num_links = 1; + card->name = dd->pdata->card_name; + card->dev = dev; + + return 0; +} + +static void atmel_pdmic_get_sample_rate(struct atmel_pdmic *dd, + unsigned int *rate_min, unsigned int *rate_max) +{ + u32 mic_min_freq = dd->pdata->mic_min_freq; + u32 mic_max_freq = dd->pdata->mic_max_freq; + u32 clk_max_rate = (u32)(clk_get_rate(dd->pclk) >> 1); + u32 clk_min_rate = (u32)(clk_get_rate(dd->gclk) >> 8); + + if (mic_max_freq > clk_max_rate) + mic_max_freq = clk_max_rate; + + if (mic_min_freq < clk_min_rate) + mic_min_freq = clk_min_rate; + + *rate_min = DIV_ROUND_CLOSEST(mic_min_freq, 128); + *rate_max = mic_max_freq >> 6; +} + +/* PDMIC interrupt handler */ +static irqreturn_t atmel_pdmic_interrupt(int irq, void *dev_id) +{ + struct atmel_pdmic *dd = (struct atmel_pdmic *)dev_id; + u32 pdmic_isr; + irqreturn_t ret = IRQ_NONE; + + regmap_read(dd->regmap, PDMIC_ISR, &pdmic_isr); + + if (pdmic_isr & PDMIC_ISR_OVRE) { + regmap_update_bits(dd->regmap, PDMIC_CR, PDMIC_CR_ENPDM_MASK, + PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT); + + snd_pcm_stop_xrun(dd->substream); + + ret = IRQ_HANDLED; + } + + return ret; +} + +/* regmap configuration */ +#define ATMEL_PDMIC_REG_MAX 0x124 +static const struct regmap_config atmel_pdmic_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = ATMEL_PDMIC_REG_MAX, +}; + +static int atmel_pdmic_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct atmel_pdmic *dd; + struct resource *res; + void __iomem *io_base; + const struct atmel_pdmic_pdata *pdata; + struct snd_soc_card *card; + unsigned int rate_min, rate_max; + int ret; + + pdata = atmel_pdmic_dt_init(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + + dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL); + if (!dd) + return -ENOMEM; + + dd->pdata = pdata; + + dd->irq = platform_get_irq(pdev, 0); + if (dd->irq < 0) { + ret = dd->irq; + dev_err(dev, "failed to could not get irq: %d\n", ret); + return ret; + } + + dd->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(dd->pclk)) { + ret = PTR_ERR(dd->pclk); + dev_err(dev, "failed to get peripheral clock: %d\n", ret); + return ret; + } + + dd->gclk = devm_clk_get(dev, "gclk"); + if (IS_ERR(dd->gclk)) { + ret = PTR_ERR(dd->gclk); + dev_err(dev, "failed to get GCK: %d\n", ret); + return ret; + } + + /* The gclk clock frequency must always be tree times + * lower than the pclk clock frequency + */ + ret = clk_set_rate(dd->gclk, clk_get_rate(dd->pclk)/3); + if (ret) { + dev_err(dev, "failed to set GCK clock rate: %d\n", ret); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no memory resource\n"); + return -ENXIO; + } + + io_base = devm_ioremap_resource(dev, res); + if (IS_ERR(io_base)) { + ret = PTR_ERR(io_base); + dev_err(dev, "failed to remap register memory: %d\n", ret); + return ret; + } + + dd->phy_base = res->start; + + dd->regmap = devm_regmap_init_mmio(dev, io_base, + &atmel_pdmic_regmap_config); + if (IS_ERR(dd->regmap)) { + ret = PTR_ERR(dd->regmap); + dev_err(dev, "failed to init register map: %d\n", ret); + return ret; + } + + ret = devm_request_irq(dev, dd->irq, atmel_pdmic_interrupt, 0, + "PDMIC", (void *)dd); + if (ret < 0) { + dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", + dd->irq, ret); + return ret; + } + + /* Get the minimal and maximal sample rate that micphone supports */ + atmel_pdmic_get_sample_rate(dd, &rate_min, &rate_max); + + /* register cpu dai */ + atmel_pdmic_cpu_dai.capture.rate_min = rate_min; + atmel_pdmic_cpu_dai.capture.rate_max = rate_max; + ret = devm_snd_soc_register_component(dev, + &atmel_pdmic_cpu_dai_component, + &atmel_pdmic_cpu_dai, 1); + if (ret) { + dev_err(dev, "could not register CPU DAI: %d\n", ret); + return ret; + } + + /* register platform */ + ret = devm_snd_dmaengine_pcm_register(dev, + &atmel_pdmic_dmaengine_pcm_config, + 0); + if (ret) { + dev_err(dev, "could not register platform: %d\n", ret); + return ret; + } + + /* register codec and codec dai */ + atmel_pdmic_codec_dai.capture.rate_min = rate_min; + atmel_pdmic_codec_dai.capture.rate_max = rate_max; + ret = snd_soc_register_codec(dev, &soc_codec_dev_pdmic, + &atmel_pdmic_codec_dai, 1); + if (ret) { + dev_err(dev, "could not register codec: %d\n", ret); + return ret; + } + + /* register sound card */ + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) { + ret = -ENOMEM; + goto unregister_codec; + } + + snd_soc_card_set_drvdata(card, dd); + platform_set_drvdata(pdev, card); + + ret = atmel_pdmic_asoc_card_init(dev, card); + if (ret) { + dev_err(dev, "failed to init sound card: %d\n", ret); + goto unregister_codec; + } + + ret = devm_snd_soc_register_card(dev, card); + if (ret) { + dev_err(dev, "failed to register sound card: %d\n", ret); + goto unregister_codec; + } + + return 0; + +unregister_codec: + snd_soc_unregister_codec(dev); + return ret; +} + +static int atmel_pdmic_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver atmel_pdmic_driver = { + .driver = { + .name = "atmel-pdmic", + .of_match_table = of_match_ptr(atmel_pdmic_of_match), + .pm = &snd_soc_pm_ops, + }, + .probe = atmel_pdmic_probe, + .remove = atmel_pdmic_remove, +}; +module_platform_driver(atmel_pdmic_driver); + +MODULE_DESCRIPTION("Atmel PDMIC driver under ALSA SoC architecture"); +MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/atmel/atmel-pdmic.h b/sound/soc/atmel/atmel-pdmic.h new file mode 100644 index 000000000000..4527ac741919 --- /dev/null +++ b/sound/soc/atmel/atmel-pdmic.h @@ -0,0 +1,80 @@ +#ifndef __ATMEL_PDMIC_H_ +#define __ATMEL_PDMIC_H_ + +#include <linux/bitops.h> + +#define PDMIC_CR 0x00000000 + +#define PDMIC_CR_SWRST 0x1 +#define PDMIC_CR_SWRST_MASK BIT(0) +#define PDMIC_CR_SWRST_SHIFT (0) + +#define PDMIC_CR_ENPDM_DIS 0x0 +#define PDMIC_CR_ENPDM_EN 0x1 +#define PDMIC_CR_ENPDM_MASK BIT(4) +#define PDMIC_CR_ENPDM_SHIFT (4) + +#define PDMIC_MR 0x00000004 + +#define PDMIC_MR_CLKS_PCK 0x0 +#define PDMIC_MR_CLKS_GCK 0x1 +#define PDMIC_MR_CLKS_MASK BIT(4) +#define PDMIC_MR_CLKS_SHIFT (4) + +#define PDMIC_MR_PRESCAL_MASK GENMASK(14, 8) +#define PDMIC_MR_PRESCAL_SHIFT (8) + +#define PDMIC_CDR 0x00000014 + +#define PDMIC_IER 0x00000018 +#define PDMIC_IER_OVRE BIT(25) + +#define PDMIC_IDR 0x0000001c +#define PDMIC_IDR_OVRE BIT(25) + +#define PDMIC_IMR 0x00000020 + +#define PDMIC_ISR 0x00000024 +#define PDMIC_ISR_OVRE BIT(25) + +#define PDMIC_DSPR0 0x00000058 + +#define PDMIC_DSPR0_HPFBYP_DIS 0x1 +#define PDMIC_DSPR0_HPFBYP_EN 0x0 +#define PDMIC_DSPR0_HPFBYP_MASK BIT(1) +#define PDMIC_DSPR0_HPFBYP_SHIFT (1) + +#define PDMIC_DSPR0_SINBYP_DIS 0x1 +#define PDMIC_DSPR0_SINBYP_EN 0x0 +#define PDMIC_DSPR0_SINBYP_MASK BIT(2) +#define PDMIC_DSPR0_SINBYP_SHIFT (2) + +#define PDMIC_DSPR0_SIZE_16_BITS 0x0 +#define PDMIC_DSPR0_SIZE_32_BITS 0x1 +#define PDMIC_DSPR0_SIZE_MASK BIT(3) +#define PDMIC_DSPR0_SIZE_SHIFT (3) + +#define PDMIC_DSPR0_OSR_128 0x0 +#define PDMIC_DSPR0_OSR_64 0x1 +#define PDMIC_DSPR0_OSR_MASK GENMASK(6, 4) +#define PDMIC_DSPR0_OSR_SHIFT (4) + +#define PDMIC_DSPR0_SCALE_MASK GENMASK(11, 8) +#define PDMIC_DSPR0_SCALE_SHIFT (8) + +#define PDMIC_DSPR0_SHIFT_MASK GENMASK(15, 12) +#define PDMIC_DSPR0_SHIFT_SHIFT (12) + +#define PDMIC_DSPR1 0x0000005c + +#define PDMIC_DSPR1_DGAIN_MASK GENMASK(14, 0) +#define PDMIC_DSPR1_DGAIN_SHIFT (0) + +#define PDMIC_DSPR1_OFFSET_MASK GENMASK(31, 16) +#define PDMIC_DSPR1_OFFSET_SHIFT (16) + +#define PDMIC_WPMR 0x000000e4 + +#define PDMIC_WPSR 0x000000e8 + +#endif diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c index 1933bcd46cca..fdd28ed3e0b9 100644 --- a/sound/soc/atmel/atmel_wm8904.c +++ b/sound/soc/atmel/atmel_wm8904.c @@ -183,6 +183,7 @@ static struct platform_driver atmel_asoc_wm8904_driver = { .driver = { .name = "atmel-wm8904-audio", .of_match_table = of_match_ptr(atmel_asoc_wm8904_dt_ids), + .pm = &snd_soc_pm_ops, }, .probe = atmel_asoc_wm8904_probe, .remove = atmel_asoc_wm8904_remove, diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 89d789e3a2d0..784468e1cbad 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -55,9 +55,11 @@ config SND_SOC_ALL_CODECS select SND_SOC_CS4271_SPI if SPI_MASTER select SND_SOC_CS42XX8_I2C if I2C select SND_SOC_CS4349 if I2C + select SND_SOC_CS47L24 if MFD_CS47L24 select SND_SOC_CX20442 if TTY select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI select SND_SOC_DA7213 if I2C + select SND_SOC_DA7218 if I2C select SND_SOC_DA7219 if I2C select SND_SOC_DA732X if I2C select SND_SOC_DA9055 if I2C @@ -66,6 +68,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_ES8328_SPI if SPI_MASTER select SND_SOC_ES8328_I2C if I2C select SND_SOC_GTM601 + select SND_SOC_HDAC_HDMI select SND_SOC_ICS43432 select SND_SOC_INNO_RK3036 select SND_SOC_ISABELLE if I2C @@ -86,6 +89,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM1681 if I2C select SND_SOC_PCM1792A 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_PCM512x_I2C if I2C select SND_SOC_PCM512x_SPI if SPI_MASTER select SND_SOC_RT286 if I2C @@ -196,10 +201,12 @@ config SND_SOC_88PM860X config SND_SOC_ARIZONA tristate + default y if SND_SOC_CS47L24=y default y if SND_SOC_WM5102=y default y if SND_SOC_WM5110=y default y if SND_SOC_WM8997=y default y if SND_SOC_WM8998=y + default m if SND_SOC_CS47L24=m default m if SND_SOC_WM5102=m default m if SND_SOC_WM5110=m default m if SND_SOC_WM8997=m @@ -212,9 +219,12 @@ config SND_SOC_WM_HUBS config SND_SOC_WM_ADSP tristate + select SND_SOC_COMPRESS + default y if SND_SOC_CS47L24=y default y if SND_SOC_WM5102=y default y if SND_SOC_WM5110=y default y if SND_SOC_WM2200=y + default m if SND_SOC_CS47L24=m default m if SND_SOC_WM5102=m default m if SND_SOC_WM5110=m default m if SND_SOC_WM2200=m @@ -423,6 +433,9 @@ config SND_SOC_CS4349 tristate "Cirrus Logic CS4349 CODEC" depends on I2C +config SND_SOC_CS47L24 + tristate + config SND_SOC_CX20442 tristate depends on TTY @@ -440,6 +453,9 @@ config SND_SOC_DA7210 config SND_SOC_DA7213 tristate +config SND_SOC_DA7218 + tristate + config SND_SOC_DA7219 tristate @@ -469,6 +485,11 @@ config SND_SOC_ES8328_SPI config SND_SOC_GTM601 tristate 'GTM601 UMTS modem audio codec' +config SND_SOC_HDAC_HDMI + tristate + select SND_HDA_EXT_CORE + select HDMI + config SND_SOC_ICS43432 tristate @@ -510,6 +531,21 @@ config SND_SOC_PCM1792A config SND_SOC_PCM3008 tristate +config SND_SOC_PCM3168A + tristate + +config SND_SOC_PCM3168A_I2C + tristate "Texas Instruments PCM3168A CODEC - I2C" + depends on I2C + select SND_SOC_PCM3168A + select REGMAP_I2C + +config SND_SOC_PCM3168A_SPI + tristate "Texas Instruments PCM3168A CODEC - SPI" + depends on SPI_MASTER + select SND_SOC_PCM3168A + select REGMAP_SPI + config SND_SOC_PCM512x tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 2f6bc6c01178..44d8958bd032 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -47,9 +47,11 @@ snd-soc-cs4271-spi-objs := cs4271-spi.o snd-soc-cs42xx8-objs := cs42xx8.o snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o snd-soc-cs4349-objs := cs4349.o +snd-soc-cs47l24-objs := cs47l24.o snd-soc-cx20442-objs := cx20442.o snd-soc-da7210-objs := da7210.o snd-soc-da7213-objs := da7213.o +snd-soc-da7218-objs := da7218.o snd-soc-da7219-objs := da7219.o da7219-aad.o snd-soc-da732x-objs := da732x.o snd-soc-da9055-objs := da9055.o @@ -59,6 +61,7 @@ snd-soc-es8328-objs := es8328.o snd-soc-es8328-i2c-objs := es8328-i2c.o snd-soc-es8328-spi-objs := es8328-spi.o snd-soc-gtm601-objs := gtm601.o +snd-soc-hdac-hdmi-objs := hdac_hdmi.o snd-soc-ics43432-objs := ics43432.o snd-soc-inno-rk3036-objs := inno_rk3036.o snd-soc-isabelle-objs := isabelle.o @@ -79,6 +82,9 @@ snd-soc-nau8825-objs := nau8825.o snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm1792a-codec-objs := pcm1792a.o 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-pcm512x-objs := pcm512x.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o @@ -243,9 +249,11 @@ obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o +obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o +obj-$(CONFIG_SND_SOC_DA7218) += snd-soc-da7218.o obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o @@ -255,6 +263,7 @@ obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o +obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o @@ -275,6 +284,9 @@ obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o 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_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/ak4613.c b/sound/soc/codecs/ak4613.c index 07a266460ec3..647f69de6baa 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -70,18 +70,11 @@ #define FMT_MASK (0xf8) /* CTRL2 */ +#define DFS_MASK (3 << 2) #define DFS_NORMAL_SPEED (0 << 2) #define DFS_DOUBLE_SPEED (1 << 2) #define DFS_QUAD_SPEED (2 << 2) -struct ak4613_priv { - struct mutex lock; - - unsigned int fmt; - u8 fmt_ctrl; - int cnt; -}; - struct ak4613_formats { unsigned int width; unsigned int fmt; @@ -92,6 +85,16 @@ struct ak4613_interface { struct ak4613_formats playback; }; +struct ak4613_priv { + struct mutex lock; + const struct ak4613_interface *iface; + + unsigned int fmt; + u8 oc; + u8 ic; + int cnt; +}; + /* * Playback Volume * @@ -126,7 +129,7 @@ static const struct reg_default ak4613_reg[] = { { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 }, }; -#define AUDIO_IFACE_IDX_TO_VAL(i) (i << 3) +#define AUDIO_IFACE_TO_VAL(fmts) ((fmts - ak4613_iface) << 3) #define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt } static const struct ak4613_interface ak4613_iface[] = { /* capture */ /* playback */ @@ -240,7 +243,7 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, priv->cnt = 0; } if (!priv->cnt) - priv->fmt_ctrl = NO_FMT; + priv->iface = NULL; mutex_unlock(&priv->lock); } @@ -265,13 +268,35 @@ static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +static bool ak4613_dai_fmt_matching(const struct ak4613_interface *iface, + int is_play, + unsigned int fmt, unsigned int width) +{ + const struct ak4613_formats *fmts; + + fmts = (is_play) ? &iface->playback : &iface->capture; + + if (fmts->fmt != fmt) + return false; + + if (fmt == SND_SOC_DAIFMT_RIGHT_J) { + if (fmts->width != width) + return false; + } else { + if (fmts->width < width) + return false; + } + + return true; +} + static int ak4613_dai_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 ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); - const struct ak4613_formats *fmts; + const struct ak4613_interface *iface; struct device *dev = codec->dev; unsigned int width = params_width(params); unsigned int fmt = priv->fmt; @@ -305,33 +330,27 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, * It doesn't support TDM at this point */ fmt_ctrl = NO_FMT; - for (i = 0; i < ARRAY_SIZE(ak4613_iface); i++) { - fmts = (is_play) ? &ak4613_iface[i].playback : - &ak4613_iface[i].capture; - - if (fmts->fmt != fmt) - continue; + ret = -EINVAL; + iface = NULL; - if (fmt == SND_SOC_DAIFMT_RIGHT_J) { - if (fmts->width != width) - continue; - } else { - if (fmts->width < width) + mutex_lock(&priv->lock); + if (priv->iface) { + if (ak4613_dai_fmt_matching(priv->iface, is_play, fmt, width)) + iface = priv->iface; + } else { + for (i = ARRAY_SIZE(ak4613_iface); i >= 0; i--) { + if (!ak4613_dai_fmt_matching(ak4613_iface + i, + is_play, + fmt, width)) continue; + iface = ak4613_iface + i; + break; } - - fmt_ctrl = AUDIO_IFACE_IDX_TO_VAL(i); - break; } - ret = -EINVAL; - if (fmt_ctrl == NO_FMT) - goto hw_params_end; - - mutex_lock(&priv->lock); - if ((priv->fmt_ctrl == NO_FMT) || - (priv->fmt_ctrl == fmt_ctrl)) { - priv->fmt_ctrl = fmt_ctrl; + if ((priv->iface == NULL) || + (priv->iface == iface)) { + priv->iface = iface; priv->cnt++; ret = 0; } @@ -340,8 +359,13 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, if (ret < 0) goto hw_params_end; + fmt_ctrl = AUDIO_IFACE_TO_VAL(iface); + snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl); - snd_soc_write(codec, CTRL2, ctrl2); + snd_soc_update_bits(codec, CTRL2, DFS_MASK, ctrl2); + + snd_soc_write(codec, ICTRL, priv->ic); + snd_soc_write(codec, OCTRL, priv->oc); hw_params_end: if (ret < 0) @@ -431,6 +455,28 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4613 = { .num_dapm_routes = ARRAY_SIZE(ak4613_intercon), }; +static void ak4613_parse_of(struct ak4613_priv *priv, + struct device *dev) +{ + struct device_node *np = dev->of_node; + char prop[32]; + int i; + + /* Input 1 - 2 */ + for (i = 0; i < 2; i++) { + snprintf(prop, sizeof(prop), "asahi-kasei,in%d-single-end", i + 1); + if (!of_get_property(np, prop, NULL)) + priv->ic |= 1 << i; + } + + /* Output 1 - 6 */ + for (i = 0; i < 6; i++) { + snprintf(prop, sizeof(prop), "asahi-kasei,out%d-single-end", i + 1); + if (!of_get_property(np, prop, NULL)) + priv->oc |= 1 << i; + } +} + static int ak4613_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -458,7 +504,9 @@ static int ak4613_i2c_probe(struct i2c_client *i2c, if (!priv) return -ENOMEM; - priv->fmt_ctrl = NO_FMT; + ak4613_parse_of(priv, dev); + + priv->iface = NULL; priv->cnt = 0; mutex_init(&priv->lock); diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 9929efc6b9aa..38a73e3da508 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -310,7 +310,7 @@ int arizona_init_gpio(struct snd_soc_codec *codec) } EXPORT_SYMBOL_GPL(arizona_init_gpio); -const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { +const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "None", "Tone Generator 1", "Tone Generator 2", @@ -418,7 +418,7 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { }; EXPORT_SYMBOL_GPL(arizona_mixer_texts); -int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = { +unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = { 0x00, /* None */ 0x04, /* Tone */ 0x05, @@ -555,12 +555,12 @@ const char *arizona_sample_rate_val_to_name(unsigned int rate_val) } EXPORT_SYMBOL_GPL(arizona_sample_rate_val_to_name); -const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = { +const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = { "SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate", }; EXPORT_SYMBOL_GPL(arizona_rate_text); -const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = { +const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = { 0, 1, 2, 8, }; EXPORT_SYMBOL_GPL(arizona_rate_val); @@ -702,6 +702,100 @@ const struct soc_enum arizona_in_dmic_osr[] = { }; EXPORT_SYMBOL_GPL(arizona_in_dmic_osr); +static const char * const arizona_anc_input_src_text[] = { + "None", "IN1", "IN2", "IN3", "IN4", +}; + +static const char * const arizona_anc_channel_src_text[] = { + "None", "Left", "Right", "Combine", +}; + +const struct soc_enum arizona_anc_input_src[] = { + SOC_ENUM_SINGLE(ARIZONA_ANC_SRC, + ARIZONA_IN_RXANCL_SEL_SHIFT, + ARRAY_SIZE(arizona_anc_input_src_text), + arizona_anc_input_src_text), + SOC_ENUM_SINGLE(ARIZONA_FCL_ADC_REFORMATTER_CONTROL, + ARIZONA_FCL_MIC_MODE_SEL, + ARRAY_SIZE(arizona_anc_channel_src_text), + arizona_anc_channel_src_text), + SOC_ENUM_SINGLE(ARIZONA_ANC_SRC, + ARIZONA_IN_RXANCR_SEL_SHIFT, + ARRAY_SIZE(arizona_anc_input_src_text), + arizona_anc_input_src_text), + SOC_ENUM_SINGLE(ARIZONA_FCR_ADC_REFORMATTER_CONTROL, + ARIZONA_FCR_MIC_MODE_SEL, + ARRAY_SIZE(arizona_anc_channel_src_text), + arizona_anc_channel_src_text), +}; +EXPORT_SYMBOL_GPL(arizona_anc_input_src); + +static const char * const arizona_anc_ng_texts[] = { + "None", + "Internal", + "External", +}; + +SOC_ENUM_SINGLE_DECL(arizona_anc_ng_enum, SND_SOC_NOPM, 0, + arizona_anc_ng_texts); +EXPORT_SYMBOL_GPL(arizona_anc_ng_enum); + +static const char * const arizona_output_anc_src_text[] = { + "None", "RXANCL", "RXANCR", +}; + +const struct soc_enum arizona_output_anc_src[] = { + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L, + ARIZONA_OUT1L_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1R, + ARIZONA_OUT1R_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2L, + ARIZONA_OUT2L_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2R, + ARIZONA_OUT2R_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L, + ARIZONA_OUT3L_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_DAC_VOLUME_LIMIT_3R, + ARIZONA_OUT3R_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_4L, + ARIZONA_OUT4L_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_4R, + ARIZONA_OUT4R_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_5L, + ARIZONA_OUT5L_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_5R, + ARIZONA_OUT5R_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_6L, + ARIZONA_OUT6L_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_6R, + ARIZONA_OUT6R_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), +}; +EXPORT_SYMBOL_GPL(arizona_output_anc_src); + static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena) { struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); @@ -1023,24 +1117,43 @@ void arizona_init_dvfs(struct arizona_priv *priv) } EXPORT_SYMBOL_GPL(arizona_init_dvfs); -static unsigned int arizona_sysclk_48k_rates[] = { +int arizona_anc_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + 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) { + case SND_SOC_DAPM_POST_PMU: + val = 1 << w->shift; + break; + case SND_SOC_DAPM_PRE_PMD: + val = 1 << (w->shift + 1); + break; + default: + return 0; + } + + snd_soc_update_bits(codec, ARIZONA_CLOCK_CONTROL, mask, val); + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_anc_ev); + +static unsigned int arizona_opclk_ref_48k_rates[] = { 6144000, 12288000, 24576000, 49152000, - 73728000, - 98304000, - 147456000, }; -static unsigned int arizona_sysclk_44k1_rates[] = { +static unsigned int arizona_opclk_ref_44k1_rates[] = { 5644800, 11289600, 22579200, 45158400, - 67737600, - 90316800, - 135475200, }; static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk, @@ -1065,11 +1178,11 @@ static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk, } if (refclk % 8000) - rates = arizona_sysclk_44k1_rates; + rates = arizona_opclk_ref_44k1_rates; else - rates = arizona_sysclk_48k_rates; + rates = arizona_opclk_ref_48k_rates; - for (ref = 0; ref < ARRAY_SIZE(arizona_sysclk_48k_rates) && + for (ref = 0; ref < ARRAY_SIZE(arizona_opclk_ref_48k_rates) && rates[ref] <= refclk; ref++) { div = 1; while (rates[ref] / div >= freq && div < 32) { @@ -1101,7 +1214,7 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, unsigned int reg; unsigned int mask = ARIZONA_SYSCLK_FREQ_MASK | ARIZONA_SYSCLK_SRC_MASK; unsigned int val = source << ARIZONA_SYSCLK_SRC_SHIFT; - unsigned int *clk; + int *clk; switch (clk_id) { case ARIZONA_CLK_SYSCLK: @@ -1907,18 +2020,18 @@ static int arizona_calc_fratio(struct arizona_fll *fll, } switch (fll->arizona->type) { + case WM5102: + case WM8997: + return init_ratio; case WM5110: case WM8280: if (fll->arizona->rev < 3 || sync) return init_ratio; break; - case WM8998: - case WM1814: + default: if (sync) return init_ratio; break; - default: - return init_ratio; } cfg->fratio = init_ratio - 1; @@ -2099,9 +2212,9 @@ static int arizona_enable_fll(struct arizona_fll *fll) /* Facilitate smooth refclk across the transition */ regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9, ARIZONA_FLL1_GAIN_MASK, 0); - regmap_update_bits_async(fll->arizona->regmap, fll->base + 1, - ARIZONA_FLL1_FREERUN, - ARIZONA_FLL1_FREERUN); + regmap_update_bits(fll->arizona->regmap, fll->base + 1, + ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN); + udelay(32); } /* diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index fea8b8ae8e1a..8b6adb5419bb 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -57,7 +57,7 @@ #define ARIZONA_CLK_98MHZ 5 #define ARIZONA_CLK_147MHZ 6 -#define ARIZONA_MAX_DAI 6 +#define ARIZONA_MAX_DAI 8 #define ARIZONA_MAX_ADSP 4 #define ARIZONA_DVFS_SR1_RQ 0x001 @@ -96,8 +96,8 @@ struct arizona_priv { #define ARIZONA_NUM_MIXER_INPUTS 104 extern const unsigned int arizona_mixer_tlv[]; -extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS]; -extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; +extern const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS]; +extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; #define ARIZONA_GAINMUX_CONTROLS(name, base) \ SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \ @@ -216,8 +216,8 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; #define ARIZONA_RATE_ENUM_SIZE 4 #define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14 -extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE]; -extern const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE]; +extern const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE]; +extern const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE]; extern const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE]; extern const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE]; @@ -242,6 +242,10 @@ extern const struct soc_enum arizona_in_dmic_osr[]; extern const struct snd_kcontrol_new arizona_adsp2_rate_controls[]; +extern const struct soc_enum arizona_anc_input_src[]; +extern const struct soc_enum arizona_anc_ng_enum; +extern const struct soc_enum arizona_output_anc_src[]; + extern int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); @@ -251,6 +255,9 @@ extern int arizona_out_ev(struct snd_soc_dapm_widget *w, extern int arizona_hp_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); +extern int arizona_anc_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event); extern int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c new file mode 100644 index 000000000000..dc5ae7f7a1bd --- /dev/null +++ b/sound/soc/codecs/cs47l24.c @@ -0,0 +1,1148 @@ +/* + * cs47l24.h -- ALSA SoC Audio driver for Cirrus Logic CS47L24 + * + * Copyright 2015 Cirrus Logic Inc. + * + * Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.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. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include <linux/mfd/arizona/core.h> +#include <linux/mfd/arizona/registers.h> + +#include "arizona.h" +#include "wm_adsp.h" +#include "cs47l24.h" + +struct cs47l24_priv { + struct arizona_priv core; + struct arizona_fll fll[2]; +}; + +static const struct wm_adsp_region cs47l24_dsp2_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x200000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x280000 }, + { .type = WMFW_ADSP2_XM, .base = 0x290000 }, + { .type = WMFW_ADSP2_YM, .base = 0x2a8000 }, +}; + +static const struct wm_adsp_region cs47l24_dsp3_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x300000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x380000 }, + { .type = WMFW_ADSP2_XM, .base = 0x390000 }, + { .type = WMFW_ADSP2_YM, .base = 0x3a8000 }, +}; + +static const struct wm_adsp_region *cs47l24_dsp_regions[] = { + cs47l24_dsp2_regions, + cs47l24_dsp3_regions, +}; + +static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); +static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); + +#define CS47L24_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG SPKOUT Switch", base, 6, 1, 0) + +static const struct snd_kcontrol_new cs47l24_snd_controls[] = { +SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]), + +SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum), + +SOC_SINGLE("IN1L HPF Switch", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN1R HPF Switch", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2L HPF Switch", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2R HPF Switch", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_HPF_SHIFT, 1, 0), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), + +SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), +SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), + +ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), + +ARIZONA_EQ_CONTROL("EQ1 Coefficients", ARIZONA_EQ1_2), +SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_EQ_CONTROL("EQ2 Coefficients", ARIZONA_EQ2_2), +SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC2L", ARIZONA_DRC2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC2R", ARIZONA_DRC2RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5, + ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA), +SND_SOC_BYTES_MASK("DRC2", ARIZONA_DRC2_CTRL1, 5, + ARIZONA_DRC2R_ENA | ARIZONA_DRC2L_ENA), + +ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE), + +ARIZONA_LHPF_CONTROL("LHPF1 Coefficients", ARIZONA_HPLPF1_2), +ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2), +ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2), +ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2), + +SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), + +SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]), +SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]), +SOC_ENUM("ISRC3 FSL", arizona_isrc_fsl[2]), +SOC_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]), +SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]), +SOC_ENUM("ISRC3 FSH", arizona_isrc_fsh[2]), +SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1), + +ARIZONA_MIXER_CONTROLS("DSP2L", ARIZONA_DSP2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP2R", ARIZONA_DSP2RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP3L", ARIZONA_DSP3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP3R", ARIZONA_DSP3RMIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv), + +ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUT", ARIZONA_OUT4LMIX_INPUT_1_SOURCE), + +SOC_SINGLE("HPOUT1 SC Protect Switch", ARIZONA_HP1_SHORT_CIRCUIT_CTRL, + ARIZONA_HP1_SC_ENA_SHIFT, 1, 0), + +SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_OUT4L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_OUT4L_VOL_SHIFT, + 0xbf, 0, digital_tlv), + +SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), +SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), + +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +CS47L24_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), +CS47L24_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), +CS47L24_NG_SRC("SPKOUT", ARIZONA_NOISE_GATE_SELECT_4L), + +ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX3", ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX4", ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX5", ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), +}; + +ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC2L, ARIZONA_DRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC2R, ARIZONA_DRC2RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DSP2L, ARIZONA_DSP2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DSP2R, ARIZONA_DSP2RMIX_INPUT_1_SOURCE); +ARIZONA_DSP_AUX_ENUMS(DSP2, ARIZONA_DSP2AUX1MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DSP3L, ARIZONA_DSP3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DSP3R, ARIZONA_DSP3RMIX_INPUT_1_SOURCE); +ARIZONA_DSP_AUX_ENUMS(DSP3, ARIZONA_DSP3AUX1MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUT, ARIZONA_OUT4LMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX3, ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX4, ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX5, ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX6, ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT3, ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT4, ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC3, ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC4, ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT3, ARIZONA_ISRC2INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT4, ARIZONA_ISRC2INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC3, ARIZONA_ISRC2DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC4, ARIZONA_ISRC2DEC4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC3INT1, ARIZONA_ISRC3INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3INT2, ARIZONA_ISRC3INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3INT3, ARIZONA_ISRC3INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3INT4, ARIZONA_ISRC3INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC3DEC1, ARIZONA_ISRC3DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3DEC2, ARIZONA_ISRC3DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3DEC3, ARIZONA_ISRC3DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3DEC4, ARIZONA_ISRC3DEC4MIX_INPUT_1_SOURCE); + +static const char * const cs47l24_aec_loopback_texts[] = { + "HPOUT1L", "HPOUT1R", "SPKOUT", +}; + +static const unsigned int cs47l24_aec_loopback_values[] = { + 0, 1, 6, +}; + +static const struct soc_enum cs47l24_aec_loopback = + SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(cs47l24_aec_loopback_texts), + cs47l24_aec_loopback_texts, + cs47l24_aec_loopback_values); + +static const struct snd_kcontrol_new cs47l24_aec_loopback_mux = + SOC_DAPM_ENUM("AEC Loopback", cs47l24_aec_loopback); + +static const struct snd_soc_dapm_widget cs47l24_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, + ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, + ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, + ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_INPUT("IN1L"), +SND_SOC_DAPM_INPUT("IN1R"), +SND_SOC_DAPM_INPUT("IN2L"), +SND_SOC_DAPM_INPUT("IN2R"), + +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), +SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), + +SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2L", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2R", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, + NULL, 0), + +WM_ADSP2("DSP2", 1), +WM_ADSP2("DSP3", 2), + +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT3", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT4", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC3", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC4", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3INT1", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT2", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT3", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT4", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3DEC1", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC2", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC3", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC4", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, + &cs47l24_aec_loopback_mux), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"), +ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"), + +ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"), +ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"), +ARIZONA_MIXER_WIDGETS(DRC2L, "DRC2L"), +ARIZONA_MIXER_WIDGETS(DRC2R, "DRC2R"), + +ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"), +ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"), +ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"), +ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"), +ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"), + +ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"), +ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"), +ARIZONA_MIXER_WIDGETS(SPKOUT, "SPKOUT"), + +ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), +ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"), +ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"), + +ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), +ARIZONA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"), +ARIZONA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"), +ARIZONA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"), +ARIZONA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"), + +ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), + +ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"), +ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"), +ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"), +ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"), + +ARIZONA_DSP_WIDGETS(DSP2, "DSP2"), +ARIZONA_DSP_WIDGETS(DSP3, "DSP3"), + +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), +ARIZONA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"), +ARIZONA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), +ARIZONA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"), +ARIZONA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), +ARIZONA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"), +ARIZONA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), +ARIZONA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"), +ARIZONA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"), + +ARIZONA_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"), +ARIZONA_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"), +ARIZONA_MUX_WIDGETS(ISRC3DEC3, "ISRC3DEC3"), +ARIZONA_MUX_WIDGETS(ISRC3DEC4, "ISRC3DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"), +ARIZONA_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"), +ARIZONA_MUX_WIDGETS(ISRC3INT3, "ISRC3INT3"), +ARIZONA_MUX_WIDGETS(ISRC3INT4, "ISRC3INT4"), + +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("SPKOUTN"), +SND_SOC_DAPM_OUTPUT("SPKOUTP"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define ARIZONA_MIXER_INPUT_ROUTES(name) \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC", "AEC Loopback" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2L PGA" }, \ + { name, "IN2R", "IN2R PGA" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF1RX7", "AIF1RX7" }, \ + { name, "AIF1RX8", "AIF1RX8" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF2RX3", "AIF2RX3" }, \ + { name, "AIF2RX4", "AIF2RX4" }, \ + { name, "AIF2RX5", "AIF2RX5" }, \ + { name, "AIF2RX6", "AIF2RX6" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "DRC2L", "DRC2L" }, \ + { name, "DRC2R", "DRC2R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ASRC1L", "ASRC1L" }, \ + { name, "ASRC1R", "ASRC1R" }, \ + { name, "ASRC2L", "ASRC2L" }, \ + { name, "ASRC2R", "ASRC2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1DEC3", "ISRC1DEC3" }, \ + { name, "ISRC1DEC4", "ISRC1DEC4" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC1INT3", "ISRC1INT3" }, \ + { name, "ISRC1INT4", "ISRC1INT4" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2DEC3", "ISRC2DEC3" }, \ + { name, "ISRC2DEC4", "ISRC2DEC4" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ + { name, "ISRC2INT3", "ISRC2INT3" }, \ + { name, "ISRC2INT4", "ISRC2INT4" }, \ + { name, "ISRC3DEC1", "ISRC3DEC1" }, \ + { name, "ISRC3DEC2", "ISRC3DEC2" }, \ + { name, "ISRC3DEC3", "ISRC3DEC3" }, \ + { name, "ISRC3DEC4", "ISRC3DEC4" }, \ + { name, "ISRC3INT1", "ISRC3INT1" }, \ + { name, "ISRC3INT2", "ISRC3INT2" }, \ + { name, "ISRC3INT3", "ISRC3INT3" }, \ + { name, "ISRC3INT4", "ISRC3INT4" }, \ + { name, "DSP2.1", "DSP2" }, \ + { name, "DSP2.2", "DSP2" }, \ + { name, "DSP2.3", "DSP2" }, \ + { name, "DSP2.4", "DSP2" }, \ + { name, "DSP2.5", "DSP2" }, \ + { name, "DSP2.6", "DSP2" }, \ + { name, "DSP3.1", "DSP3" }, \ + { name, "DSP3.2", "DSP3" }, \ + { name, "DSP3.3", "DSP3" }, \ + { name, "DSP3.4", "DSP3" }, \ + { name, "DSP3.5", "DSP3" }, \ + { name, "DSP3.6", "DSP3" } + +static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = { + { "OUT1L", NULL, "CPVDD" }, + { "OUT1R", NULL, "CPVDD" }, + + { "OUT4L", NULL, "SPKVDD" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + + { "Noise Generator", NULL, "SYSCLK" }, + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Noise Generator", NULL, "NOISE" }, + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + { "AIF1 Capture", NULL, "AIF1TX7" }, + { "AIF1 Capture", NULL, "AIF1TX8" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + { "AIF1RX7", NULL, "AIF1 Playback" }, + { "AIF1RX8", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + { "AIF2 Capture", NULL, "AIF2TX3" }, + { "AIF2 Capture", NULL, "AIF2TX4" }, + { "AIF2 Capture", NULL, "AIF2TX5" }, + { "AIF2 Capture", NULL, "AIF2TX6" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + { "AIF2RX3", NULL, "AIF2 Playback" }, + { "AIF2RX4", NULL, "AIF2 Playback" }, + { "AIF2RX5", NULL, "AIF2 Playback" }, + { "AIF2RX6", NULL, "AIF2 Playback" }, + + { "AIF3 Capture", NULL, "AIF3TX1" }, + { "AIF3 Capture", NULL, "AIF3TX2" }, + + { "AIF3RX1", NULL, "AIF3 Playback" }, + { "AIF3RX2", NULL, "AIF3 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "AIF3 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "AIF3 Capture", NULL, "SYSCLK" }, + + { "IN1L PGA", NULL, "IN1L" }, + { "IN1R PGA", NULL, "IN1R" }, + + { "IN2L PGA", NULL, "IN2L" }, + { "IN2R PGA", NULL, "IN2R" }, + + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), + ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), + + ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUT"), + + ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"), + ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"), + + ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + ARIZONA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"), + ARIZONA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"), + ARIZONA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"), + ARIZONA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"), + + ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + + ARIZONA_MIXER_ROUTES("EQ1", "EQ1"), + ARIZONA_MIXER_ROUTES("EQ2", "EQ2"), + + ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"), + ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"), + ARIZONA_MIXER_ROUTES("DRC2L", "DRC2L"), + ARIZONA_MIXER_ROUTES("DRC2R", "DRC2R"), + + ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"), + ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"), + ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), + ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + + ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"), + ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"), + ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), + ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"), + + ARIZONA_DSP_ROUTES("DSP2"), + ARIZONA_DSP_ROUTES("DSP3"), + + ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + ARIZONA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"), + ARIZONA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + ARIZONA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"), + ARIZONA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"), + + ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + ARIZONA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"), + ARIZONA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + ARIZONA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"), + ARIZONA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"), + + ARIZONA_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"), + ARIZONA_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"), + ARIZONA_MUX_ROUTES("ISRC3INT3", "ISRC3INT3"), + ARIZONA_MUX_ROUTES("ISRC3INT4", "ISRC3INT4"), + + ARIZONA_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"), + ARIZONA_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"), + ARIZONA_MUX_ROUTES("ISRC3DEC3", "ISRC3DEC3"), + ARIZONA_MUX_ROUTES("ISRC3DEC4", "ISRC3DEC4"), + + { "AEC Loopback", "HPOUT1L", "OUT1L" }, + { "AEC Loopback", "HPOUT1R", "OUT1R" }, + { "HPOUT1L", NULL, "OUT1L" }, + { "HPOUT1R", NULL, "OUT1R" }, + + { "AEC Loopback", "SPKOUT", "OUT4L" }, + { "SPKOUTN", NULL, "OUT4L" }, + { "SPKOUTP", NULL, "OUT4L" }, + + { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "DRC1L" }, + { "DRC1 Signal Activity", NULL, "DRC1R" }, + { "DRC2 Signal Activity", NULL, "DRC2L" }, + { "DRC2 Signal Activity", NULL, "DRC2R" }, +}; + +static int cs47l24_set_fll(struct snd_soc_codec *codec, int fll_id, int source, + unsigned int Fref, unsigned int Fout) +{ + struct cs47l24_priv *cs47l24 = snd_soc_codec_get_drvdata(codec); + + switch (fll_id) { + case CS47L24_FLL1: + return arizona_set_fll(&cs47l24->fll[0], source, Fref, Fout); + case CS47L24_FLL2: + return arizona_set_fll(&cs47l24->fll[1], source, Fref, Fout); + case CS47L24_FLL1_REFCLK: + return arizona_set_fll_refclk(&cs47l24->fll[0], source, Fref, + Fout); + case CS47L24_FLL2_REFCLK: + return arizona_set_fll_refclk(&cs47l24->fll[1], source, Fref, + Fout); + default: + return -EINVAL; + } +} + +#define CS47L24_RATES SNDRV_PCM_RATE_8000_192000 + +#define CS47L24_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver cs47l24_dai[] = { + { + .name = "cs47l24-aif1", + .id = 1, + .base = ARIZONA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, + { + .name = "cs47l24-aif2", + .id = 2, + .base = ARIZONA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, + { + .name = "cs47l24-aif3", + .id = 3, + .base = ARIZONA_AIF3_BCLK_CTRL, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, +}; + +static int cs47l24_codec_probe(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec); + int ret; + + priv->core.arizona->dapm = dapm; + + arizona_init_spk(codec); + arizona_init_gpio(codec); + arizona_init_mono(codec); + + ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec); + if (ret) + goto err_adsp2_codec_probe; + + ret = wm_adsp2_codec_probe(&priv->core.adsp[2], codec); + if (ret) + goto err_adsp2_codec_probe; + + ret = snd_soc_add_codec_controls(codec, + &arizona_adsp2_rate_controls[1], 2); + if (ret) + goto err_adsp2_codec_probe; + + snd_soc_dapm_disable_pin(dapm, "HAPTICS"); + + return 0; + +err_adsp2_codec_probe: + wm_adsp2_codec_remove(&priv->core.adsp[1], codec); + wm_adsp2_codec_remove(&priv->core.adsp[2], codec); + + return ret; +} + +static int cs47l24_codec_remove(struct snd_soc_codec *codec) +{ + struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec); + + + wm_adsp2_codec_remove(&priv->core.adsp[1], codec); + wm_adsp2_codec_remove(&priv->core.adsp[2], codec); + + priv->core.arizona->dapm = NULL; + + return 0; +} + +#define CS47L24_DIG_VU 0x0200 + +static unsigned int cs47l24_digital_vu[] = { + ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, + ARIZONA_DAC_DIGITAL_VOLUME_4L, +}; + +static struct regmap *cs47l24_get_regmap(struct device *dev) +{ + struct cs47l24_priv *priv = dev_get_drvdata(dev); + + return priv->core.arizona->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_cs47l24 = { + .probe = cs47l24_codec_probe, + .remove = cs47l24_codec_remove, + .get_regmap = cs47l24_get_regmap, + + .idle_bias_off = true, + + .set_sysclk = arizona_set_sysclk, + .set_pll = cs47l24_set_fll, + + .controls = cs47l24_snd_controls, + .num_controls = ARRAY_SIZE(cs47l24_snd_controls), + .dapm_widgets = cs47l24_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs47l24_dapm_widgets), + .dapm_routes = cs47l24_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs47l24_dapm_routes), +}; + +static int cs47l24_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct cs47l24_priv *cs47l24; + int i, ret; + + BUILD_BUG_ON(ARRAY_SIZE(cs47l24_dai) > ARIZONA_MAX_DAI); + + cs47l24 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l24_priv), + GFP_KERNEL); + if (!cs47l24) + return -ENOMEM; + + platform_set_drvdata(pdev, cs47l24); + + cs47l24->core.arizona = arizona; + cs47l24->core.num_inputs = 4; + + for (i = 1; i <= 2; i++) { + cs47l24->core.adsp[i].part = "cs47l24"; + cs47l24->core.adsp[i].num = i + 1; + cs47l24->core.adsp[i].type = WMFW_ADSP2; + cs47l24->core.adsp[i].dev = arizona->dev; + cs47l24->core.adsp[i].regmap = arizona->regmap; + + cs47l24->core.adsp[i].base = ARIZONA_DSP1_CONTROL_1 + + (0x100 * i); + cs47l24->core.adsp[i].mem = cs47l24_dsp_regions[i - 1]; + cs47l24->core.adsp[i].num_mems = + ARRAY_SIZE(cs47l24_dsp2_regions); + + ret = wm_adsp2_init(&cs47l24->core.adsp[i]); + if (ret != 0) + return ret; + } + + for (i = 0; i < ARRAY_SIZE(cs47l24->fll); i++) + cs47l24->fll[i].vco_mult = 3; + + arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1, + ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK, + &cs47l24->fll[0]); + arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1, + ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK, + &cs47l24->fll[1]); + + /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */ + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2, + ARIZONA_SAMPLE_RATE_2_MASK, 0x11); + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3, + ARIZONA_SAMPLE_RATE_3_MASK, 0x12); + + for (i = 0; i < ARRAY_SIZE(cs47l24_dai); i++) + arizona_init_dai(&cs47l24->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(cs47l24_digital_vu); i++) + regmap_update_bits(arizona->regmap, cs47l24_digital_vu[i], + CS47L24_DIG_VU, CS47L24_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24, + cs47l24_dai, ARRAY_SIZE(cs47l24_dai)); +} + +static int cs47l24_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static struct platform_driver cs47l24_codec_driver = { + .driver = { + .name = "cs47l24-codec", + }, + .probe = cs47l24_probe, + .remove = cs47l24_remove, +}; + +module_platform_driver(cs47l24_codec_driver); + +MODULE_DESCRIPTION("ASoC CS47L24 driver"); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cs47l24-codec"); diff --git a/sound/soc/codecs/cs47l24.h b/sound/soc/codecs/cs47l24.h new file mode 100644 index 000000000000..77ab2b77b2e6 --- /dev/null +++ b/sound/soc/codecs/cs47l24.h @@ -0,0 +1,23 @@ +/* + * cs47l24.h -- ALSA SoC Audio driver for Cirrus Logic CS47L24 + * + * Copyright 2015 Cirrus Logic Inc. + * + * Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.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. + */ + +#ifndef _CS47L24_H +#define _CS47L24_H + +#include "arizona.h" + +#define CS47L24_FLL1 1 +#define CS47L24_FLL2 2 +#define CS47L24_FLL1_REFCLK 3 +#define CS47L24_FLL2_REFCLK 4 + +#endif diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c new file mode 100644 index 000000000000..72686517ff54 --- /dev/null +++ b/sound/soc/codecs/da7218.c @@ -0,0 +1,3314 @@ +/* + * da7218.c - DA7218 ALSA SoC Codec Driver + * + * Copyright (c) 2015 Dialog Semiconductor + * + * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/clk.h> +#include <linux/i2c.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/pm.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/regulator/consumer.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/jack.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <asm/div64.h> + +#include <sound/da7218.h> +#include "da7218.h" + + +/* + * TLVs and Enums + */ + +/* Input TLVs */ +static const DECLARE_TLV_DB_SCALE(da7218_mic_gain_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_mixin_gain_tlv, -450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7218_in_dig_gain_tlv, -8325, 75, 0); +static const DECLARE_TLV_DB_SCALE(da7218_ags_trigger_tlv, -9000, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_ags_att_max_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_alc_threshold_tlv, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7218_alc_gain_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_alc_ana_gain_tlv, 0, 600, 0); + +/* Input/Output TLVs */ +static const DECLARE_TLV_DB_SCALE(da7218_dmix_gain_tlv, -4200, 150, 0); + +/* Output TLVs */ +static const DECLARE_TLV_DB_SCALE(da7218_dgs_trigger_tlv, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7218_dgs_anticlip_tlv, -4200, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_dgs_signal_tlv, -9000, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_out_eq_band_tlv, -1050, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7218_out_dig_gain_tlv, -8325, 75, 0); +static const DECLARE_TLV_DB_SCALE(da7218_dac_ng_threshold_tlv, -10200, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_mixout_gain_tlv, -100, 50, 0); +static const DECLARE_TLV_DB_SCALE(da7218_hp_gain_tlv, -5700, 150, 0); + +/* Input Enums */ +static const char * const da7218_alc_attack_rate_txt[] = { + "7.33/fs", "14.66/fs", "29.32/fs", "58.64/fs", "117.3/fs", "234.6/fs", + "469.1/fs", "938.2/fs", "1876/fs", "3753/fs", "7506/fs", "15012/fs", + "30024/fs", +}; + +static const struct soc_enum da7218_alc_attack_rate = + SOC_ENUM_SINGLE(DA7218_ALC_CTRL2, DA7218_ALC_ATTACK_SHIFT, + DA7218_ALC_ATTACK_MAX, da7218_alc_attack_rate_txt); + +static const char * const da7218_alc_release_rate_txt[] = { + "28.66/fs", "57.33/fs", "114.6/fs", "229.3/fs", "458.6/fs", "917.1/fs", + "1834/fs", "3668/fs", "7337/fs", "14674/fs", "29348/fs", +}; + +static const struct soc_enum da7218_alc_release_rate = + SOC_ENUM_SINGLE(DA7218_ALC_CTRL2, DA7218_ALC_RELEASE_SHIFT, + DA7218_ALC_RELEASE_MAX, da7218_alc_release_rate_txt); + +static const char * const da7218_alc_hold_time_txt[] = { + "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs", + "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs", + "253952/fs", "507904/fs", "1015808/fs", "2031616/fs" +}; + +static const struct soc_enum da7218_alc_hold_time = + SOC_ENUM_SINGLE(DA7218_ALC_CTRL3, DA7218_ALC_HOLD_SHIFT, + DA7218_ALC_HOLD_MAX, da7218_alc_hold_time_txt); + +static const char * const da7218_alc_anticlip_step_txt[] = { + "0.034dB/fs", "0.068dB/fs", "0.136dB/fs", "0.272dB/fs", +}; + +static const struct soc_enum da7218_alc_anticlip_step = + SOC_ENUM_SINGLE(DA7218_ALC_ANTICLIP_CTRL, + DA7218_ALC_ANTICLIP_STEP_SHIFT, + DA7218_ALC_ANTICLIP_STEP_MAX, + da7218_alc_anticlip_step_txt); + +static const char * const da7218_integ_rate_txt[] = { + "1/4", "1/16", "1/256", "1/65536" +}; + +static const struct soc_enum da7218_integ_attack_rate = + SOC_ENUM_SINGLE(DA7218_ENV_TRACK_CTRL, DA7218_INTEG_ATTACK_SHIFT, + DA7218_INTEG_MAX, da7218_integ_rate_txt); + +static const struct soc_enum da7218_integ_release_rate = + SOC_ENUM_SINGLE(DA7218_ENV_TRACK_CTRL, DA7218_INTEG_RELEASE_SHIFT, + DA7218_INTEG_MAX, da7218_integ_rate_txt); + +/* Input/Output Enums */ +static const char * const da7218_gain_ramp_rate_txt[] = { + "Nominal Rate * 8", "Nominal Rate", "Nominal Rate / 8", + "Nominal Rate / 16", +}; + +static const struct soc_enum da7218_gain_ramp_rate = + SOC_ENUM_SINGLE(DA7218_GAIN_RAMP_CTRL, DA7218_GAIN_RAMP_RATE_SHIFT, + DA7218_GAIN_RAMP_RATE_MAX, da7218_gain_ramp_rate_txt); + +static const char * const da7218_hpf_mode_txt[] = { + "Disabled", "Audio", "Voice", +}; + +static const unsigned int da7218_hpf_mode_val[] = { + DA7218_HPF_DISABLED, DA7218_HPF_AUDIO_EN, DA7218_HPF_VOICE_EN, +}; + +static const struct soc_enum da7218_in1_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7218_IN_1_HPF_FILTER_CTRL, + DA7218_HPF_MODE_SHIFT, DA7218_HPF_MODE_MASK, + DA7218_HPF_MODE_MAX, da7218_hpf_mode_txt, + da7218_hpf_mode_val); + +static const struct soc_enum da7218_in2_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7218_IN_2_HPF_FILTER_CTRL, + DA7218_HPF_MODE_SHIFT, DA7218_HPF_MODE_MASK, + DA7218_HPF_MODE_MAX, da7218_hpf_mode_txt, + da7218_hpf_mode_val); + +static const struct soc_enum da7218_out1_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7218_OUT_1_HPF_FILTER_CTRL, + DA7218_HPF_MODE_SHIFT, DA7218_HPF_MODE_MASK, + DA7218_HPF_MODE_MAX, da7218_hpf_mode_txt, + da7218_hpf_mode_val); + +static const char * const da7218_audio_hpf_corner_txt[] = { + "2Hz", "4Hz", "8Hz", "16Hz", +}; + +static const struct soc_enum da7218_in1_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7218_IN_1_HPF_FILTER_CTRL, + DA7218_IN_1_AUDIO_HPF_CORNER_SHIFT, + DA7218_AUDIO_HPF_CORNER_MAX, + da7218_audio_hpf_corner_txt); + +static const struct soc_enum da7218_in2_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7218_IN_2_HPF_FILTER_CTRL, + DA7218_IN_2_AUDIO_HPF_CORNER_SHIFT, + DA7218_AUDIO_HPF_CORNER_MAX, + da7218_audio_hpf_corner_txt); + +static const struct soc_enum da7218_out1_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7218_OUT_1_HPF_FILTER_CTRL, + DA7218_OUT_1_AUDIO_HPF_CORNER_SHIFT, + DA7218_AUDIO_HPF_CORNER_MAX, + da7218_audio_hpf_corner_txt); + +static const char * const da7218_voice_hpf_corner_txt[] = { + "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz", +}; + +static const struct soc_enum da7218_in1_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7218_IN_1_HPF_FILTER_CTRL, + DA7218_IN_1_VOICE_HPF_CORNER_SHIFT, + DA7218_VOICE_HPF_CORNER_MAX, + da7218_voice_hpf_corner_txt); + +static const struct soc_enum da7218_in2_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7218_IN_2_HPF_FILTER_CTRL, + DA7218_IN_2_VOICE_HPF_CORNER_SHIFT, + DA7218_VOICE_HPF_CORNER_MAX, + da7218_voice_hpf_corner_txt); + +static const struct soc_enum da7218_out1_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7218_OUT_1_HPF_FILTER_CTRL, + DA7218_OUT_1_VOICE_HPF_CORNER_SHIFT, + DA7218_VOICE_HPF_CORNER_MAX, + da7218_voice_hpf_corner_txt); + +static const char * const da7218_tonegen_dtmf_key_txt[] = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", + "*", "#" +}; + +static const struct soc_enum da7218_tonegen_dtmf_key = + SOC_ENUM_SINGLE(DA7218_TONE_GEN_CFG1, DA7218_DTMF_REG_SHIFT, + DA7218_DTMF_REG_MAX, da7218_tonegen_dtmf_key_txt); + +static const char * const da7218_tonegen_swg_sel_txt[] = { + "Sum", "SWG1", "SWG2", "SWG1_1-Cos" +}; + +static const struct soc_enum da7218_tonegen_swg_sel = + SOC_ENUM_SINGLE(DA7218_TONE_GEN_CFG2, DA7218_SWG_SEL_SHIFT, + DA7218_SWG_SEL_MAX, da7218_tonegen_swg_sel_txt); + +/* Output Enums */ +static const char * const da7218_dgs_rise_coeff_txt[] = { + "1/1", "1/16", "1/64", "1/256", "1/1024", "1/4096", "1/16384", +}; + +static const struct soc_enum da7218_dgs_rise_coeff = + SOC_ENUM_SINGLE(DA7218_DGS_RISE_FALL, DA7218_DGS_RISE_COEFF_SHIFT, + DA7218_DGS_RISE_COEFF_MAX, da7218_dgs_rise_coeff_txt); + +static const char * const da7218_dgs_fall_coeff_txt[] = { + "1/4", "1/16", "1/64", "1/256", "1/1024", "1/4096", "1/16384", "1/65536", +}; + +static const struct soc_enum da7218_dgs_fall_coeff = + SOC_ENUM_SINGLE(DA7218_DGS_RISE_FALL, DA7218_DGS_FALL_COEFF_SHIFT, + DA7218_DGS_FALL_COEFF_MAX, da7218_dgs_fall_coeff_txt); + +static const char * const da7218_dac_ng_setup_time_txt[] = { + "256 Samples", "512 Samples", "1024 Samples", "2048 Samples" +}; + +static const struct soc_enum da7218_dac_ng_setup_time = + SOC_ENUM_SINGLE(DA7218_DAC_NG_SETUP_TIME, + DA7218_DAC_NG_SETUP_TIME_SHIFT, + DA7218_DAC_NG_SETUP_TIME_MAX, + da7218_dac_ng_setup_time_txt); + +static const char * const da7218_dac_ng_rampup_txt[] = { + "0.22ms/dB", "0.0138ms/dB" +}; + +static const struct soc_enum da7218_dac_ng_rampup_rate = + SOC_ENUM_SINGLE(DA7218_DAC_NG_SETUP_TIME, + DA7218_DAC_NG_RAMPUP_RATE_SHIFT, + DA7218_DAC_NG_RAMPUP_RATE_MAX, + da7218_dac_ng_rampup_txt); + +static const char * const da7218_dac_ng_rampdown_txt[] = { + "0.88ms/dB", "14.08ms/dB" +}; + +static const struct soc_enum da7218_dac_ng_rampdown_rate = + SOC_ENUM_SINGLE(DA7218_DAC_NG_SETUP_TIME, + DA7218_DAC_NG_RAMPDN_RATE_SHIFT, + DA7218_DAC_NG_RAMPDN_RATE_MAX, + da7218_dac_ng_rampdown_txt); + +static const char * const da7218_cp_mchange_txt[] = { + "Largest Volume", "DAC Volume", "Signal Magnitude" +}; + +static const unsigned int da7218_cp_mchange_val[] = { + DA7218_CP_MCHANGE_LARGEST_VOL, DA7218_CP_MCHANGE_DAC_VOL, + DA7218_CP_MCHANGE_SIG_MAG +}; + +static const struct soc_enum da7218_cp_mchange = + SOC_VALUE_ENUM_SINGLE(DA7218_CP_CTRL, DA7218_CP_MCHANGE_SHIFT, + DA7218_CP_MCHANGE_REL_MASK, DA7218_CP_MCHANGE_MAX, + da7218_cp_mchange_txt, da7218_cp_mchange_val); + +static const char * const da7218_cp_fcontrol_txt[] = { + "1MHz", "500KHz", "250KHz", "125KHz", "63KHz", "0KHz" +}; + +static const struct soc_enum da7218_cp_fcontrol = + SOC_ENUM_SINGLE(DA7218_CP_DELAY, DA7218_CP_FCONTROL_SHIFT, + DA7218_CP_FCONTROL_MAX, da7218_cp_fcontrol_txt); + +static const char * const da7218_cp_tau_delay_txt[] = { + "0ms", "2ms", "4ms", "16ms", "64ms", "128ms", "256ms", "512ms" +}; + +static const struct soc_enum da7218_cp_tau_delay = + SOC_ENUM_SINGLE(DA7218_CP_DELAY, DA7218_CP_TAU_DELAY_SHIFT, + DA7218_CP_TAU_DELAY_MAX, da7218_cp_tau_delay_txt); + +/* + * Control Functions + */ + +/* ALC */ +static void da7218_alc_calib(struct snd_soc_codec *codec) +{ + u8 mic_1_ctrl, mic_2_ctrl; + u8 mixin_1_ctrl, mixin_2_ctrl; + u8 in_1l_filt_ctrl, in_1r_filt_ctrl, in_2l_filt_ctrl, in_2r_filt_ctrl; + u8 in_1_hpf_ctrl, in_2_hpf_ctrl; + u8 calib_ctrl; + int i = 0; + bool calibrated = false; + + /* Save current state of MIC control registers */ + mic_1_ctrl = snd_soc_read(codec, DA7218_MIC_1_CTRL); + mic_2_ctrl = snd_soc_read(codec, DA7218_MIC_2_CTRL); + + /* Save current state of input mixer control registers */ + mixin_1_ctrl = snd_soc_read(codec, DA7218_MIXIN_1_CTRL); + mixin_2_ctrl = snd_soc_read(codec, DA7218_MIXIN_2_CTRL); + + /* Save current state of input filter control registers */ + in_1l_filt_ctrl = snd_soc_read(codec, DA7218_IN_1L_FILTER_CTRL); + in_1r_filt_ctrl = snd_soc_read(codec, DA7218_IN_1R_FILTER_CTRL); + in_2l_filt_ctrl = snd_soc_read(codec, DA7218_IN_2L_FILTER_CTRL); + in_2r_filt_ctrl = snd_soc_read(codec, DA7218_IN_2R_FILTER_CTRL); + + /* Save current state of input HPF control registers */ + in_1_hpf_ctrl = snd_soc_read(codec, DA7218_IN_1_HPF_FILTER_CTRL); + in_2_hpf_ctrl = snd_soc_read(codec, DA7218_IN_2_HPF_FILTER_CTRL); + + /* Enable then Mute MIC PGAs */ + snd_soc_update_bits(codec, DA7218_MIC_1_CTRL, DA7218_MIC_1_AMP_EN_MASK, + DA7218_MIC_1_AMP_EN_MASK); + snd_soc_update_bits(codec, DA7218_MIC_2_CTRL, DA7218_MIC_2_AMP_EN_MASK, + DA7218_MIC_2_AMP_EN_MASK); + snd_soc_update_bits(codec, DA7218_MIC_1_CTRL, + DA7218_MIC_1_AMP_MUTE_EN_MASK, + DA7218_MIC_1_AMP_MUTE_EN_MASK); + snd_soc_update_bits(codec, DA7218_MIC_2_CTRL, + DA7218_MIC_2_AMP_MUTE_EN_MASK, + DA7218_MIC_2_AMP_MUTE_EN_MASK); + + /* Enable input mixers unmuted */ + snd_soc_update_bits(codec, DA7218_MIXIN_1_CTRL, + DA7218_MIXIN_1_AMP_EN_MASK | + DA7218_MIXIN_1_AMP_MUTE_EN_MASK, + DA7218_MIXIN_1_AMP_EN_MASK); + snd_soc_update_bits(codec, DA7218_MIXIN_2_CTRL, + DA7218_MIXIN_2_AMP_EN_MASK | + DA7218_MIXIN_2_AMP_MUTE_EN_MASK, + DA7218_MIXIN_2_AMP_EN_MASK); + + /* Enable input filters unmuted */ + snd_soc_update_bits(codec, DA7218_IN_1L_FILTER_CTRL, + DA7218_IN_1L_FILTER_EN_MASK | + DA7218_IN_1L_MUTE_EN_MASK, + DA7218_IN_1L_FILTER_EN_MASK); + snd_soc_update_bits(codec, DA7218_IN_1R_FILTER_CTRL, + DA7218_IN_1R_FILTER_EN_MASK | + DA7218_IN_1R_MUTE_EN_MASK, + DA7218_IN_1R_FILTER_EN_MASK); + snd_soc_update_bits(codec, DA7218_IN_2L_FILTER_CTRL, + DA7218_IN_2L_FILTER_EN_MASK | + DA7218_IN_2L_MUTE_EN_MASK, + DA7218_IN_2L_FILTER_EN_MASK); + snd_soc_update_bits(codec, DA7218_IN_2R_FILTER_CTRL, + DA7218_IN_2R_FILTER_EN_MASK | + DA7218_IN_2R_MUTE_EN_MASK, + DA7218_IN_2R_FILTER_EN_MASK); + + /* + * Make sure input HPFs voice mode is disabled, otherwise for sampling + * rates above 32KHz the ADC signals will be stopped and will cause + * calibration to lock up. + */ + snd_soc_update_bits(codec, DA7218_IN_1_HPF_FILTER_CTRL, + DA7218_IN_1_VOICE_EN_MASK, 0); + snd_soc_update_bits(codec, DA7218_IN_2_HPF_FILTER_CTRL, + DA7218_IN_2_VOICE_EN_MASK, 0); + + /* Perform auto calibration */ + snd_soc_update_bits(codec, DA7218_CALIB_CTRL, DA7218_CALIB_AUTO_EN_MASK, + DA7218_CALIB_AUTO_EN_MASK); + do { + calib_ctrl = snd_soc_read(codec, DA7218_CALIB_CTRL); + if (calib_ctrl & DA7218_CALIB_AUTO_EN_MASK) { + ++i; + usleep_range(DA7218_ALC_CALIB_DELAY_MIN, + DA7218_ALC_CALIB_DELAY_MAX); + } else { + calibrated = true; + } + + } while ((i < DA7218_ALC_CALIB_MAX_TRIES) && (!calibrated)); + + /* If auto calibration fails, disable DC offset, hybrid ALC */ + if ((!calibrated) || (calib_ctrl & DA7218_CALIB_OVERFLOW_MASK)) { + dev_warn(codec->dev, + "ALC auto calibration failed - %s\n", + (calibrated) ? "overflow" : "timeout"); + snd_soc_update_bits(codec, DA7218_CALIB_CTRL, + DA7218_CALIB_OFFSET_EN_MASK, 0); + snd_soc_update_bits(codec, DA7218_ALC_CTRL1, + DA7218_ALC_SYNC_MODE_MASK, 0); + + } else { + /* Enable DC offset cancellation */ + snd_soc_update_bits(codec, DA7218_CALIB_CTRL, + DA7218_CALIB_OFFSET_EN_MASK, + DA7218_CALIB_OFFSET_EN_MASK); + + /* Enable ALC hybrid mode */ + snd_soc_update_bits(codec, DA7218_ALC_CTRL1, + DA7218_ALC_SYNC_MODE_MASK, + DA7218_ALC_SYNC_MODE_CH1 | + DA7218_ALC_SYNC_MODE_CH2); + } + + /* Restore input HPF control registers to original states */ + snd_soc_write(codec, DA7218_IN_1_HPF_FILTER_CTRL, in_1_hpf_ctrl); + snd_soc_write(codec, DA7218_IN_2_HPF_FILTER_CTRL, in_2_hpf_ctrl); + + /* Restore input filter control registers to original states */ + snd_soc_write(codec, DA7218_IN_1L_FILTER_CTRL, in_1l_filt_ctrl); + snd_soc_write(codec, DA7218_IN_1R_FILTER_CTRL, in_1r_filt_ctrl); + snd_soc_write(codec, DA7218_IN_2L_FILTER_CTRL, in_2l_filt_ctrl); + snd_soc_write(codec, DA7218_IN_2R_FILTER_CTRL, in_2r_filt_ctrl); + + /* Restore input mixer control registers to original state */ + snd_soc_write(codec, DA7218_MIXIN_1_CTRL, mixin_1_ctrl); + snd_soc_write(codec, DA7218_MIXIN_2_CTRL, mixin_2_ctrl); + + /* Restore MIC control registers to original states */ + snd_soc_write(codec, DA7218_MIC_1_CTRL, mic_1_ctrl); + snd_soc_write(codec, DA7218_MIC_2_CTRL, mic_2_ctrl); +} + +static int da7218_mixin_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + + /* + * If ALC in operation and value of control has been updated, + * make sure calibrated offsets are updated. + */ + if ((ret == 1) && (da7218->alc_en)) + da7218_alc_calib(codec); + + return ret; +} + +static int da7218_alc_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *) kcontrol->private_value; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + unsigned int lvalue = ucontrol->value.integer.value[0]; + unsigned int rvalue = ucontrol->value.integer.value[1]; + unsigned int lshift = mc->shift; + unsigned int rshift = mc->rshift; + unsigned int mask = (mc->max << lshift) | (mc->max << rshift); + + /* Force ALC offset calibration if enabling ALC */ + if ((lvalue || rvalue) && (!da7218->alc_en)) + da7218_alc_calib(codec); + + /* Update bits to detail which channels are enabled/disabled */ + da7218->alc_en &= ~mask; + da7218->alc_en |= (lvalue << lshift) | (rvalue << rshift); + + return snd_soc_put_volsw(kcontrol, ucontrol); +} + +/* ToneGen */ +static int da7218_tonegen_freq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int reg = mixer_ctrl->reg; + u16 val; + int ret; + + /* + * Frequency value spans two 8-bit registers, lower then upper byte. + * Therefore we need to convert to host endianness here. + */ + ret = regmap_raw_read(da7218->regmap, reg, &val, 2); + if (ret) + return ret; + + ucontrol->value.integer.value[0] = le16_to_cpu(val); + + return 0; +} + +static int da7218_tonegen_freq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int reg = mixer_ctrl->reg; + u16 val; + + /* + * Frequency value spans two 8-bit registers, lower then upper byte. + * Therefore we need to convert to little endian here to align with + * HW registers. + */ + val = cpu_to_le16(ucontrol->value.integer.value[0]); + + return regmap_raw_write(da7218->regmap, reg, &val, 2); +} + +static int da7218_mic_lvl_det_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int lvalue = ucontrol->value.integer.value[0]; + unsigned int rvalue = ucontrol->value.integer.value[1]; + unsigned int lshift = mixer_ctrl->shift; + unsigned int rshift = mixer_ctrl->rshift; + unsigned int mask = (mixer_ctrl->max << lshift) | + (mixer_ctrl->max << rshift); + da7218->mic_lvl_det_en &= ~mask; + da7218->mic_lvl_det_en |= (lvalue << lshift) | (rvalue << rshift); + + /* + * Here we only enable the feature on paths which are already + * powered. If a channel is enabled here for level detect, but that path + * isn't powered, then the channel will actually be enabled when we do + * power the path (IN_FILTER widget events). This handling avoids + * unwanted level detect events. + */ + return snd_soc_write(codec, mixer_ctrl->reg, + (da7218->in_filt_en & da7218->mic_lvl_det_en)); +} + +static int da7218_mic_lvl_det_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int lshift = mixer_ctrl->shift; + unsigned int rshift = mixer_ctrl->rshift; + unsigned int lmask = (mixer_ctrl->max << lshift); + unsigned int rmask = (mixer_ctrl->max << rshift); + + ucontrol->value.integer.value[0] = + (da7218->mic_lvl_det_en & lmask) >> lshift; + ucontrol->value.integer.value[1] = + (da7218->mic_lvl_det_en & rmask) >> rshift; + + return 0; +} + +static int da7218_biquad_coeff_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *) kcontrol->private_value; + + /* Determine which BiQuads we're setting based on size of config data */ + switch (bytes_ext->max) { + case DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE: + memcpy(ucontrol->value.bytes.data, da7218->biq_5stage_coeff, + bytes_ext->max); + break; + case DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE: + memcpy(ucontrol->value.bytes.data, da7218->stbiq_3stage_coeff, + bytes_ext->max); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int da7218_biquad_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *) kcontrol->private_value; + u8 reg, out_filt1l; + u8 cfg[DA7218_BIQ_CFG_SIZE]; + int i; + + /* + * Determine which BiQuads we're setting based on size of config data, + * and stored the data for use by get function. + */ + switch (bytes_ext->max) { + case DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE: + reg = DA7218_OUT_1_BIQ_5STAGE_DATA; + memcpy(da7218->biq_5stage_coeff, ucontrol->value.bytes.data, + bytes_ext->max); + break; + case DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE: + reg = DA7218_SIDETONE_BIQ_3STAGE_DATA; + memcpy(da7218->stbiq_3stage_coeff, ucontrol->value.bytes.data, + bytes_ext->max); + break; + default: + return -EINVAL; + } + + /* Make sure at least out filter1 enabled to allow programming */ + out_filt1l = snd_soc_read(codec, DA7218_OUT_1L_FILTER_CTRL); + snd_soc_write(codec, DA7218_OUT_1L_FILTER_CTRL, + out_filt1l | DA7218_OUT_1L_FILTER_EN_MASK); + + for (i = 0; i < bytes_ext->max; ++i) { + cfg[DA7218_BIQ_CFG_DATA] = ucontrol->value.bytes.data[i]; + cfg[DA7218_BIQ_CFG_ADDR] = i; + regmap_raw_write(da7218->regmap, reg, cfg, DA7218_BIQ_CFG_SIZE); + } + + /* Restore filter to previous setting */ + snd_soc_write(codec, DA7218_OUT_1L_FILTER_CTRL, out_filt1l); + + return 0; +} + + +/* + * KControls + */ + +static const struct snd_kcontrol_new da7218_snd_controls[] = { + /* Mics */ + SOC_SINGLE_TLV("Mic1 Volume", DA7218_MIC_1_GAIN, + DA7218_MIC_1_AMP_GAIN_SHIFT, DA7218_MIC_AMP_GAIN_MAX, + DA7218_NO_INVERT, da7218_mic_gain_tlv), + SOC_SINGLE("Mic1 Switch", DA7218_MIC_1_CTRL, + DA7218_MIC_1_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + SOC_SINGLE_TLV("Mic2 Volume", DA7218_MIC_2_GAIN, + DA7218_MIC_2_AMP_GAIN_SHIFT, DA7218_MIC_AMP_GAIN_MAX, + DA7218_NO_INVERT, da7218_mic_gain_tlv), + SOC_SINGLE("Mic2 Switch", DA7218_MIC_2_CTRL, + DA7218_MIC_2_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + + /* Mixer Input */ + SOC_SINGLE_EXT_TLV("Mixin1 Volume", DA7218_MIXIN_1_GAIN, + DA7218_MIXIN_1_AMP_GAIN_SHIFT, + DA7218_MIXIN_AMP_GAIN_MAX, DA7218_NO_INVERT, + snd_soc_get_volsw, da7218_mixin_gain_put, + da7218_mixin_gain_tlv), + SOC_SINGLE("Mixin1 Switch", DA7218_MIXIN_1_CTRL, + DA7218_MIXIN_1_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + SOC_SINGLE("Mixin1 Gain Ramp Switch", DA7218_MIXIN_1_CTRL, + DA7218_MIXIN_1_AMP_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("Mixin1 ZC Gain Switch", DA7218_MIXIN_1_CTRL, + DA7218_MIXIN_1_AMP_ZC_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE_EXT_TLV("Mixin2 Volume", DA7218_MIXIN_2_GAIN, + DA7218_MIXIN_2_AMP_GAIN_SHIFT, + DA7218_MIXIN_AMP_GAIN_MAX, DA7218_NO_INVERT, + snd_soc_get_volsw, da7218_mixin_gain_put, + da7218_mixin_gain_tlv), + SOC_SINGLE("Mixin2 Switch", DA7218_MIXIN_2_CTRL, + DA7218_MIXIN_2_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + SOC_SINGLE("Mixin2 Gain Ramp Switch", DA7218_MIXIN_2_CTRL, + DA7218_MIXIN_2_AMP_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("Mixin2 ZC Gain Switch", DA7218_MIXIN_2_CTRL, + DA7218_MIXIN_2_AMP_ZC_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + + /* ADCs */ + SOC_SINGLE("ADC1 AAF Switch", DA7218_ADC_1_CTRL, + DA7218_ADC_1_AAF_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("ADC2 AAF Switch", DA7218_ADC_2_CTRL, + DA7218_ADC_2_AAF_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("ADC LP Mode Switch", DA7218_ADC_MODE, + DA7218_ADC_LP_MODE_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + + /* Input Filters */ + SOC_SINGLE_TLV("In Filter1L Volume", DA7218_IN_1L_GAIN, + DA7218_IN_1L_DIGITAL_GAIN_SHIFT, + DA7218_IN_DIGITAL_GAIN_MAX, DA7218_NO_INVERT, + da7218_in_dig_gain_tlv), + SOC_SINGLE("In Filter1L Switch", DA7218_IN_1L_FILTER_CTRL, + DA7218_IN_1L_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + SOC_SINGLE("In Filter1L Gain Ramp Switch", DA7218_IN_1L_FILTER_CTRL, + DA7218_IN_1L_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE_TLV("In Filter1R Volume", DA7218_IN_1R_GAIN, + DA7218_IN_1R_DIGITAL_GAIN_SHIFT, + DA7218_IN_DIGITAL_GAIN_MAX, DA7218_NO_INVERT, + da7218_in_dig_gain_tlv), + SOC_SINGLE("In Filter1R Switch", DA7218_IN_1R_FILTER_CTRL, + DA7218_IN_1R_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + SOC_SINGLE("In Filter1R Gain Ramp Switch", + DA7218_IN_1R_FILTER_CTRL, DA7218_IN_1R_RAMP_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), + SOC_SINGLE_TLV("In Filter2L Volume", DA7218_IN_2L_GAIN, + DA7218_IN_2L_DIGITAL_GAIN_SHIFT, + DA7218_IN_DIGITAL_GAIN_MAX, DA7218_NO_INVERT, + da7218_in_dig_gain_tlv), + SOC_SINGLE("In Filter2L Switch", DA7218_IN_2L_FILTER_CTRL, + DA7218_IN_2L_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + SOC_SINGLE("In Filter2L Gain Ramp Switch", DA7218_IN_2L_FILTER_CTRL, + DA7218_IN_2L_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE_TLV("In Filter2R Volume", DA7218_IN_2R_GAIN, + DA7218_IN_2R_DIGITAL_GAIN_SHIFT, + DA7218_IN_DIGITAL_GAIN_MAX, DA7218_NO_INVERT, + da7218_in_dig_gain_tlv), + SOC_SINGLE("In Filter2R Switch", DA7218_IN_2R_FILTER_CTRL, + DA7218_IN_2R_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + SOC_SINGLE("In Filter2R Gain Ramp Switch", + DA7218_IN_2R_FILTER_CTRL, DA7218_IN_2R_RAMP_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), + + /* AGS */ + SOC_SINGLE_TLV("AGS Trigger", DA7218_AGS_TRIGGER, + DA7218_AGS_TRIGGER_SHIFT, DA7218_AGS_TRIGGER_MAX, + DA7218_INVERT, da7218_ags_trigger_tlv), + SOC_SINGLE_TLV("AGS Max Attenuation", DA7218_AGS_ATT_MAX, + DA7218_AGS_ATT_MAX_SHIFT, DA7218_AGS_ATT_MAX_MAX, + DA7218_NO_INVERT, da7218_ags_att_max_tlv), + SOC_SINGLE("AGS Anticlip Switch", DA7218_AGS_ANTICLIP_CTRL, + DA7218_AGS_ANTICLIP_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("AGS Channel1 Switch", DA7218_AGS_ENABLE, + DA7218_AGS_ENABLE_CHAN1_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("AGS Channel2 Switch", DA7218_AGS_ENABLE, + DA7218_AGS_ENABLE_CHAN2_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + + /* ALC */ + SOC_ENUM("ALC Attack Rate", da7218_alc_attack_rate), + SOC_ENUM("ALC Release Rate", da7218_alc_release_rate), + SOC_ENUM("ALC Hold Time", da7218_alc_hold_time), + SOC_SINGLE_TLV("ALC Noise Threshold", DA7218_ALC_NOISE, + DA7218_ALC_NOISE_SHIFT, DA7218_ALC_THRESHOLD_MAX, + DA7218_INVERT, da7218_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Min Threshold", DA7218_ALC_TARGET_MIN, + DA7218_ALC_THRESHOLD_MIN_SHIFT, DA7218_ALC_THRESHOLD_MAX, + DA7218_INVERT, da7218_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Threshold", DA7218_ALC_TARGET_MAX, + DA7218_ALC_THRESHOLD_MAX_SHIFT, DA7218_ALC_THRESHOLD_MAX, + DA7218_INVERT, da7218_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Attenuation", DA7218_ALC_GAIN_LIMITS, + DA7218_ALC_ATTEN_MAX_SHIFT, DA7218_ALC_ATTEN_GAIN_MAX, + DA7218_NO_INVERT, da7218_alc_gain_tlv), + SOC_SINGLE_TLV("ALC Max Gain", DA7218_ALC_GAIN_LIMITS, + DA7218_ALC_GAIN_MAX_SHIFT, DA7218_ALC_ATTEN_GAIN_MAX, + DA7218_NO_INVERT, da7218_alc_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC Min Analog Gain", DA7218_ALC_ANA_GAIN_LIMITS, + DA7218_ALC_ANA_GAIN_MIN_SHIFT, + DA7218_ALC_ANA_GAIN_MIN, DA7218_ALC_ANA_GAIN_MAX, + DA7218_NO_INVERT, da7218_alc_ana_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC Max Analog Gain", DA7218_ALC_ANA_GAIN_LIMITS, + DA7218_ALC_ANA_GAIN_MAX_SHIFT, + DA7218_ALC_ANA_GAIN_MIN, DA7218_ALC_ANA_GAIN_MAX, + DA7218_NO_INVERT, da7218_alc_ana_gain_tlv), + SOC_ENUM("ALC Anticlip Step", da7218_alc_anticlip_step), + SOC_SINGLE("ALC Anticlip Switch", DA7218_ALC_ANTICLIP_CTRL, + DA7218_ALC_ANTICLIP_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_DOUBLE_EXT("ALC Channel1 Switch", DA7218_ALC_CTRL1, + DA7218_ALC_CHAN1_L_EN_SHIFT, DA7218_ALC_CHAN1_R_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT, + snd_soc_get_volsw, da7218_alc_sw_put), + SOC_DOUBLE_EXT("ALC Channel2 Switch", DA7218_ALC_CTRL1, + DA7218_ALC_CHAN2_L_EN_SHIFT, DA7218_ALC_CHAN2_R_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT, + snd_soc_get_volsw, da7218_alc_sw_put), + + /* Envelope Tracking */ + SOC_ENUM("Envelope Tracking Attack Rate", da7218_integ_attack_rate), + SOC_ENUM("Envelope Tracking Release Rate", da7218_integ_release_rate), + + /* Input High-Pass Filters */ + SOC_ENUM("In Filter1 HPF Mode", da7218_in1_hpf_mode), + SOC_ENUM("In Filter1 HPF Corner Audio", da7218_in1_audio_hpf_corner), + SOC_ENUM("In Filter1 HPF Corner Voice", da7218_in1_voice_hpf_corner), + SOC_ENUM("In Filter2 HPF Mode", da7218_in2_hpf_mode), + SOC_ENUM("In Filter2 HPF Corner Audio", da7218_in2_audio_hpf_corner), + SOC_ENUM("In Filter2 HPF Corner Voice", da7218_in2_voice_hpf_corner), + + /* Mic Level Detect */ + SOC_DOUBLE_EXT("Mic Level Detect Channel1 Switch", DA7218_LVL_DET_CTRL, + DA7218_LVL_DET_EN_CHAN1L_SHIFT, + DA7218_LVL_DET_EN_CHAN1R_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT, da7218_mic_lvl_det_sw_get, + da7218_mic_lvl_det_sw_put), + SOC_DOUBLE_EXT("Mic Level Detect Channel2 Switch", DA7218_LVL_DET_CTRL, + DA7218_LVL_DET_EN_CHAN2L_SHIFT, + DA7218_LVL_DET_EN_CHAN2R_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT, da7218_mic_lvl_det_sw_get, + da7218_mic_lvl_det_sw_put), + SOC_SINGLE("Mic Level Detect Level", DA7218_LVL_DET_LEVEL, + DA7218_LVL_DET_LEVEL_SHIFT, DA7218_LVL_DET_LEVEL_MAX, + DA7218_NO_INVERT), + + /* Digital Mixer (Input) */ + SOC_SINGLE_TLV("DMix In Filter1L Out1 DAIL Volume", + DA7218_DMIX_OUTDAI_1L_INFILT_1L_GAIN, + DA7218_OUTDAI_1L_INFILT_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter1L Out1 DAIR Volume", + DA7218_DMIX_OUTDAI_1R_INFILT_1L_GAIN, + DA7218_OUTDAI_1R_INFILT_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter1L Out2 DAIL Volume", + DA7218_DMIX_OUTDAI_2L_INFILT_1L_GAIN, + DA7218_OUTDAI_2L_INFILT_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter1L Out2 DAIR Volume", + DA7218_DMIX_OUTDAI_2R_INFILT_1L_GAIN, + DA7218_OUTDAI_2R_INFILT_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In Filter1R Out1 DAIL Volume", + DA7218_DMIX_OUTDAI_1L_INFILT_1R_GAIN, + DA7218_OUTDAI_1L_INFILT_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter1R Out1 DAIR Volume", + DA7218_DMIX_OUTDAI_1R_INFILT_1R_GAIN, + DA7218_OUTDAI_1R_INFILT_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter1R Out2 DAIL Volume", + DA7218_DMIX_OUTDAI_2L_INFILT_1R_GAIN, + DA7218_OUTDAI_2L_INFILT_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter1R Out2 DAIR Volume", + DA7218_DMIX_OUTDAI_2R_INFILT_1R_GAIN, + DA7218_OUTDAI_2R_INFILT_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In Filter2L Out1 DAIL Volume", + DA7218_DMIX_OUTDAI_1L_INFILT_2L_GAIN, + DA7218_OUTDAI_1L_INFILT_2L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter2L Out1 DAIR Volume", + DA7218_DMIX_OUTDAI_1R_INFILT_2L_GAIN, + DA7218_OUTDAI_1R_INFILT_2L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter2L Out2 DAIL Volume", + DA7218_DMIX_OUTDAI_2L_INFILT_2L_GAIN, + DA7218_OUTDAI_2L_INFILT_2L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter2L Out2 DAIR Volume", + DA7218_DMIX_OUTDAI_2R_INFILT_2L_GAIN, + DA7218_OUTDAI_2R_INFILT_2L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In Filter2R Out1 DAIL Volume", + DA7218_DMIX_OUTDAI_1L_INFILT_2R_GAIN, + DA7218_OUTDAI_1L_INFILT_2R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter2R Out1 DAIR Volume", + DA7218_DMIX_OUTDAI_1R_INFILT_2R_GAIN, + DA7218_OUTDAI_1R_INFILT_2R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter2R Out2 DAIL Volume", + DA7218_DMIX_OUTDAI_2L_INFILT_2R_GAIN, + DA7218_OUTDAI_2L_INFILT_2R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter2R Out2 DAIR Volume", + DA7218_DMIX_OUTDAI_2R_INFILT_2R_GAIN, + DA7218_OUTDAI_2R_INFILT_2R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix ToneGen Out1 DAIL Volume", + DA7218_DMIX_OUTDAI_1L_TONEGEN_GAIN, + DA7218_OUTDAI_1L_TONEGEN_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix ToneGen Out1 DAIR Volume", + DA7218_DMIX_OUTDAI_1R_TONEGEN_GAIN, + DA7218_OUTDAI_1R_TONEGEN_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix ToneGen Out2 DAIL Volume", + DA7218_DMIX_OUTDAI_2L_TONEGEN_GAIN, + DA7218_OUTDAI_2L_TONEGEN_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix ToneGen Out2 DAIR Volume", + DA7218_DMIX_OUTDAI_2R_TONEGEN_GAIN, + DA7218_OUTDAI_2R_TONEGEN_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In DAIL Out1 DAIL Volume", + DA7218_DMIX_OUTDAI_1L_INDAI_1L_GAIN, + DA7218_OUTDAI_1L_INDAI_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In DAIL Out1 DAIR Volume", + DA7218_DMIX_OUTDAI_1R_INDAI_1L_GAIN, + DA7218_OUTDAI_1R_INDAI_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In DAIL Out2 DAIL Volume", + DA7218_DMIX_OUTDAI_2L_INDAI_1L_GAIN, + DA7218_OUTDAI_2L_INDAI_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In DAIL Out2 DAIR Volume", + DA7218_DMIX_OUTDAI_2R_INDAI_1L_GAIN, + DA7218_OUTDAI_2R_INDAI_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In DAIR Out1 DAIL Volume", + DA7218_DMIX_OUTDAI_1L_INDAI_1R_GAIN, + DA7218_OUTDAI_1L_INDAI_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In DAIR Out1 DAIR Volume", + DA7218_DMIX_OUTDAI_1R_INDAI_1R_GAIN, + DA7218_OUTDAI_1R_INDAI_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In DAIR Out2 DAIL Volume", + DA7218_DMIX_OUTDAI_2L_INDAI_1R_GAIN, + DA7218_OUTDAI_2L_INDAI_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In DAIR Out2 DAIR Volume", + DA7218_DMIX_OUTDAI_2R_INDAI_1R_GAIN, + DA7218_OUTDAI_2R_INDAI_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + /* Digital Mixer (Output) */ + SOC_SINGLE_TLV("DMix In Filter1L Out FilterL Volume", + DA7218_DMIX_OUTFILT_1L_INFILT_1L_GAIN, + DA7218_OUTFILT_1L_INFILT_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter1L Out FilterR Volume", + DA7218_DMIX_OUTFILT_1R_INFILT_1L_GAIN, + DA7218_OUTFILT_1R_INFILT_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In Filter1R Out FilterL Volume", + DA7218_DMIX_OUTFILT_1L_INFILT_1R_GAIN, + DA7218_OUTFILT_1L_INFILT_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter1R Out FilterR Volume", + DA7218_DMIX_OUTFILT_1R_INFILT_1R_GAIN, + DA7218_OUTFILT_1R_INFILT_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In Filter2L Out FilterL Volume", + DA7218_DMIX_OUTFILT_1L_INFILT_2L_GAIN, + DA7218_OUTFILT_1L_INFILT_2L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter2L Out FilterR Volume", + DA7218_DMIX_OUTFILT_1R_INFILT_2L_GAIN, + DA7218_OUTFILT_1R_INFILT_2L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In Filter2R Out FilterL Volume", + DA7218_DMIX_OUTFILT_1L_INFILT_2R_GAIN, + DA7218_OUTFILT_1L_INFILT_2R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter2R Out FilterR Volume", + DA7218_DMIX_OUTFILT_1R_INFILT_2R_GAIN, + DA7218_OUTFILT_1R_INFILT_2R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix ToneGen Out FilterL Volume", + DA7218_DMIX_OUTFILT_1L_TONEGEN_GAIN, + DA7218_OUTFILT_1L_TONEGEN_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix ToneGen Out FilterR Volume", + DA7218_DMIX_OUTFILT_1R_TONEGEN_GAIN, + DA7218_OUTFILT_1R_TONEGEN_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In DAIL Out FilterL Volume", + DA7218_DMIX_OUTFILT_1L_INDAI_1L_GAIN, + DA7218_OUTFILT_1L_INDAI_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In DAIL Out FilterR Volume", + DA7218_DMIX_OUTFILT_1R_INDAI_1L_GAIN, + DA7218_OUTFILT_1R_INDAI_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In DAIR Out FilterL Volume", + DA7218_DMIX_OUTFILT_1L_INDAI_1R_GAIN, + DA7218_OUTFILT_1L_INDAI_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In DAIR Out FilterR Volume", + DA7218_DMIX_OUTFILT_1R_INDAI_1R_GAIN, + DA7218_OUTFILT_1R_INDAI_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + /* Sidetone Filter */ + SND_SOC_BYTES_EXT("Sidetone BiQuad Coefficients", + DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE, + da7218_biquad_coeff_get, da7218_biquad_coeff_put), + SOC_SINGLE_TLV("Sidetone Volume", DA7218_SIDETONE_GAIN, + DA7218_SIDETONE_GAIN_SHIFT, DA7218_DMIX_GAIN_MAX, + DA7218_NO_INVERT, da7218_dmix_gain_tlv), + SOC_SINGLE("Sidetone Switch", DA7218_SIDETONE_CTRL, + DA7218_SIDETONE_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + + /* Tone Generator */ + SOC_ENUM("ToneGen DTMF Key", da7218_tonegen_dtmf_key), + SOC_SINGLE("ToneGen DTMF Switch", DA7218_TONE_GEN_CFG1, + DA7218_DTMF_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_ENUM("ToneGen Sinewave Gen Type", da7218_tonegen_swg_sel), + SOC_SINGLE_EXT("ToneGen Sinewave1 Freq", DA7218_TONE_GEN_FREQ1_L, + DA7218_FREQ1_L_SHIFT, DA7218_FREQ_MAX, DA7218_NO_INVERT, + da7218_tonegen_freq_get, da7218_tonegen_freq_put), + SOC_SINGLE_EXT("ToneGen Sinewave2 Freq", DA7218_TONE_GEN_FREQ2_L, + DA7218_FREQ2_L_SHIFT, DA7218_FREQ_MAX, DA7218_NO_INVERT, + da7218_tonegen_freq_get, da7218_tonegen_freq_put), + SOC_SINGLE("ToneGen On Time", DA7218_TONE_GEN_ON_PER, + DA7218_BEEP_ON_PER_SHIFT, DA7218_BEEP_ON_OFF_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("ToneGen Off Time", DA7218_TONE_GEN_OFF_PER, + DA7218_BEEP_OFF_PER_SHIFT, DA7218_BEEP_ON_OFF_MAX, + DA7218_NO_INVERT), + + /* Gain ramping */ + SOC_ENUM("Gain Ramp Rate", da7218_gain_ramp_rate), + + /* DGS */ + SOC_SINGLE_TLV("DGS Trigger", DA7218_DGS_TRIGGER, + DA7218_DGS_TRIGGER_LVL_SHIFT, DA7218_DGS_TRIGGER_MAX, + DA7218_INVERT, da7218_dgs_trigger_tlv), + SOC_ENUM("DGS Rise Coefficient", da7218_dgs_rise_coeff), + SOC_ENUM("DGS Fall Coefficient", da7218_dgs_fall_coeff), + SOC_SINGLE("DGS Sync Delay", DA7218_DGS_SYNC_DELAY, + DA7218_DGS_SYNC_DELAY_SHIFT, DA7218_DGS_SYNC_DELAY_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("DGS Fast SR Sync Delay", DA7218_DGS_SYNC_DELAY2, + DA7218_DGS_SYNC_DELAY2_SHIFT, DA7218_DGS_SYNC_DELAY_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("DGS Voice Filter Sync Delay", DA7218_DGS_SYNC_DELAY3, + DA7218_DGS_SYNC_DELAY3_SHIFT, DA7218_DGS_SYNC_DELAY3_MAX, + DA7218_NO_INVERT), + SOC_SINGLE_TLV("DGS Anticlip Level", DA7218_DGS_LEVELS, + DA7218_DGS_ANTICLIP_LVL_SHIFT, + DA7218_DGS_ANTICLIP_LVL_MAX, DA7218_INVERT, + da7218_dgs_anticlip_tlv), + SOC_SINGLE_TLV("DGS Signal Level", DA7218_DGS_LEVELS, + DA7218_DGS_SIGNAL_LVL_SHIFT, DA7218_DGS_SIGNAL_LVL_MAX, + DA7218_INVERT, da7218_dgs_signal_tlv), + SOC_SINGLE("DGS Gain Subrange Switch", DA7218_DGS_GAIN_CTRL, + DA7218_DGS_SUBR_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("DGS Gain Ramp Switch", DA7218_DGS_GAIN_CTRL, + DA7218_DGS_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("DGS Gain Steps", DA7218_DGS_GAIN_CTRL, + DA7218_DGS_STEPS_SHIFT, DA7218_DGS_STEPS_MAX, + DA7218_NO_INVERT), + SOC_DOUBLE("DGS Switch", DA7218_DGS_ENABLE, DA7218_DGS_ENABLE_L_SHIFT, + DA7218_DGS_ENABLE_R_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + + /* Output High-Pass Filter */ + SOC_ENUM("Out Filter HPF Mode", da7218_out1_hpf_mode), + SOC_ENUM("Out Filter HPF Corner Audio", da7218_out1_audio_hpf_corner), + SOC_ENUM("Out Filter HPF Corner Voice", da7218_out1_voice_hpf_corner), + + /* 5-Band Equaliser */ + SOC_SINGLE_TLV("Out EQ Band1 Volume", DA7218_OUT_1_EQ_12_FILTER_CTRL, + DA7218_OUT_1_EQ_BAND1_SHIFT, DA7218_OUT_EQ_BAND_MAX, + DA7218_NO_INVERT, da7218_out_eq_band_tlv), + SOC_SINGLE_TLV("Out EQ Band2 Volume", DA7218_OUT_1_EQ_12_FILTER_CTRL, + DA7218_OUT_1_EQ_BAND2_SHIFT, DA7218_OUT_EQ_BAND_MAX, + DA7218_NO_INVERT, da7218_out_eq_band_tlv), + SOC_SINGLE_TLV("Out EQ Band3 Volume", DA7218_OUT_1_EQ_34_FILTER_CTRL, + DA7218_OUT_1_EQ_BAND3_SHIFT, DA7218_OUT_EQ_BAND_MAX, + DA7218_NO_INVERT, da7218_out_eq_band_tlv), + SOC_SINGLE_TLV("Out EQ Band4 Volume", DA7218_OUT_1_EQ_34_FILTER_CTRL, + DA7218_OUT_1_EQ_BAND4_SHIFT, DA7218_OUT_EQ_BAND_MAX, + DA7218_NO_INVERT, da7218_out_eq_band_tlv), + SOC_SINGLE_TLV("Out EQ Band5 Volume", DA7218_OUT_1_EQ_5_FILTER_CTRL, + DA7218_OUT_1_EQ_BAND5_SHIFT, DA7218_OUT_EQ_BAND_MAX, + DA7218_NO_INVERT, da7218_out_eq_band_tlv), + SOC_SINGLE("Out EQ Switch", DA7218_OUT_1_EQ_5_FILTER_CTRL, + DA7218_OUT_1_EQ_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + + /* BiQuad Filters */ + SND_SOC_BYTES_EXT("BiQuad Coefficients", + DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE, + da7218_biquad_coeff_get, da7218_biquad_coeff_put), + SOC_SINGLE("BiQuad Filter Switch", DA7218_OUT_1_BIQ_5STAGE_CTRL, + DA7218_OUT_1_BIQ_5STAGE_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + + /* Output Filters */ + SOC_DOUBLE_R_RANGE_TLV("Out Filter Volume", DA7218_OUT_1L_GAIN, + DA7218_OUT_1R_GAIN, + DA7218_OUT_1L_DIGITAL_GAIN_SHIFT, + DA7218_OUT_DIGITAL_GAIN_MIN, + DA7218_OUT_DIGITAL_GAIN_MAX, DA7218_NO_INVERT, + da7218_out_dig_gain_tlv), + SOC_DOUBLE_R("Out Filter Switch", DA7218_OUT_1L_FILTER_CTRL, + DA7218_OUT_1R_FILTER_CTRL, DA7218_OUT_1L_MUTE_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_INVERT), + SOC_DOUBLE_R("Out Filter Gain Subrange Switch", + DA7218_OUT_1L_FILTER_CTRL, DA7218_OUT_1R_FILTER_CTRL, + DA7218_OUT_1L_SUBRANGE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_DOUBLE_R("Out Filter Gain Ramp Switch", DA7218_OUT_1L_FILTER_CTRL, + DA7218_OUT_1R_FILTER_CTRL, DA7218_OUT_1L_RAMP_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), + + /* Mixer Output */ + SOC_DOUBLE_R_RANGE_TLV("Mixout Volume", DA7218_MIXOUT_L_GAIN, + DA7218_MIXOUT_R_GAIN, + DA7218_MIXOUT_L_AMP_GAIN_SHIFT, + DA7218_MIXOUT_AMP_GAIN_MIN, + DA7218_MIXOUT_AMP_GAIN_MAX, DA7218_NO_INVERT, + da7218_mixout_gain_tlv), + + /* DAC Noise Gate */ + SOC_ENUM("DAC NG Setup Time", da7218_dac_ng_setup_time), + SOC_ENUM("DAC NG Rampup Rate", da7218_dac_ng_rampup_rate), + SOC_ENUM("DAC NG Rampdown Rate", da7218_dac_ng_rampdown_rate), + SOC_SINGLE_TLV("DAC NG Off Threshold", DA7218_DAC_NG_OFF_THRESH, + DA7218_DAC_NG_OFF_THRESHOLD_SHIFT, + DA7218_DAC_NG_THRESHOLD_MAX, DA7218_NO_INVERT, + da7218_dac_ng_threshold_tlv), + SOC_SINGLE_TLV("DAC NG On Threshold", DA7218_DAC_NG_ON_THRESH, + DA7218_DAC_NG_ON_THRESHOLD_SHIFT, + DA7218_DAC_NG_THRESHOLD_MAX, DA7218_NO_INVERT, + da7218_dac_ng_threshold_tlv), + SOC_SINGLE("DAC NG Switch", DA7218_DAC_NG_CTRL, DA7218_DAC_NG_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), + + /* CP */ + SOC_ENUM("Charge Pump Track Mode", da7218_cp_mchange), + SOC_ENUM("Charge Pump Frequency", da7218_cp_fcontrol), + SOC_ENUM("Charge Pump Decay Rate", da7218_cp_tau_delay), + SOC_SINGLE("Charge Pump Threshold", DA7218_CP_VOL_THRESHOLD1, + DA7218_CP_THRESH_VDD2_SHIFT, DA7218_CP_THRESH_VDD2_MAX, + DA7218_NO_INVERT), + + /* Headphones */ + SOC_DOUBLE_R_RANGE_TLV("Headphone Volume", DA7218_HP_L_GAIN, + DA7218_HP_R_GAIN, DA7218_HP_L_AMP_GAIN_SHIFT, + DA7218_HP_AMP_GAIN_MIN, DA7218_HP_AMP_GAIN_MAX, + DA7218_NO_INVERT, da7218_hp_gain_tlv), + SOC_DOUBLE_R("Headphone Switch", DA7218_HP_L_CTRL, DA7218_HP_R_CTRL, + DA7218_HP_L_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + SOC_DOUBLE_R("Headphone Gain Ramp Switch", DA7218_HP_L_CTRL, + DA7218_HP_R_CTRL, DA7218_HP_L_AMP_RAMP_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), + SOC_DOUBLE_R("Headphone ZC Gain Switch", DA7218_HP_L_CTRL, + DA7218_HP_R_CTRL, DA7218_HP_L_AMP_ZC_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), +}; + + +/* + * DAPM Mux Controls + */ + +static const char * const da7218_mic_sel_text[] = { "Analog", "Digital" }; + +static const struct soc_enum da7218_mic1_sel = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(da7218_mic_sel_text), + da7218_mic_sel_text); + +static const struct snd_kcontrol_new da7218_mic1_sel_mux = + SOC_DAPM_ENUM("Mic1 Mux", da7218_mic1_sel); + +static const struct soc_enum da7218_mic2_sel = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(da7218_mic_sel_text), + da7218_mic_sel_text); + +static const struct snd_kcontrol_new da7218_mic2_sel_mux = + SOC_DAPM_ENUM("Mic2 Mux", da7218_mic2_sel); + +static const char * const da7218_sidetone_in_sel_txt[] = { + "In Filter1L", "In Filter1R", "In Filter2L", "In Filter2R" +}; + +static const struct soc_enum da7218_sidetone_in_sel = + SOC_ENUM_SINGLE(DA7218_SIDETONE_IN_SELECT, + DA7218_SIDETONE_IN_SELECT_SHIFT, + DA7218_SIDETONE_IN_SELECT_MAX, + da7218_sidetone_in_sel_txt); + +static const struct snd_kcontrol_new da7218_sidetone_in_sel_mux = + SOC_DAPM_ENUM("Sidetone Mux", da7218_sidetone_in_sel); + +static const char * const da7218_out_filt_biq_sel_txt[] = { + "Bypass", "Enabled" +}; + +static const struct soc_enum da7218_out_filtl_biq_sel = + SOC_ENUM_SINGLE(DA7218_OUT_1L_FILTER_CTRL, + DA7218_OUT_1L_BIQ_5STAGE_SEL_SHIFT, + DA7218_OUT_BIQ_5STAGE_SEL_MAX, + da7218_out_filt_biq_sel_txt); + +static const struct snd_kcontrol_new da7218_out_filtl_biq_sel_mux = + SOC_DAPM_ENUM("Out FilterL BiQuad Mux", da7218_out_filtl_biq_sel); + +static const struct soc_enum da7218_out_filtr_biq_sel = + SOC_ENUM_SINGLE(DA7218_OUT_1R_FILTER_CTRL, + DA7218_OUT_1R_BIQ_5STAGE_SEL_SHIFT, + DA7218_OUT_BIQ_5STAGE_SEL_MAX, + da7218_out_filt_biq_sel_txt); + +static const struct snd_kcontrol_new da7218_out_filtr_biq_sel_mux = + SOC_DAPM_ENUM("Out FilterR BiQuad Mux", da7218_out_filtr_biq_sel); + + +/* + * DAPM Mixer Controls + */ + +#define DA7218_DMIX_CTRLS(reg) \ + SOC_DAPM_SINGLE("In Filter1L Switch", reg, \ + DA7218_DMIX_SRC_INFILT1L, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \ + SOC_DAPM_SINGLE("In Filter1R Switch", reg, \ + DA7218_DMIX_SRC_INFILT1R, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \ + SOC_DAPM_SINGLE("In Filter2L Switch", reg, \ + DA7218_DMIX_SRC_INFILT2L, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \ + SOC_DAPM_SINGLE("In Filter2R Switch", reg, \ + DA7218_DMIX_SRC_INFILT2R, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \ + SOC_DAPM_SINGLE("ToneGen Switch", reg, \ + DA7218_DMIX_SRC_TONEGEN, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \ + SOC_DAPM_SINGLE("DAIL Switch", reg, DA7218_DMIX_SRC_DAIL, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \ + SOC_DAPM_SINGLE("DAIR Switch", reg, DA7218_DMIX_SRC_DAIR, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT) + +static const struct snd_kcontrol_new da7218_out_dai1l_mix_controls[] = { + DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTDAI_1L), +}; + +static const struct snd_kcontrol_new da7218_out_dai1r_mix_controls[] = { + DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTDAI_1R), +}; + +static const struct snd_kcontrol_new da7218_out_dai2l_mix_controls[] = { + DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTDAI_2L), +}; + +static const struct snd_kcontrol_new da7218_out_dai2r_mix_controls[] = { + DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTDAI_2R), +}; + +static const struct snd_kcontrol_new da7218_out_filtl_mix_controls[] = { + DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTFILT_1L), +}; + +static const struct snd_kcontrol_new da7218_out_filtr_mix_controls[] = { + DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTFILT_1R), +}; + +#define DA7218_DMIX_ST_CTRLS(reg) \ + SOC_DAPM_SINGLE("Out FilterL Switch", reg, \ + DA7218_DMIX_ST_SRC_OUTFILT1L, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \ + SOC_DAPM_SINGLE("Out FilterR Switch", reg, \ + DA7218_DMIX_ST_SRC_OUTFILT1R, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \ + SOC_DAPM_SINGLE("Sidetone Switch", reg, \ + DA7218_DMIX_ST_SRC_SIDETONE, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT) \ + +static const struct snd_kcontrol_new da7218_st_out_filtl_mix_controls[] = { + DA7218_DMIX_ST_CTRLS(DA7218_DROUTING_ST_OUTFILT_1L), +}; + +static const struct snd_kcontrol_new da7218_st_out_filtr_mix_controls[] = { + DA7218_DMIX_ST_CTRLS(DA7218_DROUTING_ST_OUTFILT_1R), +}; + + +/* + * DAPM Events + */ + +/* + * We keep track of which input filters are enabled. This is used in the logic + * for controlling the mic level detect feature. + */ +static int da7218_in_filter_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 da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + u8 mask; + + switch (w->reg) { + case DA7218_IN_1L_FILTER_CTRL: + mask = (1 << DA7218_LVL_DET_EN_CHAN1L_SHIFT); + break; + case DA7218_IN_1R_FILTER_CTRL: + mask = (1 << DA7218_LVL_DET_EN_CHAN1R_SHIFT); + break; + case DA7218_IN_2L_FILTER_CTRL: + mask = (1 << DA7218_LVL_DET_EN_CHAN2L_SHIFT); + break; + case DA7218_IN_2R_FILTER_CTRL: + mask = (1 << DA7218_LVL_DET_EN_CHAN2R_SHIFT); + break; + default: + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + da7218->in_filt_en |= mask; + /* + * If we're enabling path for mic level detect, wait for path + * to settle before enabling feature to avoid incorrect and + * unwanted detect events. + */ + if (mask & da7218->mic_lvl_det_en) + msleep(DA7218_MIC_LVL_DET_DELAY); + break; + case SND_SOC_DAPM_PRE_PMD: + da7218->in_filt_en &= ~mask; + break; + default: + return -EINVAL; + } + + /* Enable configured level detection paths */ + snd_soc_write(codec, DA7218_LVL_DET_CTRL, + (da7218->in_filt_en & da7218->mic_lvl_det_en)); + + return 0; +} + +static int da7218_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 da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + u8 pll_ctrl, pll_status, refosc_cal; + int i; + bool success; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (da7218->master) + /* Enable DAI clks for master mode */ + snd_soc_update_bits(codec, DA7218_DAI_CLK_MODE, + DA7218_DAI_CLK_EN_MASK, + DA7218_DAI_CLK_EN_MASK); + + /* Tune reference oscillator */ + snd_soc_write(codec, DA7218_PLL_REFOSC_CAL, + DA7218_PLL_REFOSC_CAL_START_MASK); + snd_soc_write(codec, DA7218_PLL_REFOSC_CAL, + DA7218_PLL_REFOSC_CAL_START_MASK | + DA7218_PLL_REFOSC_CAL_EN_MASK); + + /* Check tuning complete */ + i = 0; + success = false; + do { + refosc_cal = snd_soc_read(codec, DA7218_PLL_REFOSC_CAL); + if (!(refosc_cal & DA7218_PLL_REFOSC_CAL_START_MASK)) { + success = true; + } else { + ++i; + usleep_range(DA7218_REF_OSC_CHECK_DELAY_MIN, + DA7218_REF_OSC_CHECK_DELAY_MAX); + } + } while ((i < DA7218_REF_OSC_CHECK_TRIES) && (!success)); + + if (!success) + dev_warn(codec->dev, + "Reference oscillator failed calibration\n"); + + /* PC synchronised to DAI */ + snd_soc_write(codec, DA7218_PC_COUNT, + DA7218_PC_RESYNC_AUTO_MASK); + + /* If SRM not enabled, we don't need to check status */ + pll_ctrl = snd_soc_read(codec, DA7218_PLL_CTRL); + if ((pll_ctrl & DA7218_PLL_MODE_MASK) != DA7218_PLL_MODE_SRM) + return 0; + + /* Check SRM has locked */ + i = 0; + success = false; + do { + pll_status = snd_soc_read(codec, DA7218_PLL_STATUS); + if (pll_status & DA7218_PLL_SRM_STATUS_SRM_LOCK) { + success = true; + } else { + ++i; + msleep(DA7218_SRM_CHECK_DELAY); + } + } while ((i < DA7218_SRM_CHECK_TRIES) & (!success)); + + if (!success) + dev_warn(codec->dev, "SRM failed to lock\n"); + + return 0; + case SND_SOC_DAPM_POST_PMD: + /* PC free-running */ + snd_soc_write(codec, DA7218_PC_COUNT, DA7218_PC_FREERUN_MASK); + + if (da7218->master) + /* Disable DAI clks for master mode */ + snd_soc_update_bits(codec, DA7218_DAI_CLK_MODE, + DA7218_DAI_CLK_EN_MASK, 0); + + return 0; + default: + return -EINVAL; + } +} + +static int da7218_cp_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 da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + + /* + * If this is DA7217 and we're using single supply for differential + * output, we really don't want to touch the charge pump. + */ + if (da7218->hp_single_supply) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, DA7218_CP_CTRL, DA7218_CP_EN_MASK, + DA7218_CP_EN_MASK); + return 0; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, DA7218_CP_CTRL, DA7218_CP_EN_MASK, + 0); + return 0; + default: + return -EINVAL; + } +} + +static int da7218_hp_pga_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); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Enable headphone output */ + snd_soc_update_bits(codec, w->reg, DA7218_HP_AMP_OE_MASK, + DA7218_HP_AMP_OE_MASK); + return 0; + case SND_SOC_DAPM_PRE_PMD: + /* Headphone output high impedance */ + snd_soc_update_bits(codec, w->reg, DA7218_HP_AMP_OE_MASK, 0); + return 0; + default: + return -EINVAL; + } +} + + +/* + * DAPM Widgets + */ + +static const struct snd_soc_dapm_widget da7218_dapm_widgets[] = { + /* Input Supplies */ + SND_SOC_DAPM_SUPPLY("Mic Bias1", DA7218_MICBIAS_EN, + DA7218_MICBIAS_1_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias2", DA7218_MICBIAS_EN, + DA7218_MICBIAS_2_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DMic1 Left", DA7218_DMIC_1_CTRL, + DA7218_DMIC_1L_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DMic1 Right", DA7218_DMIC_1_CTRL, + DA7218_DMIC_1R_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DMic2 Left", DA7218_DMIC_2_CTRL, + DA7218_DMIC_2L_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DMic2 Right", DA7218_DMIC_2_CTRL, + DA7218_DMIC_2R_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + + /* Inputs */ + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("DMIC1L"), + SND_SOC_DAPM_INPUT("DMIC1R"), + SND_SOC_DAPM_INPUT("DMIC2L"), + SND_SOC_DAPM_INPUT("DMIC2R"), + + /* Input Mixer Supplies */ + SND_SOC_DAPM_SUPPLY("Mixin1 Supply", DA7218_MIXIN_1_CTRL, + DA7218_MIXIN_1_MIX_SEL_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("Mixin2 Supply", DA7218_MIXIN_2_CTRL, + DA7218_MIXIN_2_MIX_SEL_SHIFT, DA7218_NO_INVERT, + NULL, 0), + + /* Input PGAs */ + SND_SOC_DAPM_PGA("Mic1 PGA", DA7218_MIC_1_CTRL, + DA7218_MIC_1_AMP_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Mic2 PGA", DA7218_MIC_2_CTRL, + DA7218_MIC_2_AMP_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Mixin1 PGA", DA7218_MIXIN_1_CTRL, + DA7218_MIXIN_1_AMP_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Mixin2 PGA", DA7218_MIXIN_2_CTRL, + DA7218_MIXIN_2_AMP_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + + /* Mic/DMic Muxes */ + SND_SOC_DAPM_MUX("Mic1 Mux", SND_SOC_NOPM, 0, 0, &da7218_mic1_sel_mux), + SND_SOC_DAPM_MUX("Mic2 Mux", SND_SOC_NOPM, 0, 0, &da7218_mic2_sel_mux), + + /* Input Filters */ + SND_SOC_DAPM_ADC_E("In Filter1L", NULL, DA7218_IN_1L_FILTER_CTRL, + DA7218_IN_1L_FILTER_EN_SHIFT, DA7218_NO_INVERT, + da7218_in_filter_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("In Filter1R", NULL, DA7218_IN_1R_FILTER_CTRL, + DA7218_IN_1R_FILTER_EN_SHIFT, DA7218_NO_INVERT, + da7218_in_filter_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("In Filter2L", NULL, DA7218_IN_2L_FILTER_CTRL, + DA7218_IN_2L_FILTER_EN_SHIFT, DA7218_NO_INVERT, + da7218_in_filter_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("In Filter2R", NULL, DA7218_IN_2R_FILTER_CTRL, + DA7218_IN_2R_FILTER_EN_SHIFT, DA7218_NO_INVERT, + da7218_in_filter_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + /* Tone Generator */ + SND_SOC_DAPM_SIGGEN("TONE"), + SND_SOC_DAPM_PGA("Tone Generator", DA7218_TONE_GEN_CFG1, + DA7218_START_STOPN_SHIFT, DA7218_NO_INVERT, NULL, 0), + + /* Sidetone Input */ + SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0, + &da7218_sidetone_in_sel_mux), + SND_SOC_DAPM_ADC("Sidetone Filter", NULL, DA7218_SIDETONE_CTRL, + DA7218_SIDETONE_FILTER_EN_SHIFT, DA7218_NO_INVERT), + + /* Input Mixers */ + SND_SOC_DAPM_MIXER("Mixer DAI1L", SND_SOC_NOPM, 0, 0, + da7218_out_dai1l_mix_controls, + ARRAY_SIZE(da7218_out_dai1l_mix_controls)), + SND_SOC_DAPM_MIXER("Mixer DAI1R", SND_SOC_NOPM, 0, 0, + da7218_out_dai1r_mix_controls, + ARRAY_SIZE(da7218_out_dai1r_mix_controls)), + SND_SOC_DAPM_MIXER("Mixer DAI2L", SND_SOC_NOPM, 0, 0, + da7218_out_dai2l_mix_controls, + ARRAY_SIZE(da7218_out_dai2l_mix_controls)), + SND_SOC_DAPM_MIXER("Mixer DAI2R", SND_SOC_NOPM, 0, 0, + da7218_out_dai2r_mix_controls, + ARRAY_SIZE(da7218_out_dai2r_mix_controls)), + + /* DAI Supply */ + SND_SOC_DAPM_SUPPLY("DAI", DA7218_DAI_CTRL, DA7218_DAI_EN_SHIFT, + DA7218_NO_INVERT, da7218_dai_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* DAI */ + SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Output Mixers */ + SND_SOC_DAPM_MIXER("Mixer Out FilterL", SND_SOC_NOPM, 0, 0, + da7218_out_filtl_mix_controls, + ARRAY_SIZE(da7218_out_filtl_mix_controls)), + SND_SOC_DAPM_MIXER("Mixer Out FilterR", SND_SOC_NOPM, 0, 0, + da7218_out_filtr_mix_controls, + ARRAY_SIZE(da7218_out_filtr_mix_controls)), + + /* BiQuad Filters */ + SND_SOC_DAPM_MUX("Out FilterL BiQuad Mux", SND_SOC_NOPM, 0, 0, + &da7218_out_filtl_biq_sel_mux), + SND_SOC_DAPM_MUX("Out FilterR BiQuad Mux", SND_SOC_NOPM, 0, 0, + &da7218_out_filtr_biq_sel_mux), + SND_SOC_DAPM_DAC("BiQuad Filter", NULL, DA7218_OUT_1_BIQ_5STAGE_CTRL, + DA7218_OUT_1_BIQ_5STAGE_FILTER_EN_SHIFT, + DA7218_NO_INVERT), + + /* Sidetone Mixers */ + SND_SOC_DAPM_MIXER("ST Mixer Out FilterL", SND_SOC_NOPM, 0, 0, + da7218_st_out_filtl_mix_controls, + ARRAY_SIZE(da7218_st_out_filtl_mix_controls)), + SND_SOC_DAPM_MIXER("ST Mixer Out FilterR", SND_SOC_NOPM, 0, 0, + da7218_st_out_filtr_mix_controls, + ARRAY_SIZE(da7218_st_out_filtr_mix_controls)), + + /* Output Filters */ + SND_SOC_DAPM_DAC("Out FilterL", NULL, DA7218_OUT_1L_FILTER_CTRL, + DA7218_OUT_1L_FILTER_EN_SHIFT, DA7218_NO_INVERT), + SND_SOC_DAPM_DAC("Out FilterR", NULL, DA7218_OUT_1R_FILTER_CTRL, + DA7218_IN_1R_FILTER_EN_SHIFT, DA7218_NO_INVERT), + + /* Output PGAs */ + SND_SOC_DAPM_PGA("Mixout Left PGA", DA7218_MIXOUT_L_CTRL, + DA7218_MIXOUT_L_AMP_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Mixout Right PGA", DA7218_MIXOUT_R_CTRL, + DA7218_MIXOUT_R_AMP_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA_E("Headphone Left PGA", DA7218_HP_L_CTRL, + DA7218_HP_L_AMP_EN_SHIFT, DA7218_NO_INVERT, NULL, 0, + da7218_hp_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("Headphone Right PGA", DA7218_HP_R_CTRL, + DA7218_HP_R_AMP_EN_SHIFT, DA7218_NO_INVERT, NULL, 0, + da7218_hp_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + /* Output Supplies */ + SND_SOC_DAPM_SUPPLY("Charge Pump", SND_SOC_NOPM, 0, 0, da7218_cp_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), +}; + + +/* + * DAPM Mixer Routes + */ + +#define DA7218_DMIX_ROUTES(name) \ + {name, "In Filter1L Switch", "In Filter1L"}, \ + {name, "In Filter1R Switch", "In Filter1R"}, \ + {name, "In Filter2L Switch", "In Filter2L"}, \ + {name, "In Filter2R Switch", "In Filter2R"}, \ + {name, "ToneGen Switch", "Tone Generator"}, \ + {name, "DAIL Switch", "DAIIN"}, \ + {name, "DAIR Switch", "DAIIN"} + +#define DA7218_DMIX_ST_ROUTES(name) \ + {name, "Out FilterL Switch", "Out FilterL BiQuad Mux"}, \ + {name, "Out FilterR Switch", "Out FilterR BiQuad Mux"}, \ + {name, "Sidetone Switch", "Sidetone Filter"} + + +/* + * DAPM audio route definition + */ + +static const struct snd_soc_dapm_route da7218_audio_map[] = { + /* Input paths */ + {"MIC1", NULL, "Mic Bias1"}, + {"MIC2", NULL, "Mic Bias2"}, + {"DMIC1L", NULL, "Mic Bias1"}, + {"DMIC1L", NULL, "DMic1 Left"}, + {"DMIC1R", NULL, "Mic Bias1"}, + {"DMIC1R", NULL, "DMic1 Right"}, + {"DMIC2L", NULL, "Mic Bias2"}, + {"DMIC2L", NULL, "DMic2 Left"}, + {"DMIC2R", NULL, "Mic Bias2"}, + {"DMIC2R", NULL, "DMic2 Right"}, + + {"Mic1 PGA", NULL, "MIC1"}, + {"Mic2 PGA", NULL, "MIC2"}, + + {"Mixin1 PGA", NULL, "Mixin1 Supply"}, + {"Mixin2 PGA", NULL, "Mixin2 Supply"}, + + {"Mixin1 PGA", NULL, "Mic1 PGA"}, + {"Mixin2 PGA", NULL, "Mic2 PGA"}, + + {"Mic1 Mux", "Analog", "Mixin1 PGA"}, + {"Mic1 Mux", "Digital", "DMIC1L"}, + {"Mic1 Mux", "Digital", "DMIC1R"}, + {"Mic2 Mux", "Analog", "Mixin2 PGA"}, + {"Mic2 Mux", "Digital", "DMIC2L"}, + {"Mic2 Mux", "Digital", "DMIC2R"}, + + {"In Filter1L", NULL, "Mic1 Mux"}, + {"In Filter1R", NULL, "Mic1 Mux"}, + {"In Filter2L", NULL, "Mic2 Mux"}, + {"In Filter2R", NULL, "Mic2 Mux"}, + + {"Tone Generator", NULL, "TONE"}, + + {"Sidetone Mux", "In Filter1L", "In Filter1L"}, + {"Sidetone Mux", "In Filter1R", "In Filter1R"}, + {"Sidetone Mux", "In Filter2L", "In Filter2L"}, + {"Sidetone Mux", "In Filter2R", "In Filter2R"}, + {"Sidetone Filter", NULL, "Sidetone Mux"}, + + DA7218_DMIX_ROUTES("Mixer DAI1L"), + DA7218_DMIX_ROUTES("Mixer DAI1R"), + DA7218_DMIX_ROUTES("Mixer DAI2L"), + DA7218_DMIX_ROUTES("Mixer DAI2R"), + + {"DAIOUT", NULL, "Mixer DAI1L"}, + {"DAIOUT", NULL, "Mixer DAI1R"}, + {"DAIOUT", NULL, "Mixer DAI2L"}, + {"DAIOUT", NULL, "Mixer DAI2R"}, + + {"DAIOUT", NULL, "DAI"}, + + /* Output paths */ + {"DAIIN", NULL, "DAI"}, + + DA7218_DMIX_ROUTES("Mixer Out FilterL"), + DA7218_DMIX_ROUTES("Mixer Out FilterR"), + + {"BiQuad Filter", NULL, "Mixer Out FilterL"}, + {"BiQuad Filter", NULL, "Mixer Out FilterR"}, + + {"Out FilterL BiQuad Mux", "Bypass", "Mixer Out FilterL"}, + {"Out FilterL BiQuad Mux", "Enabled", "BiQuad Filter"}, + {"Out FilterR BiQuad Mux", "Bypass", "Mixer Out FilterR"}, + {"Out FilterR BiQuad Mux", "Enabled", "BiQuad Filter"}, + + DA7218_DMIX_ST_ROUTES("ST Mixer Out FilterL"), + DA7218_DMIX_ST_ROUTES("ST Mixer Out FilterR"), + + {"Out FilterL", NULL, "ST Mixer Out FilterL"}, + {"Out FilterR", NULL, "ST Mixer Out FilterR"}, + + {"Mixout Left PGA", NULL, "Out FilterL"}, + {"Mixout Right PGA", NULL, "Out FilterR"}, + + {"Headphone Left PGA", NULL, "Mixout Left PGA"}, + {"Headphone Right PGA", NULL, "Mixout Right PGA"}, + + {"HPL", NULL, "Headphone Left PGA"}, + {"HPR", NULL, "Headphone Right PGA"}, + + {"HPL", NULL, "Charge Pump"}, + {"HPR", NULL, "Charge Pump"}, +}; + + +/* + * DAI operations + */ + +static int da7218_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + int ret; + + if (da7218->mclk_rate == freq) + return 0; + + if (((freq < 2000000) && (freq != 32768)) || (freq > 54000000)) { + dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", + freq); + return -EINVAL; + } + + switch (clk_id) { + case DA7218_CLKSRC_MCLK_SQR: + snd_soc_update_bits(codec, DA7218_PLL_CTRL, + DA7218_PLL_MCLK_SQR_EN_MASK, + DA7218_PLL_MCLK_SQR_EN_MASK); + break; + case DA7218_CLKSRC_MCLK: + snd_soc_update_bits(codec, DA7218_PLL_CTRL, + DA7218_PLL_MCLK_SQR_EN_MASK, 0); + break; + default: + dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); + return -EINVAL; + } + + if (da7218->mclk) { + freq = clk_round_rate(da7218->mclk, freq); + ret = clk_set_rate(da7218->mclk, freq); + if (ret) { + dev_err(codec_dai->dev, "Failed to set clock rate %d\n", + freq); + return ret; + } + } + + da7218->mclk_rate = freq; + + return 0; +} + +static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + + u8 pll_ctrl, indiv_bits, indiv; + u8 pll_frac_top, pll_frac_bot, pll_integer; + u32 freq_ref; + u64 frac_div; + + /* 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; + } 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 <= 54000000) { + indiv_bits = DA7218_PLL_INDIV_40_54_MHZ; + indiv = DA7218_PLL_INDIV_40_54_MHZ_VAL; + } else { + dev_err(codec->dev, "PLL input clock %d above valid range\n", + da7218->mclk_rate); + return -EINVAL; + } + freq_ref = (da7218->mclk_rate / indiv); + pll_ctrl = indiv_bits; + + /* Configure PLL */ + switch (source) { + case DA7218_SYSCLK_MCLK: + pll_ctrl |= DA7218_PLL_MODE_BYPASS; + snd_soc_update_bits(codec, DA7218_PLL_CTRL, + DA7218_PLL_INDIV_MASK | + DA7218_PLL_MODE_MASK, pll_ctrl); + return 0; + case DA7218_SYSCLK_PLL: + pll_ctrl |= DA7218_PLL_MODE_NORMAL; + break; + case DA7218_SYSCLK_PLL_SRM: + pll_ctrl |= DA7218_PLL_MODE_SRM; + break; + case DA7218_SYSCLK_PLL_32KHZ: + pll_ctrl |= DA7218_PLL_MODE_32KHZ; + break; + default: + dev_err(codec->dev, "Invalid PLL config\n"); + return -EINVAL; + } + + /* Calculate dividers for PLL */ + pll_integer = fout / freq_ref; + frac_div = (u64)(fout % freq_ref) * 8192ULL; + do_div(frac_div, freq_ref); + pll_frac_top = (frac_div >> DA7218_BYTE_SHIFT) & DA7218_BYTE_MASK; + pll_frac_bot = (frac_div) & DA7218_BYTE_MASK; + + /* Write PLL config & dividers */ + snd_soc_write(codec, DA7218_PLL_FRAC_TOP, pll_frac_top); + snd_soc_write(codec, DA7218_PLL_FRAC_BOT, pll_frac_bot); + snd_soc_write(codec, DA7218_PLL_INTEGER, pll_integer); + snd_soc_update_bits(codec, DA7218_PLL_CTRL, + DA7218_PLL_MODE_MASK | DA7218_PLL_INDIV_MASK, + pll_ctrl); + + return 0; +} + +static int da7218_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + u8 dai_clk_mode = 0, dai_ctrl = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + da7218->master = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + da7218->master = false; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + dai_clk_mode |= DA7218_DAI_WCLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + dai_clk_mode |= DA7218_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_IF: + dai_clk_mode |= DA7218_DAI_WCLK_POL_INV | DA7218_DAI_CLK_POL_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dai_ctrl |= DA7218_DAI_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + dai_ctrl |= DA7218_DAI_FORMAT_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dai_ctrl |= DA7218_DAI_FORMAT_RIGHT_J; + break; + case SND_SOC_DAIFMT_DSP_B: + dai_ctrl |= DA7218_DAI_FORMAT_DSP; + break; + default: + return -EINVAL; + } + + /* By default 64 BCLKs per WCLK is supported */ + dai_clk_mode |= DA7218_DAI_BCLKS_PER_WCLK_64; + + snd_soc_write(codec, DA7218_DAI_CLK_MODE, dai_clk_mode); + snd_soc_update_bits(codec, DA7218_DAI_CTRL, DA7218_DAI_FORMAT_MASK, + dai_ctrl); + + return 0; +} + +static int da7218_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + u8 dai_bclks_per_wclk; + u32 frame_size; + + /* No channels enabled so disable TDM, revert to 64-bit frames */ + if (!tx_mask) { + snd_soc_update_bits(codec, DA7218_DAI_TDM_CTRL, + DA7218_DAI_TDM_CH_EN_MASK | + DA7218_DAI_TDM_MODE_EN_MASK, 0); + snd_soc_update_bits(codec, DA7218_DAI_CLK_MODE, + DA7218_DAI_BCLKS_PER_WCLK_MASK, + DA7218_DAI_BCLKS_PER_WCLK_64); + return 0; + } + + /* Check we have valid slots */ + if (fls(tx_mask) > DA7218_DAI_TDM_MAX_SLOTS) { + dev_err(codec->dev, "Invalid number of slots, max = %d\n", + DA7218_DAI_TDM_MAX_SLOTS); + return -EINVAL; + } + + /* Check we have a valid offset given (first 2 bytes of rx_mask) */ + if (rx_mask >> DA7218_2BYTE_SHIFT) { + dev_err(codec->dev, "Invalid slot offset, max = %d\n", + DA7218_2BYTE_MASK); + return -EINVAL; + } + + /* Calculate & validate frame size based on slot info provided. */ + frame_size = slots * slot_width; + switch (frame_size) { + case 32: + dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_32; + break; + case 64: + dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_64; + break; + case 128: + dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_128; + break; + case 256: + dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_256; + break; + default: + dev_err(codec->dev, "Invalid frame size\n"); + return -EINVAL; + } + + snd_soc_update_bits(codec, DA7218_DAI_CLK_MODE, + DA7218_DAI_BCLKS_PER_WCLK_MASK, + dai_bclks_per_wclk); + snd_soc_write(codec, DA7218_DAI_OFFSET_LOWER, + (rx_mask & DA7218_BYTE_MASK)); + snd_soc_write(codec, DA7218_DAI_OFFSET_UPPER, + ((rx_mask >> DA7218_BYTE_SHIFT) & DA7218_BYTE_MASK)); + snd_soc_update_bits(codec, DA7218_DAI_TDM_CTRL, + DA7218_DAI_TDM_CH_EN_MASK | + DA7218_DAI_TDM_MODE_EN_MASK, + (tx_mask << DA7218_DAI_TDM_CH_EN_SHIFT) | + DA7218_DAI_TDM_MODE_EN_MASK); + + return 0; +} + +static int da7218_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; + u8 dai_ctrl = 0, fs; + unsigned int channels; + + switch (params_width(params)) { + case 16: + dai_ctrl |= DA7218_DAI_WORD_LENGTH_S16_LE; + break; + case 20: + dai_ctrl |= DA7218_DAI_WORD_LENGTH_S20_LE; + break; + case 24: + dai_ctrl |= DA7218_DAI_WORD_LENGTH_S24_LE; + break; + case 32: + dai_ctrl |= DA7218_DAI_WORD_LENGTH_S32_LE; + break; + default: + return -EINVAL; + } + + channels = params_channels(params); + if ((channels < 1) || (channels > DA7218_DAI_CH_NUM_MAX)) { + dev_err(codec->dev, + "Invalid number of channels, only 1 to %d supported\n", + DA7218_DAI_CH_NUM_MAX); + return -EINVAL; + } + dai_ctrl |= channels << DA7218_DAI_CH_NUM_SHIFT; + + switch (params_rate(params)) { + case 8000: + fs = DA7218_SR_8000; + break; + case 11025: + fs = DA7218_SR_11025; + break; + case 12000: + fs = DA7218_SR_12000; + break; + case 16000: + fs = DA7218_SR_16000; + break; + case 22050: + fs = DA7218_SR_22050; + break; + case 24000: + fs = DA7218_SR_24000; + break; + case 32000: + fs = DA7218_SR_32000; + break; + case 44100: + fs = DA7218_SR_44100; + break; + case 48000: + fs = DA7218_SR_48000; + break; + case 88200: + fs = DA7218_SR_88200; + break; + case 96000: + fs = DA7218_SR_96000; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, DA7218_DAI_CTRL, + DA7218_DAI_WORD_LENGTH_MASK | DA7218_DAI_CH_NUM_MASK, + dai_ctrl); + /* SRs tied for ADCs and DACs. */ + snd_soc_write(codec, DA7218_SR, + (fs << DA7218_SR_DAC_SHIFT) | (fs << DA7218_SR_ADC_SHIFT)); + + return 0; +} + +static const struct snd_soc_dai_ops da7218_dai_ops = { + .hw_params = da7218_hw_params, + .set_sysclk = da7218_set_dai_sysclk, + .set_pll = da7218_set_dai_pll, + .set_fmt = da7218_set_dai_fmt, + .set_tdm_slot = da7218_set_dai_tdm_slot, +}; + +#define DA7218_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver da7218_dai = { + .name = "da7218-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 4, /* Only 2 channels of data */ + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA7218_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA7218_FORMATS, + }, + .ops = &da7218_dai_ops, + .symmetric_rates = 1, + .symmetric_channels = 1, + .symmetric_samplebits = 1, +}; + + +/* + * HP Detect + */ + +int da7218_hpldet(struct snd_soc_codec *codec, struct snd_soc_jack *jack) +{ + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + + if (da7218->dev_id == DA7217_DEV_ID) + return -EINVAL; + + da7218->jack = jack; + snd_soc_update_bits(codec, DA7218_HPLDET_JACK, + DA7218_HPLDET_JACK_EN_MASK, + jack ? DA7218_HPLDET_JACK_EN_MASK : 0); + + return 0; +} +EXPORT_SYMBOL_GPL(da7218_hpldet); + +static void da7218_micldet_irq(struct snd_soc_codec *codec) +{ + char *envp[] = { + "EVENT=MIC_LEVEL_DETECT", + NULL, + }; + + kobject_uevent_env(&codec->dev->kobj, KOBJ_CHANGE, envp); +} + +static void da7218_hpldet_irq(struct snd_soc_codec *codec) +{ + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + u8 jack_status; + int report; + + jack_status = snd_soc_read(codec, DA7218_EVENT_STATUS); + + if (jack_status & DA7218_HPLDET_JACK_STS_MASK) + report = SND_JACK_HEADPHONE; + else + report = 0; + + snd_soc_jack_report(da7218->jack, report, SND_JACK_HEADPHONE); +} + +/* + * IRQ + */ + +static irqreturn_t da7218_irq_thread(int irq, void *data) +{ + struct snd_soc_codec *codec = data; + u8 status; + + /* Read IRQ status reg */ + status = snd_soc_read(codec, DA7218_EVENT); + if (!status) + return IRQ_NONE; + + /* Mic level detect */ + if (status & DA7218_LVL_DET_EVENT_MASK) + da7218_micldet_irq(codec); + + /* HP detect */ + if (status & DA7218_HPLDET_JACK_EVENT_MASK) + da7218_hpldet_irq(codec); + + /* Clear interrupts */ + snd_soc_write(codec, DA7218_EVENT, status); + + return IRQ_HANDLED; +} + +/* + * DT + */ + +static const struct of_device_id da7218_of_match[] = { + { .compatible = "dlg,da7217", .data = (void *) DA7217_DEV_ID }, + { .compatible = "dlg,da7218", .data = (void *) DA7218_DEV_ID }, + { } +}; +MODULE_DEVICE_TABLE(of, da7218_of_match); + +static inline int da7218_of_get_id(struct device *dev) +{ + const struct of_device_id *id = of_match_device(da7218_of_match, dev); + + if (id) + return (uintptr_t)id->data; + else + return -EINVAL; +} + +static enum da7218_micbias_voltage + da7218_of_micbias_lvl(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1200: + return DA7218_MICBIAS_1_2V; + case 1600: + return DA7218_MICBIAS_1_6V; + case 1800: + return DA7218_MICBIAS_1_8V; + case 2000: + return DA7218_MICBIAS_2_0V; + case 2200: + return DA7218_MICBIAS_2_2V; + case 2400: + return DA7218_MICBIAS_2_4V; + case 2600: + return DA7218_MICBIAS_2_6V; + case 2800: + return DA7218_MICBIAS_2_8V; + case 3000: + return DA7218_MICBIAS_3_0V; + default: + dev_warn(codec->dev, "Invalid micbias level"); + return DA7218_MICBIAS_1_6V; + } +} + +static enum da7218_mic_amp_in_sel + da7218_of_mic_amp_in_sel(struct snd_soc_codec *codec, const char *str) +{ + if (!strcmp(str, "diff")) { + return DA7218_MIC_AMP_IN_SEL_DIFF; + } else if (!strcmp(str, "se_p")) { + return DA7218_MIC_AMP_IN_SEL_SE_P; + } else if (!strcmp(str, "se_n")) { + return DA7218_MIC_AMP_IN_SEL_SE_N; + } else { + dev_warn(codec->dev, "Invalid mic input type selection"); + return DA7218_MIC_AMP_IN_SEL_DIFF; + } +} + +static enum da7218_dmic_data_sel + da7218_of_dmic_data_sel(struct snd_soc_codec *codec, const char *str) +{ + if (!strcmp(str, "lrise_rfall")) { + return DA7218_DMIC_DATA_LRISE_RFALL; + } else if (!strcmp(str, "lfall_rrise")) { + return DA7218_DMIC_DATA_LFALL_RRISE; + } else { + dev_warn(codec->dev, "Invalid DMIC data type selection"); + return DA7218_DMIC_DATA_LRISE_RFALL; + } +} + +static enum da7218_dmic_samplephase + da7218_of_dmic_samplephase(struct snd_soc_codec *codec, const char *str) +{ + if (!strcmp(str, "on_clkedge")) { + return DA7218_DMIC_SAMPLE_ON_CLKEDGE; + } else if (!strcmp(str, "between_clkedge")) { + return DA7218_DMIC_SAMPLE_BETWEEN_CLKEDGE; + } else { + dev_warn(codec->dev, "Invalid DMIC sample phase"); + return DA7218_DMIC_SAMPLE_ON_CLKEDGE; + } +} + +static enum da7218_dmic_clk_rate + da7218_of_dmic_clkrate(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 1500000: + return DA7218_DMIC_CLK_1_5MHZ; + case 3000000: + return DA7218_DMIC_CLK_3_0MHZ; + default: + dev_warn(codec->dev, "Invalid DMIC clock rate"); + return DA7218_DMIC_CLK_3_0MHZ; + } +} + +static enum da7218_hpldet_jack_rate + da7218_of_jack_rate(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 5: + return DA7218_HPLDET_JACK_RATE_5US; + case 10: + return DA7218_HPLDET_JACK_RATE_10US; + case 20: + return DA7218_HPLDET_JACK_RATE_20US; + case 40: + return DA7218_HPLDET_JACK_RATE_40US; + case 80: + return DA7218_HPLDET_JACK_RATE_80US; + case 160: + return DA7218_HPLDET_JACK_RATE_160US; + case 320: + return DA7218_HPLDET_JACK_RATE_320US; + case 640: + return DA7218_HPLDET_JACK_RATE_640US; + default: + dev_warn(codec->dev, "Invalid jack detect rate"); + return DA7218_HPLDET_JACK_RATE_40US; + } +} + +static enum da7218_hpldet_jack_debounce + da7218_of_jack_debounce(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 0: + return DA7218_HPLDET_JACK_DEBOUNCE_OFF; + case 2: + return DA7218_HPLDET_JACK_DEBOUNCE_2; + case 3: + return DA7218_HPLDET_JACK_DEBOUNCE_3; + case 4: + return DA7218_HPLDET_JACK_DEBOUNCE_4; + default: + dev_warn(codec->dev, "Invalid jack debounce"); + return DA7218_HPLDET_JACK_DEBOUNCE_2; + } +} + +static enum da7218_hpldet_jack_thr + da7218_of_jack_thr(struct snd_soc_codec *codec, u32 val) +{ + switch (val) { + case 84: + return DA7218_HPLDET_JACK_THR_84PCT; + case 88: + return DA7218_HPLDET_JACK_THR_88PCT; + case 92: + return DA7218_HPLDET_JACK_THR_92PCT; + case 96: + return DA7218_HPLDET_JACK_THR_96PCT; + default: + dev_warn(codec->dev, "Invalid jack threshold level"); + return DA7218_HPLDET_JACK_THR_84PCT; + } +} + +static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_codec *codec) +{ + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + struct device_node *np = codec->dev->of_node; + struct device_node *hpldet_np; + struct da7218_pdata *pdata; + struct da7218_hpldet_pdata *hpldet_pdata; + const char *of_str; + u32 of_val32; + + pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_warn(codec->dev, "Failed to allocate memory for pdata\n"); + return NULL; + } + + if (of_property_read_u32(np, "dlg,micbias1-lvl-millivolt", &of_val32) >= 0) + pdata->micbias1_lvl = da7218_of_micbias_lvl(codec, of_val32); + else + pdata->micbias1_lvl = DA7218_MICBIAS_1_6V; + + if (of_property_read_u32(np, "dlg,micbias2-lvl-millivolt", &of_val32) >= 0) + pdata->micbias2_lvl = da7218_of_micbias_lvl(codec, of_val32); + else + pdata->micbias2_lvl = DA7218_MICBIAS_1_6V; + + if (!of_property_read_string(np, "dlg,mic1-amp-in-sel", &of_str)) + pdata->mic1_amp_in_sel = + da7218_of_mic_amp_in_sel(codec, of_str); + else + pdata->mic1_amp_in_sel = DA7218_MIC_AMP_IN_SEL_DIFF; + + if (!of_property_read_string(np, "dlg,mic2-amp-in-sel", &of_str)) + pdata->mic2_amp_in_sel = + da7218_of_mic_amp_in_sel(codec, of_str); + else + pdata->mic2_amp_in_sel = DA7218_MIC_AMP_IN_SEL_DIFF; + + if (!of_property_read_string(np, "dlg,dmic1-data-sel", &of_str)) + pdata->dmic1_data_sel = da7218_of_dmic_data_sel(codec, of_str); + else + pdata->dmic1_data_sel = DA7218_DMIC_DATA_LRISE_RFALL; + + if (!of_property_read_string(np, "dlg,dmic1-samplephase", &of_str)) + pdata->dmic1_samplephase = + da7218_of_dmic_samplephase(codec, of_str); + else + pdata->dmic1_samplephase = DA7218_DMIC_SAMPLE_ON_CLKEDGE; + + if (of_property_read_u32(np, "dlg,dmic1-clkrate-hz", &of_val32) >= 0) + pdata->dmic1_clk_rate = da7218_of_dmic_clkrate(codec, of_val32); + else + pdata->dmic1_clk_rate = DA7218_DMIC_CLK_3_0MHZ; + + if (!of_property_read_string(np, "dlg,dmic2-data-sel", &of_str)) + pdata->dmic2_data_sel = da7218_of_dmic_data_sel(codec, of_str); + else + pdata->dmic2_data_sel = DA7218_DMIC_DATA_LRISE_RFALL; + + if (!of_property_read_string(np, "dlg,dmic2-samplephase", &of_str)) + pdata->dmic2_samplephase = + da7218_of_dmic_samplephase(codec, of_str); + else + pdata->dmic2_samplephase = DA7218_DMIC_SAMPLE_ON_CLKEDGE; + + if (of_property_read_u32(np, "dlg,dmic2-clkrate-hz", &of_val32) >= 0) + pdata->dmic2_clk_rate = da7218_of_dmic_clkrate(codec, of_val32); + else + pdata->dmic2_clk_rate = DA7218_DMIC_CLK_3_0MHZ; + + if (da7218->dev_id == DA7217_DEV_ID) { + if (of_property_read_bool(np, "dlg,hp-diff-single-supply")) + pdata->hp_diff_single_supply = true; + } + + if (da7218->dev_id == DA7218_DEV_ID) { + hpldet_np = of_find_node_by_name(np, "da7218_hpldet"); + if (!hpldet_np) + return pdata; + + hpldet_pdata = devm_kzalloc(codec->dev, sizeof(*hpldet_pdata), + GFP_KERNEL); + if (!hpldet_pdata) { + dev_warn(codec->dev, + "Failed to allocate memory for hpldet pdata\n"); + of_node_put(hpldet_np); + return pdata; + } + pdata->hpldet_pdata = hpldet_pdata; + + if (of_property_read_u32(hpldet_np, "dlg,jack-rate-us", + &of_val32) >= 0) + hpldet_pdata->jack_rate = + da7218_of_jack_rate(codec, of_val32); + else + hpldet_pdata->jack_rate = DA7218_HPLDET_JACK_RATE_40US; + + if (of_property_read_u32(hpldet_np, "dlg,jack-debounce", + &of_val32) >= 0) + hpldet_pdata->jack_debounce = + da7218_of_jack_debounce(codec, of_val32); + else + hpldet_pdata->jack_debounce = + DA7218_HPLDET_JACK_DEBOUNCE_2; + + if (of_property_read_u32(hpldet_np, "dlg,jack-threshold-pct", + &of_val32) >= 0) + hpldet_pdata->jack_thr = + da7218_of_jack_thr(codec, of_val32); + else + hpldet_pdata->jack_thr = DA7218_HPLDET_JACK_THR_84PCT; + + if (of_property_read_bool(hpldet_np, "dlg,comp-inv")) + hpldet_pdata->comp_inv = true; + + if (of_property_read_bool(hpldet_np, "dlg,hyst")) + hpldet_pdata->hyst = true; + + if (of_property_read_bool(hpldet_np, "dlg,discharge")) + hpldet_pdata->discharge = true; + + of_node_put(hpldet_np); + } + + return pdata; +} + + +/* + * Codec driver functions + */ + +static int da7218_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + /* MCLK */ + if (da7218->mclk) { + ret = clk_prepare_enable(da7218->mclk); + if (ret) { + dev_err(codec->dev, + "Failed to enable mclk\n"); + return ret; + } + } + + /* Master bias */ + snd_soc_update_bits(codec, DA7218_REFERENCES, + DA7218_BIAS_EN_MASK, + DA7218_BIAS_EN_MASK); + + /* Internal LDO */ + snd_soc_update_bits(codec, DA7218_LDO_CTRL, + DA7218_LDO_EN_MASK, + DA7218_LDO_EN_MASK); + } + break; + case SND_SOC_BIAS_OFF: + /* Only disable if jack detection disabled */ + if (!da7218->jack) { + /* Internal LDO */ + snd_soc_update_bits(codec, DA7218_LDO_CTRL, + DA7218_LDO_EN_MASK, 0); + + /* Master bias */ + snd_soc_update_bits(codec, DA7218_REFERENCES, + DA7218_BIAS_EN_MASK, 0); + } + + /* MCLK */ + if (da7218->mclk) + clk_disable_unprepare(da7218->mclk); + break; + } + + return 0; +} + +static const char *da7218_supply_names[DA7218_NUM_SUPPLIES] = { + [DA7218_SUPPLY_VDD] = "VDD", + [DA7218_SUPPLY_VDDMIC] = "VDDMIC", + [DA7218_SUPPLY_VDDIO] = "VDDIO", +}; + +static int da7218_handle_supplies(struct snd_soc_codec *codec) +{ + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + struct regulator *vddio; + u8 io_voltage_lvl = DA7218_IO_VOLTAGE_LEVEL_2_5V_3_6V; + int i, ret; + + /* Get required supplies */ + for (i = 0; i < DA7218_NUM_SUPPLIES; ++i) + da7218->supplies[i].supply = da7218_supply_names[i]; + + ret = devm_regulator_bulk_get(codec->dev, DA7218_NUM_SUPPLIES, + da7218->supplies); + if (ret) { + dev_err(codec->dev, "Failed to get supplies\n"); + return ret; + } + + /* Determine VDDIO voltage provided */ + vddio = da7218->supplies[DA7218_SUPPLY_VDDIO].consumer; + ret = regulator_get_voltage(vddio); + if (ret < 1500000) + dev_warn(codec->dev, "Invalid VDDIO voltage\n"); + else if (ret < 2500000) + io_voltage_lvl = DA7218_IO_VOLTAGE_LEVEL_1_5V_2_5V; + + /* Enable main supplies */ + ret = regulator_bulk_enable(DA7218_NUM_SUPPLIES, da7218->supplies); + if (ret) { + dev_err(codec->dev, "Failed to enable supplies\n"); + return ret; + } + + /* Ensure device in active mode */ + snd_soc_write(codec, DA7218_SYSTEM_ACTIVE, DA7218_SYSTEM_ACTIVE_MASK); + + /* Update IO voltage level range */ + snd_soc_write(codec, DA7218_IO_CTRL, io_voltage_lvl); + + return 0; +} + +static void da7218_handle_pdata(struct snd_soc_codec *codec) +{ + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + struct da7218_pdata *pdata = da7218->pdata; + + if (pdata) { + u8 micbias_lvl = 0, dmic_cfg = 0; + + /* Mic Bias voltages */ + switch (pdata->micbias1_lvl) { + case DA7218_MICBIAS_1_2V: + micbias_lvl |= DA7218_MICBIAS_1_LP_MODE_MASK; + break; + case DA7218_MICBIAS_1_6V: + case DA7218_MICBIAS_1_8V: + case DA7218_MICBIAS_2_0V: + case DA7218_MICBIAS_2_2V: + case DA7218_MICBIAS_2_4V: + case DA7218_MICBIAS_2_6V: + case DA7218_MICBIAS_2_8V: + case DA7218_MICBIAS_3_0V: + micbias_lvl |= (pdata->micbias1_lvl << + DA7218_MICBIAS_1_LEVEL_SHIFT); + break; + } + + switch (pdata->micbias2_lvl) { + case DA7218_MICBIAS_1_2V: + micbias_lvl |= DA7218_MICBIAS_2_LP_MODE_MASK; + break; + case DA7218_MICBIAS_1_6V: + case DA7218_MICBIAS_1_8V: + case DA7218_MICBIAS_2_0V: + case DA7218_MICBIAS_2_2V: + case DA7218_MICBIAS_2_4V: + case DA7218_MICBIAS_2_6V: + case DA7218_MICBIAS_2_8V: + case DA7218_MICBIAS_3_0V: + micbias_lvl |= (pdata->micbias2_lvl << + DA7218_MICBIAS_2_LEVEL_SHIFT); + break; + } + + snd_soc_write(codec, DA7218_MICBIAS_CTRL, micbias_lvl); + + /* Mic */ + switch (pdata->mic1_amp_in_sel) { + case DA7218_MIC_AMP_IN_SEL_DIFF: + case DA7218_MIC_AMP_IN_SEL_SE_P: + case DA7218_MIC_AMP_IN_SEL_SE_N: + snd_soc_write(codec, DA7218_MIC_1_SELECT, + pdata->mic1_amp_in_sel); + break; + } + + switch (pdata->mic2_amp_in_sel) { + case DA7218_MIC_AMP_IN_SEL_DIFF: + case DA7218_MIC_AMP_IN_SEL_SE_P: + case DA7218_MIC_AMP_IN_SEL_SE_N: + snd_soc_write(codec, DA7218_MIC_2_SELECT, + pdata->mic2_amp_in_sel); + break; + } + + /* DMic */ + switch (pdata->dmic1_data_sel) { + case DA7218_DMIC_DATA_LFALL_RRISE: + case DA7218_DMIC_DATA_LRISE_RFALL: + dmic_cfg |= (pdata->dmic1_data_sel << + DA7218_DMIC_1_DATA_SEL_SHIFT); + break; + } + + switch (pdata->dmic1_samplephase) { + case DA7218_DMIC_SAMPLE_ON_CLKEDGE: + case DA7218_DMIC_SAMPLE_BETWEEN_CLKEDGE: + dmic_cfg |= (pdata->dmic1_samplephase << + DA7218_DMIC_1_SAMPLEPHASE_SHIFT); + break; + } + + switch (pdata->dmic1_clk_rate) { + case DA7218_DMIC_CLK_3_0MHZ: + case DA7218_DMIC_CLK_1_5MHZ: + dmic_cfg |= (pdata->dmic1_clk_rate << + DA7218_DMIC_1_CLK_RATE_SHIFT); + break; + } + + snd_soc_update_bits(codec, DA7218_DMIC_1_CTRL, + DA7218_DMIC_1_DATA_SEL_MASK | + DA7218_DMIC_1_SAMPLEPHASE_MASK | + DA7218_DMIC_1_CLK_RATE_MASK, dmic_cfg); + + dmic_cfg = 0; + switch (pdata->dmic2_data_sel) { + case DA7218_DMIC_DATA_LFALL_RRISE: + case DA7218_DMIC_DATA_LRISE_RFALL: + dmic_cfg |= (pdata->dmic2_data_sel << + DA7218_DMIC_2_DATA_SEL_SHIFT); + break; + } + + switch (pdata->dmic2_samplephase) { + case DA7218_DMIC_SAMPLE_ON_CLKEDGE: + case DA7218_DMIC_SAMPLE_BETWEEN_CLKEDGE: + dmic_cfg |= (pdata->dmic2_samplephase << + DA7218_DMIC_2_SAMPLEPHASE_SHIFT); + break; + } + + switch (pdata->dmic2_clk_rate) { + case DA7218_DMIC_CLK_3_0MHZ: + case DA7218_DMIC_CLK_1_5MHZ: + dmic_cfg |= (pdata->dmic2_clk_rate << + DA7218_DMIC_2_CLK_RATE_SHIFT); + break; + } + + snd_soc_update_bits(codec, DA7218_DMIC_2_CTRL, + DA7218_DMIC_2_DATA_SEL_MASK | + DA7218_DMIC_2_SAMPLEPHASE_MASK | + DA7218_DMIC_2_CLK_RATE_MASK, dmic_cfg); + + /* DA7217 Specific */ + if (da7218->dev_id == DA7217_DEV_ID) { + da7218->hp_single_supply = + pdata->hp_diff_single_supply; + + if (da7218->hp_single_supply) { + snd_soc_write(codec, DA7218_HP_DIFF_UNLOCK, + DA7218_HP_DIFF_UNLOCK_VAL); + snd_soc_update_bits(codec, DA7218_HP_DIFF_CTRL, + DA7218_HP_AMP_SINGLE_SUPPLY_EN_MASK, + DA7218_HP_AMP_SINGLE_SUPPLY_EN_MASK); + } + } + + /* DA7218 Specific */ + if ((da7218->dev_id == DA7218_DEV_ID) && + (pdata->hpldet_pdata)) { + struct da7218_hpldet_pdata *hpldet_pdata = + pdata->hpldet_pdata; + u8 hpldet_cfg = 0; + + switch (hpldet_pdata->jack_rate) { + case DA7218_HPLDET_JACK_RATE_5US: + case DA7218_HPLDET_JACK_RATE_10US: + case DA7218_HPLDET_JACK_RATE_20US: + case DA7218_HPLDET_JACK_RATE_40US: + case DA7218_HPLDET_JACK_RATE_80US: + case DA7218_HPLDET_JACK_RATE_160US: + case DA7218_HPLDET_JACK_RATE_320US: + case DA7218_HPLDET_JACK_RATE_640US: + hpldet_cfg |= + (hpldet_pdata->jack_rate << + DA7218_HPLDET_JACK_RATE_SHIFT); + break; + } + + switch (hpldet_pdata->jack_debounce) { + case DA7218_HPLDET_JACK_DEBOUNCE_OFF: + case DA7218_HPLDET_JACK_DEBOUNCE_2: + case DA7218_HPLDET_JACK_DEBOUNCE_3: + case DA7218_HPLDET_JACK_DEBOUNCE_4: + hpldet_cfg |= + (hpldet_pdata->jack_debounce << + DA7218_HPLDET_JACK_DEBOUNCE_SHIFT); + break; + } + + switch (hpldet_pdata->jack_thr) { + case DA7218_HPLDET_JACK_THR_84PCT: + case DA7218_HPLDET_JACK_THR_88PCT: + case DA7218_HPLDET_JACK_THR_92PCT: + case DA7218_HPLDET_JACK_THR_96PCT: + hpldet_cfg |= + (hpldet_pdata->jack_thr << + DA7218_HPLDET_JACK_THR_SHIFT); + break; + } + snd_soc_update_bits(codec, DA7218_HPLDET_JACK, + DA7218_HPLDET_JACK_RATE_MASK | + DA7218_HPLDET_JACK_DEBOUNCE_MASK | + DA7218_HPLDET_JACK_THR_MASK, + hpldet_cfg); + + hpldet_cfg = 0; + if (hpldet_pdata->comp_inv) + hpldet_cfg |= DA7218_HPLDET_COMP_INV_MASK; + + if (hpldet_pdata->hyst) + hpldet_cfg |= DA7218_HPLDET_HYST_EN_MASK; + + if (hpldet_pdata->discharge) + hpldet_cfg |= DA7218_HPLDET_DISCHARGE_EN_MASK; + + snd_soc_write(codec, DA7218_HPLDET_CTRL, hpldet_cfg); + } + } +} + +static int da7218_probe(struct snd_soc_codec *codec) +{ + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + int ret; + + /* Regulator configuration */ + ret = da7218_handle_supplies(codec); + if (ret) + return ret; + + /* Handle DT/Platform data */ + if (codec->dev->of_node) + da7218->pdata = da7218_of_to_pdata(codec); + else + da7218->pdata = dev_get_platdata(codec->dev); + + da7218_handle_pdata(codec); + + /* Check if MCLK provided, if not the clock is NULL */ + da7218->mclk = devm_clk_get(codec->dev, "mclk"); + if (IS_ERR(da7218->mclk)) { + if (PTR_ERR(da7218->mclk) != -ENOENT) { + ret = PTR_ERR(da7218->mclk); + goto err_disable_reg; + } else { + da7218->mclk = NULL; + } + } + + /* Default PC to free-running */ + snd_soc_write(codec, DA7218_PC_COUNT, DA7218_PC_FREERUN_MASK); + + /* + * Default Output Filter mixers to off otherwise DAPM will power + * Mic to HP passthrough paths by default at startup. + */ + snd_soc_write(codec, DA7218_DROUTING_OUTFILT_1L, 0); + snd_soc_write(codec, DA7218_DROUTING_OUTFILT_1R, 0); + + /* Default CP to normal load, power mode */ + snd_soc_update_bits(codec, DA7218_CP_CTRL, + DA7218_CP_SMALL_SWITCH_FREQ_EN_MASK, 0); + + /* Default gain ramping */ + snd_soc_update_bits(codec, DA7218_MIXIN_1_CTRL, + DA7218_MIXIN_1_AMP_RAMP_EN_MASK, + DA7218_MIXIN_1_AMP_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7218_MIXIN_2_CTRL, + DA7218_MIXIN_2_AMP_RAMP_EN_MASK, + DA7218_MIXIN_2_AMP_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7218_IN_1L_FILTER_CTRL, + DA7218_IN_1L_RAMP_EN_MASK, + DA7218_IN_1L_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7218_IN_1R_FILTER_CTRL, + DA7218_IN_1R_RAMP_EN_MASK, + DA7218_IN_1R_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7218_IN_2L_FILTER_CTRL, + DA7218_IN_2L_RAMP_EN_MASK, + DA7218_IN_2L_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7218_IN_2R_FILTER_CTRL, + DA7218_IN_2R_RAMP_EN_MASK, + DA7218_IN_2R_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7218_DGS_GAIN_CTRL, + DA7218_DGS_RAMP_EN_MASK, DA7218_DGS_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7218_OUT_1L_FILTER_CTRL, + DA7218_OUT_1L_RAMP_EN_MASK, + DA7218_OUT_1L_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7218_OUT_1R_FILTER_CTRL, + DA7218_OUT_1R_RAMP_EN_MASK, + DA7218_OUT_1R_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7218_HP_L_CTRL, + DA7218_HP_L_AMP_RAMP_EN_MASK, + DA7218_HP_L_AMP_RAMP_EN_MASK); + snd_soc_update_bits(codec, DA7218_HP_R_CTRL, + DA7218_HP_R_AMP_RAMP_EN_MASK, + DA7218_HP_R_AMP_RAMP_EN_MASK); + + /* Default infinite tone gen, start/stop by Kcontrol */ + snd_soc_write(codec, DA7218_TONE_GEN_CYCLES, DA7218_BEEP_CYCLES_MASK); + + /* DA7217 specific config */ + if (da7218->dev_id == DA7217_DEV_ID) { + snd_soc_update_bits(codec, DA7218_HP_DIFF_CTRL, + DA7218_HP_AMP_DIFF_MODE_EN_MASK, + DA7218_HP_AMP_DIFF_MODE_EN_MASK); + + /* Only DA7218 supports HP detect, mask off for DA7217 */ + snd_soc_write(codec, DA7218_EVENT_MASK, + DA7218_HPLDET_JACK_EVENT_IRQ_MSK_MASK); + } + + if (da7218->irq) { + ret = devm_request_threaded_irq(codec->dev, da7218->irq, NULL, + da7218_irq_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "da7218", codec); + if (ret != 0) { + dev_err(codec->dev, "Failed to request IRQ %d: %d\n", + da7218->irq, ret); + goto err_disable_reg; + } + + } + + return 0; + +err_disable_reg: + regulator_bulk_disable(DA7218_NUM_SUPPLIES, da7218->supplies); + + return ret; +} + +static int da7218_remove(struct snd_soc_codec *codec) +{ + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + + regulator_bulk_disable(DA7218_NUM_SUPPLIES, da7218->supplies); + + return 0; +} + +#ifdef CONFIG_PM +static int da7218_suspend(struct snd_soc_codec *codec) +{ + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + + da7218_set_bias_level(codec, SND_SOC_BIAS_OFF); + + /* Put device into standby mode if jack detection disabled */ + if (!da7218->jack) + snd_soc_write(codec, DA7218_SYSTEM_ACTIVE, 0); + + return 0; +} + +static int da7218_resume(struct snd_soc_codec *codec) +{ + struct da7218_priv *da7218 = snd_soc_codec_get_drvdata(codec); + + /* Put device into active mode if previously moved to standby */ + if (!da7218->jack) + snd_soc_write(codec, DA7218_SYSTEM_ACTIVE, + DA7218_SYSTEM_ACTIVE_MASK); + + da7218_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} +#else +#define da7218_suspend NULL +#define da7218_resume NULL +#endif + +static struct snd_soc_codec_driver soc_codec_dev_da7218 = { + .probe = da7218_probe, + .remove = da7218_remove, + .suspend = da7218_suspend, + .resume = da7218_resume, + .set_bias_level = da7218_set_bias_level, + + .controls = da7218_snd_controls, + .num_controls = ARRAY_SIZE(da7218_snd_controls), + + .dapm_widgets = da7218_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da7218_dapm_widgets), + .dapm_routes = da7218_audio_map, + .num_dapm_routes = ARRAY_SIZE(da7218_audio_map), +}; + + +/* + * Regmap configs + */ + +static struct reg_default da7218_reg_defaults[] = { + { DA7218_SYSTEM_ACTIVE, 0x00 }, + { DA7218_CIF_CTRL, 0x00 }, + { DA7218_SPARE1, 0x00 }, + { DA7218_SR, 0xAA }, + { DA7218_PC_COUNT, 0x02 }, + { DA7218_GAIN_RAMP_CTRL, 0x00 }, + { DA7218_CIF_TIMEOUT_CTRL, 0x01 }, + { DA7218_SYSTEM_MODES_INPUT, 0x00 }, + { DA7218_SYSTEM_MODES_OUTPUT, 0x00 }, + { DA7218_IN_1L_FILTER_CTRL, 0x00 }, + { DA7218_IN_1R_FILTER_CTRL, 0x00 }, + { DA7218_IN_2L_FILTER_CTRL, 0x00 }, + { DA7218_IN_2R_FILTER_CTRL, 0x00 }, + { DA7218_OUT_1L_FILTER_CTRL, 0x40 }, + { DA7218_OUT_1R_FILTER_CTRL, 0x40 }, + { DA7218_OUT_1_HPF_FILTER_CTRL, 0x80 }, + { DA7218_OUT_1_EQ_12_FILTER_CTRL, 0x77 }, + { DA7218_OUT_1_EQ_34_FILTER_CTRL, 0x77 }, + { DA7218_OUT_1_EQ_5_FILTER_CTRL, 0x07 }, + { DA7218_OUT_1_BIQ_5STAGE_CTRL, 0x40 }, + { DA7218_OUT_1_BIQ_5STAGE_DATA, 0x00 }, + { DA7218_OUT_1_BIQ_5STAGE_ADDR, 0x00 }, + { DA7218_MIXIN_1_CTRL, 0x48 }, + { DA7218_MIXIN_1_GAIN, 0x03 }, + { DA7218_MIXIN_2_CTRL, 0x48 }, + { DA7218_MIXIN_2_GAIN, 0x03 }, + { DA7218_ALC_CTRL1, 0x00 }, + { DA7218_ALC_CTRL2, 0x00 }, + { DA7218_ALC_CTRL3, 0x00 }, + { DA7218_ALC_NOISE, 0x3F }, + { DA7218_ALC_TARGET_MIN, 0x3F }, + { DA7218_ALC_TARGET_MAX, 0x00 }, + { DA7218_ALC_GAIN_LIMITS, 0xFF }, + { DA7218_ALC_ANA_GAIN_LIMITS, 0x71 }, + { DA7218_ALC_ANTICLIP_CTRL, 0x00 }, + { DA7218_AGS_ENABLE, 0x00 }, + { DA7218_AGS_TRIGGER, 0x09 }, + { DA7218_AGS_ATT_MAX, 0x00 }, + { DA7218_AGS_TIMEOUT, 0x00 }, + { DA7218_AGS_ANTICLIP_CTRL, 0x00 }, + { DA7218_ENV_TRACK_CTRL, 0x00 }, + { DA7218_LVL_DET_CTRL, 0x00 }, + { DA7218_LVL_DET_LEVEL, 0x7F }, + { DA7218_DGS_TRIGGER, 0x24 }, + { DA7218_DGS_ENABLE, 0x00 }, + { DA7218_DGS_RISE_FALL, 0x50 }, + { DA7218_DGS_SYNC_DELAY, 0xA3 }, + { DA7218_DGS_SYNC_DELAY2, 0x31 }, + { DA7218_DGS_SYNC_DELAY3, 0x11 }, + { DA7218_DGS_LEVELS, 0x01 }, + { DA7218_DGS_GAIN_CTRL, 0x74 }, + { DA7218_DROUTING_OUTDAI_1L, 0x01 }, + { DA7218_DMIX_OUTDAI_1L_INFILT_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1L_INFILT_1R_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1L_INFILT_2L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1L_INFILT_2R_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1L_TONEGEN_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1L_INDAI_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1L_INDAI_1R_GAIN, 0x1C }, + { DA7218_DROUTING_OUTDAI_1R, 0x04 }, + { DA7218_DMIX_OUTDAI_1R_INFILT_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1R_INFILT_1R_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1R_INFILT_2L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1R_INFILT_2R_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1R_TONEGEN_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1R_INDAI_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1R_INDAI_1R_GAIN, 0x1C }, + { DA7218_DROUTING_OUTFILT_1L, 0x01 }, + { DA7218_DMIX_OUTFILT_1L_INFILT_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1L_INFILT_1R_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1L_INFILT_2L_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1L_INFILT_2R_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1L_TONEGEN_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1L_INDAI_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1L_INDAI_1R_GAIN, 0x1C }, + { DA7218_DROUTING_OUTFILT_1R, 0x04 }, + { DA7218_DMIX_OUTFILT_1R_INFILT_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1R_INFILT_1R_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1R_INFILT_2L_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1R_INFILT_2R_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1R_TONEGEN_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1R_INDAI_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1R_INDAI_1R_GAIN, 0x1C }, + { DA7218_DROUTING_OUTDAI_2L, 0x04 }, + { DA7218_DMIX_OUTDAI_2L_INFILT_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2L_INFILT_1R_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2L_INFILT_2L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2L_INFILT_2R_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2L_TONEGEN_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2L_INDAI_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2L_INDAI_1R_GAIN, 0x1C }, + { DA7218_DROUTING_OUTDAI_2R, 0x08 }, + { DA7218_DMIX_OUTDAI_2R_INFILT_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2R_INFILT_1R_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2R_INFILT_2L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2R_INFILT_2R_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2R_TONEGEN_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2R_INDAI_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2R_INDAI_1R_GAIN, 0x1C }, + { DA7218_DAI_CTRL, 0x28 }, + { DA7218_DAI_TDM_CTRL, 0x40 }, + { DA7218_DAI_OFFSET_LOWER, 0x00 }, + { DA7218_DAI_OFFSET_UPPER, 0x00 }, + { DA7218_DAI_CLK_MODE, 0x01 }, + { DA7218_PLL_CTRL, 0x04 }, + { DA7218_PLL_FRAC_TOP, 0x00 }, + { DA7218_PLL_FRAC_BOT, 0x00 }, + { DA7218_PLL_INTEGER, 0x20 }, + { DA7218_DAC_NG_CTRL, 0x00 }, + { DA7218_DAC_NG_SETUP_TIME, 0x00 }, + { DA7218_DAC_NG_OFF_THRESH, 0x00 }, + { DA7218_DAC_NG_ON_THRESH, 0x00 }, + { DA7218_TONE_GEN_CFG2, 0x00 }, + { DA7218_TONE_GEN_FREQ1_L, 0x55 }, + { DA7218_TONE_GEN_FREQ1_U, 0x15 }, + { DA7218_TONE_GEN_FREQ2_L, 0x00 }, + { DA7218_TONE_GEN_FREQ2_U, 0x40 }, + { DA7218_TONE_GEN_CYCLES, 0x00 }, + { DA7218_TONE_GEN_ON_PER, 0x02 }, + { DA7218_TONE_GEN_OFF_PER, 0x01 }, + { DA7218_CP_CTRL, 0x60 }, + { DA7218_CP_DELAY, 0x11 }, + { DA7218_CP_VOL_THRESHOLD1, 0x0E }, + { DA7218_MIC_1_CTRL, 0x40 }, + { DA7218_MIC_1_GAIN, 0x01 }, + { DA7218_MIC_1_SELECT, 0x00 }, + { DA7218_MIC_2_CTRL, 0x40 }, + { DA7218_MIC_2_GAIN, 0x01 }, + { DA7218_MIC_2_SELECT, 0x00 }, + { DA7218_IN_1_HPF_FILTER_CTRL, 0x80 }, + { DA7218_IN_2_HPF_FILTER_CTRL, 0x80 }, + { DA7218_ADC_1_CTRL, 0x07 }, + { DA7218_ADC_2_CTRL, 0x07 }, + { DA7218_MIXOUT_L_CTRL, 0x00 }, + { DA7218_MIXOUT_L_GAIN, 0x03 }, + { DA7218_MIXOUT_R_CTRL, 0x00 }, + { DA7218_MIXOUT_R_GAIN, 0x03 }, + { DA7218_HP_L_CTRL, 0x40 }, + { DA7218_HP_L_GAIN, 0x3B }, + { DA7218_HP_R_CTRL, 0x40 }, + { DA7218_HP_R_GAIN, 0x3B }, + { DA7218_HP_DIFF_CTRL, 0x00 }, + { DA7218_HP_DIFF_UNLOCK, 0xC3 }, + { DA7218_HPLDET_JACK, 0x0B }, + { DA7218_HPLDET_CTRL, 0x00 }, + { DA7218_REFERENCES, 0x08 }, + { DA7218_IO_CTRL, 0x00 }, + { DA7218_LDO_CTRL, 0x00 }, + { DA7218_SIDETONE_CTRL, 0x40 }, + { DA7218_SIDETONE_IN_SELECT, 0x00 }, + { DA7218_SIDETONE_GAIN, 0x1C }, + { DA7218_DROUTING_ST_OUTFILT_1L, 0x01 }, + { DA7218_DROUTING_ST_OUTFILT_1R, 0x02 }, + { DA7218_SIDETONE_BIQ_3STAGE_DATA, 0x00 }, + { DA7218_SIDETONE_BIQ_3STAGE_ADDR, 0x00 }, + { DA7218_EVENT_MASK, 0x00 }, + { DA7218_DMIC_1_CTRL, 0x00 }, + { DA7218_DMIC_2_CTRL, 0x00 }, + { DA7218_IN_1L_GAIN, 0x6F }, + { DA7218_IN_1R_GAIN, 0x6F }, + { DA7218_IN_2L_GAIN, 0x6F }, + { DA7218_IN_2R_GAIN, 0x6F }, + { DA7218_OUT_1L_GAIN, 0x6F }, + { DA7218_OUT_1R_GAIN, 0x6F }, + { DA7218_MICBIAS_CTRL, 0x00 }, + { DA7218_MICBIAS_EN, 0x00 }, +}; + +static bool da7218_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DA7218_STATUS1: + case DA7218_SOFT_RESET: + case DA7218_SYSTEM_STATUS: + case DA7218_CALIB_CTRL: + case DA7218_CALIB_OFFSET_AUTO_M_1: + case DA7218_CALIB_OFFSET_AUTO_U_1: + case DA7218_CALIB_OFFSET_AUTO_M_2: + case DA7218_CALIB_OFFSET_AUTO_U_2: + case DA7218_PLL_STATUS: + case DA7218_PLL_REFOSC_CAL: + case DA7218_TONE_GEN_CFG1: + case DA7218_ADC_MODE: + case DA7218_HP_SNGL_CTRL: + case DA7218_HPLDET_TEST: + case DA7218_EVENT_STATUS: + case DA7218_EVENT: + return true; + default: + return false; + } +} + +static const struct regmap_config da7218_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = DA7218_MICBIAS_EN, + .reg_defaults = da7218_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(da7218_reg_defaults), + .volatile_reg = da7218_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + + +/* + * I2C layer + */ + +static int da7218_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct da7218_priv *da7218; + int ret; + + da7218 = devm_kzalloc(&i2c->dev, sizeof(struct da7218_priv), + GFP_KERNEL); + if (!da7218) + return -ENOMEM; + + i2c_set_clientdata(i2c, da7218); + + if (i2c->dev.of_node) + da7218->dev_id = da7218_of_get_id(&i2c->dev); + else + da7218->dev_id = id->driver_data; + + if ((da7218->dev_id != DA7217_DEV_ID) && + (da7218->dev_id != DA7218_DEV_ID)) { + dev_err(&i2c->dev, "Invalid device Id\n"); + return -EINVAL; + } + + da7218->irq = i2c->irq; + + da7218->regmap = devm_regmap_init_i2c(i2c, &da7218_regmap_config); + if (IS_ERR(da7218->regmap)) { + ret = PTR_ERR(da7218->regmap); + dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_da7218, &da7218_dai, 1); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register da7218 codec: %d\n", + ret); + } + return ret; +} + +static int da7218_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id da7218_i2c_id[] = { + { "da7217", DA7217_DEV_ID }, + { "da7218", DA7218_DEV_ID }, + { } +}; +MODULE_DEVICE_TABLE(i2c, da7218_i2c_id); + +static struct i2c_driver da7218_i2c_driver = { + .driver = { + .name = "da7218", + .of_match_table = of_match_ptr(da7218_of_match), + }, + .probe = da7218_i2c_probe, + .remove = da7218_i2c_remove, + .id_table = da7218_i2c_id, +}; + +module_i2c_driver(da7218_i2c_driver); + +MODULE_DESCRIPTION("ASoC DA7218 Codec driver"); +MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/da7218.h b/sound/soc/codecs/da7218.h new file mode 100644 index 000000000000..c2c59049a2ad --- /dev/null +++ b/sound/soc/codecs/da7218.h @@ -0,0 +1,1414 @@ +/* + * da7218.h - DA7218 ALSA SoC Codec Driver + * + * Copyright (c) 2015 Dialog Semiconductor + * + * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef _DA7218_H +#define _DA7218_H + +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <sound/da7218.h> + + +/* + * Registers + */ +#define DA7218_SYSTEM_ACTIVE 0x0 +#define DA7218_CIF_CTRL 0x1 +#define DA7218_CHIP_ID1 0x4 +#define DA7218_CHIP_ID2 0x5 +#define DA7218_CHIP_REVISION 0x6 +#define DA7218_SPARE1 0x7 +#define DA7218_STATUS1 0x8 +#define DA7218_SOFT_RESET 0x9 +#define DA7218_SR 0xB +#define DA7218_PC_COUNT 0xC +#define DA7218_GAIN_RAMP_CTRL 0xD +#define DA7218_CIF_TIMEOUT_CTRL 0x10 +#define DA7218_SYSTEM_MODES_INPUT 0x14 +#define DA7218_SYSTEM_MODES_OUTPUT 0x15 +#define DA7218_SYSTEM_STATUS 0x16 +#define DA7218_IN_1L_FILTER_CTRL 0x18 +#define DA7218_IN_1R_FILTER_CTRL 0x19 +#define DA7218_IN_2L_FILTER_CTRL 0x1A +#define DA7218_IN_2R_FILTER_CTRL 0x1B +#define DA7218_OUT_1L_FILTER_CTRL 0x20 +#define DA7218_OUT_1R_FILTER_CTRL 0x21 +#define DA7218_OUT_1_HPF_FILTER_CTRL 0x24 +#define DA7218_OUT_1_EQ_12_FILTER_CTRL 0x25 +#define DA7218_OUT_1_EQ_34_FILTER_CTRL 0x26 +#define DA7218_OUT_1_EQ_5_FILTER_CTRL 0x27 +#define DA7218_OUT_1_BIQ_5STAGE_CTRL 0x28 +#define DA7218_OUT_1_BIQ_5STAGE_DATA 0x29 +#define DA7218_OUT_1_BIQ_5STAGE_ADDR 0x2A +#define DA7218_MIXIN_1_CTRL 0x2C +#define DA7218_MIXIN_1_GAIN 0x2D +#define DA7218_MIXIN_2_CTRL 0x2E +#define DA7218_MIXIN_2_GAIN 0x2F +#define DA7218_ALC_CTRL1 0x30 +#define DA7218_ALC_CTRL2 0x31 +#define DA7218_ALC_CTRL3 0x32 +#define DA7218_ALC_NOISE 0x33 +#define DA7218_ALC_TARGET_MIN 0x34 +#define DA7218_ALC_TARGET_MAX 0x35 +#define DA7218_ALC_GAIN_LIMITS 0x36 +#define DA7218_ALC_ANA_GAIN_LIMITS 0x37 +#define DA7218_ALC_ANTICLIP_CTRL 0x38 +#define DA7218_AGS_ENABLE 0x3C +#define DA7218_AGS_TRIGGER 0x3D +#define DA7218_AGS_ATT_MAX 0x3E +#define DA7218_AGS_TIMEOUT 0x3F +#define DA7218_AGS_ANTICLIP_CTRL 0x40 +#define DA7218_CALIB_CTRL 0x44 +#define DA7218_CALIB_OFFSET_AUTO_M_1 0x45 +#define DA7218_CALIB_OFFSET_AUTO_U_1 0x46 +#define DA7218_CALIB_OFFSET_AUTO_M_2 0x47 +#define DA7218_CALIB_OFFSET_AUTO_U_2 0x48 +#define DA7218_ENV_TRACK_CTRL 0x4C +#define DA7218_LVL_DET_CTRL 0x50 +#define DA7218_LVL_DET_LEVEL 0x51 +#define DA7218_DGS_TRIGGER 0x54 +#define DA7218_DGS_ENABLE 0x55 +#define DA7218_DGS_RISE_FALL 0x56 +#define DA7218_DGS_SYNC_DELAY 0x57 +#define DA7218_DGS_SYNC_DELAY2 0x58 +#define DA7218_DGS_SYNC_DELAY3 0x59 +#define DA7218_DGS_LEVELS 0x5A +#define DA7218_DGS_GAIN_CTRL 0x5B +#define DA7218_DROUTING_OUTDAI_1L 0x5C +#define DA7218_DMIX_OUTDAI_1L_INFILT_1L_GAIN 0x5D +#define DA7218_DMIX_OUTDAI_1L_INFILT_1R_GAIN 0x5E +#define DA7218_DMIX_OUTDAI_1L_INFILT_2L_GAIN 0x5F +#define DA7218_DMIX_OUTDAI_1L_INFILT_2R_GAIN 0x60 +#define DA7218_DMIX_OUTDAI_1L_TONEGEN_GAIN 0x61 +#define DA7218_DMIX_OUTDAI_1L_INDAI_1L_GAIN 0x62 +#define DA7218_DMIX_OUTDAI_1L_INDAI_1R_GAIN 0x63 +#define DA7218_DROUTING_OUTDAI_1R 0x64 +#define DA7218_DMIX_OUTDAI_1R_INFILT_1L_GAIN 0x65 +#define DA7218_DMIX_OUTDAI_1R_INFILT_1R_GAIN 0x66 +#define DA7218_DMIX_OUTDAI_1R_INFILT_2L_GAIN 0x67 +#define DA7218_DMIX_OUTDAI_1R_INFILT_2R_GAIN 0x68 +#define DA7218_DMIX_OUTDAI_1R_TONEGEN_GAIN 0x69 +#define DA7218_DMIX_OUTDAI_1R_INDAI_1L_GAIN 0x6A +#define DA7218_DMIX_OUTDAI_1R_INDAI_1R_GAIN 0x6B +#define DA7218_DROUTING_OUTFILT_1L 0x6C +#define DA7218_DMIX_OUTFILT_1L_INFILT_1L_GAIN 0x6D +#define DA7218_DMIX_OUTFILT_1L_INFILT_1R_GAIN 0x6E +#define DA7218_DMIX_OUTFILT_1L_INFILT_2L_GAIN 0x6F +#define DA7218_DMIX_OUTFILT_1L_INFILT_2R_GAIN 0x70 +#define DA7218_DMIX_OUTFILT_1L_TONEGEN_GAIN 0x71 +#define DA7218_DMIX_OUTFILT_1L_INDAI_1L_GAIN 0x72 +#define DA7218_DMIX_OUTFILT_1L_INDAI_1R_GAIN 0x73 +#define DA7218_DROUTING_OUTFILT_1R 0x74 +#define DA7218_DMIX_OUTFILT_1R_INFILT_1L_GAIN 0x75 +#define DA7218_DMIX_OUTFILT_1R_INFILT_1R_GAIN 0x76 +#define DA7218_DMIX_OUTFILT_1R_INFILT_2L_GAIN 0x77 +#define DA7218_DMIX_OUTFILT_1R_INFILT_2R_GAIN 0x78 +#define DA7218_DMIX_OUTFILT_1R_TONEGEN_GAIN 0x79 +#define DA7218_DMIX_OUTFILT_1R_INDAI_1L_GAIN 0x7A +#define DA7218_DMIX_OUTFILT_1R_INDAI_1R_GAIN 0x7B +#define DA7218_DROUTING_OUTDAI_2L 0x7C +#define DA7218_DMIX_OUTDAI_2L_INFILT_1L_GAIN 0x7D +#define DA7218_DMIX_OUTDAI_2L_INFILT_1R_GAIN 0x7E +#define DA7218_DMIX_OUTDAI_2L_INFILT_2L_GAIN 0x7F +#define DA7218_DMIX_OUTDAI_2L_INFILT_2R_GAIN 0x80 +#define DA7218_DMIX_OUTDAI_2L_TONEGEN_GAIN 0x81 +#define DA7218_DMIX_OUTDAI_2L_INDAI_1L_GAIN 0x82 +#define DA7218_DMIX_OUTDAI_2L_INDAI_1R_GAIN 0x83 +#define DA7218_DROUTING_OUTDAI_2R 0x84 +#define DA7218_DMIX_OUTDAI_2R_INFILT_1L_GAIN 0x85 +#define DA7218_DMIX_OUTDAI_2R_INFILT_1R_GAIN 0x86 +#define DA7218_DMIX_OUTDAI_2R_INFILT_2L_GAIN 0x87 +#define DA7218_DMIX_OUTDAI_2R_INFILT_2R_GAIN 0x88 +#define DA7218_DMIX_OUTDAI_2R_TONEGEN_GAIN 0x89 +#define DA7218_DMIX_OUTDAI_2R_INDAI_1L_GAIN 0x8A +#define DA7218_DMIX_OUTDAI_2R_INDAI_1R_GAIN 0x8B +#define DA7218_DAI_CTRL 0x8C +#define DA7218_DAI_TDM_CTRL 0x8D +#define DA7218_DAI_OFFSET_LOWER 0x8E +#define DA7218_DAI_OFFSET_UPPER 0x8F +#define DA7218_DAI_CLK_MODE 0x90 +#define DA7218_PLL_CTRL 0x91 +#define DA7218_PLL_FRAC_TOP 0x92 +#define DA7218_PLL_FRAC_BOT 0x93 +#define DA7218_PLL_INTEGER 0x94 +#define DA7218_PLL_STATUS 0x95 +#define DA7218_PLL_REFOSC_CAL 0x98 +#define DA7218_DAC_NG_CTRL 0x9C +#define DA7218_DAC_NG_SETUP_TIME 0x9D +#define DA7218_DAC_NG_OFF_THRESH 0x9E +#define DA7218_DAC_NG_ON_THRESH 0x9F +#define DA7218_TONE_GEN_CFG1 0xA0 +#define DA7218_TONE_GEN_CFG2 0xA1 +#define DA7218_TONE_GEN_FREQ1_L 0xA2 +#define DA7218_TONE_GEN_FREQ1_U 0xA3 +#define DA7218_TONE_GEN_FREQ2_L 0xA4 +#define DA7218_TONE_GEN_FREQ2_U 0xA5 +#define DA7218_TONE_GEN_CYCLES 0xA6 +#define DA7218_TONE_GEN_ON_PER 0xA7 +#define DA7218_TONE_GEN_OFF_PER 0xA8 +#define DA7218_CP_CTRL 0xAC +#define DA7218_CP_DELAY 0xAD +#define DA7218_CP_VOL_THRESHOLD1 0xAE +#define DA7218_MIC_1_CTRL 0xB4 +#define DA7218_MIC_1_GAIN 0xB5 +#define DA7218_MIC_1_SELECT 0xB7 +#define DA7218_MIC_2_CTRL 0xB8 +#define DA7218_MIC_2_GAIN 0xB9 +#define DA7218_MIC_2_SELECT 0xBB +#define DA7218_IN_1_HPF_FILTER_CTRL 0xBC +#define DA7218_IN_2_HPF_FILTER_CTRL 0xBD +#define DA7218_ADC_1_CTRL 0xC0 +#define DA7218_ADC_2_CTRL 0xC1 +#define DA7218_ADC_MODE 0xC2 +#define DA7218_MIXOUT_L_CTRL 0xCC +#define DA7218_MIXOUT_L_GAIN 0xCD +#define DA7218_MIXOUT_R_CTRL 0xCE +#define DA7218_MIXOUT_R_GAIN 0xCF +#define DA7218_HP_L_CTRL 0xD0 +#define DA7218_HP_L_GAIN 0xD1 +#define DA7218_HP_R_CTRL 0xD2 +#define DA7218_HP_R_GAIN 0xD3 +#define DA7218_HP_SNGL_CTRL 0xD4 +#define DA7218_HP_DIFF_CTRL 0xD5 +#define DA7218_HP_DIFF_UNLOCK 0xD7 +#define DA7218_HPLDET_JACK 0xD8 +#define DA7218_HPLDET_CTRL 0xD9 +#define DA7218_HPLDET_TEST 0xDA +#define DA7218_REFERENCES 0xDC +#define DA7218_IO_CTRL 0xE0 +#define DA7218_LDO_CTRL 0xE1 +#define DA7218_SIDETONE_CTRL 0xE4 +#define DA7218_SIDETONE_IN_SELECT 0xE5 +#define DA7218_SIDETONE_GAIN 0xE6 +#define DA7218_DROUTING_ST_OUTFILT_1L 0xE8 +#define DA7218_DROUTING_ST_OUTFILT_1R 0xE9 +#define DA7218_SIDETONE_BIQ_3STAGE_DATA 0xEA +#define DA7218_SIDETONE_BIQ_3STAGE_ADDR 0xEB +#define DA7218_EVENT_STATUS 0xEC +#define DA7218_EVENT 0xED +#define DA7218_EVENT_MASK 0xEE +#define DA7218_DMIC_1_CTRL 0xF0 +#define DA7218_DMIC_2_CTRL 0xF1 +#define DA7218_IN_1L_GAIN 0xF4 +#define DA7218_IN_1R_GAIN 0xF5 +#define DA7218_IN_2L_GAIN 0xF6 +#define DA7218_IN_2R_GAIN 0xF7 +#define DA7218_OUT_1L_GAIN 0xF8 +#define DA7218_OUT_1R_GAIN 0xF9 +#define DA7218_MICBIAS_CTRL 0xFC +#define DA7218_MICBIAS_EN 0xFD + + +/* + * Bit Fields + */ + +#define DA7218_SWITCH_EN_MAX 0x1 + +/* DA7218_SYSTEM_ACTIVE = 0x0 */ +#define DA7218_SYSTEM_ACTIVE_SHIFT 0 +#define DA7218_SYSTEM_ACTIVE_MASK (0x1 << 0) + +/* DA7218_CIF_CTRL = 0x1 */ +#define DA7218_CIF_I2C_WRITE_MODE_SHIFT 0 +#define DA7218_CIF_I2C_WRITE_MODE_MASK (0x1 << 0) + +/* DA7218_CHIP_ID1 = 0x4 */ +#define DA7218_CHIP_ID1_SHIFT 0 +#define DA7218_CHIP_ID1_MASK (0xFF << 0) + +/* DA7218_CHIP_ID2 = 0x5 */ +#define DA7218_CHIP_ID2_SHIFT 0 +#define DA7218_CHIP_ID2_MASK (0xFF << 0) + +/* DA7218_CHIP_REVISION = 0x6 */ +#define DA7218_CHIP_MINOR_SHIFT 0 +#define DA7218_CHIP_MINOR_MASK (0xF << 0) +#define DA7218_CHIP_MAJOR_SHIFT 4 +#define DA7218_CHIP_MAJOR_MASK (0xF << 4) + +/* DA7218_SPARE1 = 0x7 */ +#define DA7218_SPARE1_SHIFT 0 +#define DA7218_SPARE1_MASK (0xFF << 0) + +/* DA7218_STATUS1 = 0x8 */ +#define DA7218_STATUS_SPARE1_SHIFT 0 +#define DA7218_STATUS_SPARE1_MASK (0xFF << 0) + +/* DA7218_SOFT_RESET = 0x9 */ +#define DA7218_CIF_REG_SOFT_RESET_SHIFT 7 +#define DA7218_CIF_REG_SOFT_RESET_MASK (0x1 << 7) + +/* DA7218_SR = 0xB */ +#define DA7218_SR_ADC_SHIFT 0 +#define DA7218_SR_ADC_MASK (0xF << 0) +#define DA7218_SR_DAC_SHIFT 4 +#define DA7218_SR_DAC_MASK (0xF << 4) +#define DA7218_SR_8000 0x01 +#define DA7218_SR_11025 0x02 +#define DA7218_SR_12000 0x03 +#define DA7218_SR_16000 0x05 +#define DA7218_SR_22050 0x06 +#define DA7218_SR_24000 0x07 +#define DA7218_SR_32000 0x09 +#define DA7218_SR_44100 0x0A +#define DA7218_SR_48000 0x0B +#define DA7218_SR_88200 0x0E +#define DA7218_SR_96000 0x0F + +/* DA7218_PC_COUNT = 0xC */ +#define DA7218_PC_FREERUN_SHIFT 0 +#define DA7218_PC_FREERUN_MASK (0x1 << 0) +#define DA7218_PC_RESYNC_AUTO_SHIFT 1 +#define DA7218_PC_RESYNC_AUTO_MASK (0x1 << 1) + +/* DA7218_GAIN_RAMP_CTRL = 0xD */ +#define DA7218_GAIN_RAMP_RATE_SHIFT 0 +#define DA7218_GAIN_RAMP_RATE_MASK (0x3 << 0) +#define DA7218_GAIN_RAMP_RATE_MAX 4 + +/* DA7218_CIF_TIMEOUT_CTRL = 0x10 */ +#define DA7218_I2C_TIMEOUT_EN_SHIFT 0 +#define DA7218_I2C_TIMEOUT_EN_MASK (0x1 << 0) + +/* DA7218_SYSTEM_MODES_INPUT = 0x14 */ +#define DA7218_MODE_SUBMIT_SHIFT 0 +#define DA7218_MODE_SUBMIT_MASK (0x1 << 0) +#define DA7218_ADC_MODE_SHIFT 1 +#define DA7218_ADC_MODE_MASK (0x7F << 1) + +/* DA7218_SYSTEM_MODES_OUTPUT = 0x15 */ +#define DA7218_MODE_SUBMIT_SHIFT 0 +#define DA7218_MODE_SUBMIT_MASK (0x1 << 0) +#define DA7218_DAC_MODE_SHIFT 1 +#define DA7218_DAC_MODE_MASK (0x7F << 1) + +/* DA7218_SYSTEM_STATUS = 0x16 */ +#define DA7218_SC1_BUSY_SHIFT 0 +#define DA7218_SC1_BUSY_MASK (0x1 << 0) +#define DA7218_SC2_BUSY_SHIFT 1 +#define DA7218_SC2_BUSY_MASK (0x1 << 1) + +/* DA7218_IN_1L_FILTER_CTRL = 0x18 */ +#define DA7218_IN_1L_RAMP_EN_SHIFT 5 +#define DA7218_IN_1L_RAMP_EN_MASK (0x1 << 5) +#define DA7218_IN_1L_MUTE_EN_SHIFT 6 +#define DA7218_IN_1L_MUTE_EN_MASK (0x1 << 6) +#define DA7218_IN_1L_FILTER_EN_SHIFT 7 +#define DA7218_IN_1L_FILTER_EN_MASK (0x1 << 7) + +/* DA7218_IN_1R_FILTER_CTRL = 0x19 */ +#define DA7218_IN_1R_RAMP_EN_SHIFT 5 +#define DA7218_IN_1R_RAMP_EN_MASK (0x1 << 5) +#define DA7218_IN_1R_MUTE_EN_SHIFT 6 +#define DA7218_IN_1R_MUTE_EN_MASK (0x1 << 6) +#define DA7218_IN_1R_FILTER_EN_SHIFT 7 +#define DA7218_IN_1R_FILTER_EN_MASK (0x1 << 7) + +/* DA7218_IN_2L_FILTER_CTRL = 0x1A */ +#define DA7218_IN_2L_RAMP_EN_SHIFT 5 +#define DA7218_IN_2L_RAMP_EN_MASK (0x1 << 5) +#define DA7218_IN_2L_MUTE_EN_SHIFT 6 +#define DA7218_IN_2L_MUTE_EN_MASK (0x1 << 6) +#define DA7218_IN_2L_FILTER_EN_SHIFT 7 +#define DA7218_IN_2L_FILTER_EN_MASK (0x1 << 7) + +/* DA7218_IN_2R_FILTER_CTRL = 0x1B */ +#define DA7218_IN_2R_RAMP_EN_SHIFT 5 +#define DA7218_IN_2R_RAMP_EN_MASK (0x1 << 5) +#define DA7218_IN_2R_MUTE_EN_SHIFT 6 +#define DA7218_IN_2R_MUTE_EN_MASK (0x1 << 6) +#define DA7218_IN_2R_FILTER_EN_SHIFT 7 +#define DA7218_IN_2R_FILTER_EN_MASK (0x1 << 7) + +/* DA7218_OUT_1L_FILTER_CTRL = 0x20 */ +#define DA7218_OUT_1L_BIQ_5STAGE_SEL_SHIFT 3 +#define DA7218_OUT_1L_BIQ_5STAGE_SEL_MASK (0x1 << 3) +#define DA7218_OUT_BIQ_5STAGE_SEL_MAX 2 +#define DA7218_OUT_1L_SUBRANGE_EN_SHIFT 4 +#define DA7218_OUT_1L_SUBRANGE_EN_MASK (0x1 << 4) +#define DA7218_OUT_1L_RAMP_EN_SHIFT 5 +#define DA7218_OUT_1L_RAMP_EN_MASK (0x1 << 5) +#define DA7218_OUT_1L_MUTE_EN_SHIFT 6 +#define DA7218_OUT_1L_MUTE_EN_MASK (0x1 << 6) +#define DA7218_OUT_1L_FILTER_EN_SHIFT 7 +#define DA7218_OUT_1L_FILTER_EN_MASK (0x1 << 7) + +/* DA7218_OUT_1R_FILTER_CTRL = 0x21 */ +#define DA7218_OUT_1R_BIQ_5STAGE_SEL_SHIFT 3 +#define DA7218_OUT_1R_BIQ_5STAGE_SEL_MASK (0x1 << 3) +#define DA7218_OUT_1R_SUBRANGE_EN_SHIFT 4 +#define DA7218_OUT_1R_SUBRANGE_EN_MASK (0x1 << 4) +#define DA7218_OUT_1R_RAMP_EN_SHIFT 5 +#define DA7218_OUT_1R_RAMP_EN_MASK (0x1 << 5) +#define DA7218_OUT_1R_MUTE_EN_SHIFT 6 +#define DA7218_OUT_1R_MUTE_EN_MASK (0x1 << 6) +#define DA7218_OUT_1R_FILTER_EN_SHIFT 7 +#define DA7218_OUT_1R_FILTER_EN_MASK (0x1 << 7) + +/* DA7218_OUT_1_HPF_FILTER_CTRL = 0x24 */ +#define DA7218_OUT_1_VOICE_HPF_CORNER_SHIFT 0 +#define DA7218_OUT_1_VOICE_HPF_CORNER_MASK (0x7 << 0) +#define DA7218_VOICE_HPF_CORNER_MAX 8 +#define DA7218_OUT_1_VOICE_EN_SHIFT 3 +#define DA7218_OUT_1_VOICE_EN_MASK (0x1 << 3) +#define DA7218_OUT_1_AUDIO_HPF_CORNER_SHIFT 4 +#define DA7218_OUT_1_AUDIO_HPF_CORNER_MASK (0x3 << 4) +#define DA7218_AUDIO_HPF_CORNER_MAX 4 +#define DA7218_OUT_1_HPF_EN_SHIFT 7 +#define DA7218_OUT_1_HPF_EN_MASK (0x1 << 7) +#define DA7218_HPF_MODE_SHIFT 0 +#define DA7218_HPF_DISABLED ((0x0 << 3) | (0x0 << 7)) +#define DA7218_HPF_AUDIO_EN ((0x0 << 3) | (0x1 << 7)) +#define DA7218_HPF_VOICE_EN ((0x1 << 3) | (0x1 << 7)) +#define DA7218_HPF_MODE_MASK ((0x1 << 3) | (0x1 << 7)) +#define DA7218_HPF_MODE_MAX 3 + +/* DA7218_OUT_1_EQ_12_FILTER_CTRL = 0x25 */ +#define DA7218_OUT_1_EQ_BAND1_SHIFT 0 +#define DA7218_OUT_1_EQ_BAND1_MASK (0xF << 0) +#define DA7218_OUT_EQ_BAND_MAX 0xF +#define DA7218_OUT_1_EQ_BAND2_SHIFT 4 +#define DA7218_OUT_1_EQ_BAND2_MASK (0xF << 4) + +/* DA7218_OUT_1_EQ_34_FILTER_CTRL = 0x26 */ +#define DA7218_OUT_1_EQ_BAND3_SHIFT 0 +#define DA7218_OUT_1_EQ_BAND3_MASK (0xF << 0) +#define DA7218_OUT_1_EQ_BAND4_SHIFT 4 +#define DA7218_OUT_1_EQ_BAND4_MASK (0xF << 4) + +/* DA7218_OUT_1_EQ_5_FILTER_CTRL = 0x27 */ +#define DA7218_OUT_1_EQ_BAND5_SHIFT 0 +#define DA7218_OUT_1_EQ_BAND5_MASK (0xF << 0) +#define DA7218_OUT_1_EQ_EN_SHIFT 7 +#define DA7218_OUT_1_EQ_EN_MASK (0x1 << 7) + +/* DA7218_OUT_1_BIQ_5STAGE_CTRL = 0x28 */ +#define DA7218_OUT_1_BIQ_5STAGE_MUTE_EN_SHIFT 6 +#define DA7218_OUT_1_BIQ_5STAGE_MUTE_EN_MASK (0x1 << 6) +#define DA7218_OUT_1_BIQ_5STAGE_FILTER_EN_SHIFT 7 +#define DA7218_OUT_1_BIQ_5STAGE_FILTER_EN_MASK (0x1 << 7) + +/* DA7218_OUT_1_BIQ_5STAGE_DATA = 0x29 */ +#define DA7218_OUT_1_BIQ_5STAGE_DATA_SHIFT 0 +#define DA7218_OUT_1_BIQ_5STAGE_DATA_MASK (0xFF << 0) + +/* DA7218_OUT_1_BIQ_5STAGE_ADDR = 0x2A */ +#define DA7218_OUT_1_BIQ_5STAGE_ADDR_SHIFT 0 +#define DA7218_OUT_1_BIQ_5STAGE_ADDR_MASK (0x3F << 0) +#define DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE 50 + +/* DA7218_MIXIN_1_CTRL = 0x2C */ +#define DA7218_MIXIN_1_MIX_SEL_SHIFT 3 +#define DA7218_MIXIN_1_MIX_SEL_MASK (0x1 << 3) +#define DA7218_MIXIN_1_AMP_ZC_EN_SHIFT 4 +#define DA7218_MIXIN_1_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7218_MIXIN_1_AMP_RAMP_EN_SHIFT 5 +#define DA7218_MIXIN_1_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7218_MIXIN_1_AMP_MUTE_EN_SHIFT 6 +#define DA7218_MIXIN_1_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7218_MIXIN_1_AMP_EN_SHIFT 7 +#define DA7218_MIXIN_1_AMP_EN_MASK (0x1 << 7) + +/* DA7218_MIXIN_1_GAIN = 0x2D */ +#define DA7218_MIXIN_1_AMP_GAIN_SHIFT 0 +#define DA7218_MIXIN_1_AMP_GAIN_MASK (0xF << 0) +#define DA7218_MIXIN_AMP_GAIN_MAX 0xF + +/* DA7218_MIXIN_2_CTRL = 0x2E */ +#define DA7218_MIXIN_2_MIX_SEL_SHIFT 3 +#define DA7218_MIXIN_2_MIX_SEL_MASK (0x1 << 3) +#define DA7218_MIXIN_2_AMP_ZC_EN_SHIFT 4 +#define DA7218_MIXIN_2_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7218_MIXIN_2_AMP_RAMP_EN_SHIFT 5 +#define DA7218_MIXIN_2_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7218_MIXIN_2_AMP_MUTE_EN_SHIFT 6 +#define DA7218_MIXIN_2_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7218_MIXIN_2_AMP_EN_SHIFT 7 +#define DA7218_MIXIN_2_AMP_EN_MASK (0x1 << 7) + +/* DA7218_MIXIN_2_GAIN = 0x2F */ +#define DA7218_MIXIN_2_AMP_GAIN_SHIFT 0 +#define DA7218_MIXIN_2_AMP_GAIN_MASK (0xF << 0) + +/* DA7218_ALC_CTRL1 = 0x30 */ +#define DA7218_ALC_EN_SHIFT 0 +#define DA7218_ALC_EN_MASK (0xF << 0) +#define DA7218_ALC_CHAN1_L_EN_SHIFT 0 +#define DA7218_ALC_CHAN1_R_EN_SHIFT 1 +#define DA7218_ALC_CHAN2_L_EN_SHIFT 2 +#define DA7218_ALC_CHAN2_R_EN_SHIFT 3 +#define DA7218_ALC_SYNC_MODE_SHIFT 4 +#define DA7218_ALC_SYNC_MODE_MASK (0xF << 4) +#define DA7218_ALC_SYNC_MODE_CH1 (0x1 << 4) +#define DA7218_ALC_SYNC_MODE_CH2 (0x4 << 4) + +/* DA7218_ALC_CTRL2 = 0x31 */ +#define DA7218_ALC_ATTACK_SHIFT 0 +#define DA7218_ALC_ATTACK_MASK (0xF << 0) +#define DA7218_ALC_ATTACK_MAX 13 +#define DA7218_ALC_RELEASE_SHIFT 4 +#define DA7218_ALC_RELEASE_MASK (0xF << 4) +#define DA7218_ALC_RELEASE_MAX 11 + +/* DA7218_ALC_CTRL3 = 0x32 */ +#define DA7218_ALC_HOLD_SHIFT 0 +#define DA7218_ALC_HOLD_MASK (0xF << 0) +#define DA7218_ALC_HOLD_MAX 16 + +/* DA7218_ALC_NOISE = 0x33 */ +#define DA7218_ALC_NOISE_SHIFT 0 +#define DA7218_ALC_NOISE_MASK (0x3F << 0) +#define DA7218_ALC_THRESHOLD_MAX 0x3F + +/* DA7218_ALC_TARGET_MIN = 0x34 */ +#define DA7218_ALC_THRESHOLD_MIN_SHIFT 0 +#define DA7218_ALC_THRESHOLD_MIN_MASK (0x3F << 0) + +/* DA7218_ALC_TARGET_MAX = 0x35 */ +#define DA7218_ALC_THRESHOLD_MAX_SHIFT 0 +#define DA7218_ALC_THRESHOLD_MAX_MASK (0x3F << 0) + +/* DA7218_ALC_GAIN_LIMITS = 0x36 */ +#define DA7218_ALC_ATTEN_MAX_SHIFT 0 +#define DA7218_ALC_ATTEN_MAX_MASK (0xF << 0) +#define DA7218_ALC_ATTEN_GAIN_MAX 0xF +#define DA7218_ALC_GAIN_MAX_SHIFT 4 +#define DA7218_ALC_GAIN_MAX_MASK (0xF << 4) + +/* DA7218_ALC_ANA_GAIN_LIMITS = 0x37 */ +#define DA7218_ALC_ANA_GAIN_MIN_SHIFT 0 +#define DA7218_ALC_ANA_GAIN_MIN_MASK (0x7 << 0) +#define DA7218_ALC_ANA_GAIN_MIN 0x1 +#define DA7218_ALC_ANA_GAIN_MAX 0x7 +#define DA7218_ALC_ANA_GAIN_MAX_SHIFT 4 +#define DA7218_ALC_ANA_GAIN_MAX_MASK (0x7 << 4) + +/* DA7218_ALC_ANTICLIP_CTRL = 0x38 */ +#define DA7218_ALC_ANTICLIP_STEP_SHIFT 0 +#define DA7218_ALC_ANTICLIP_STEP_MASK (0x3 << 0) +#define DA7218_ALC_ANTICLIP_STEP_MAX 4 +#define DA7218_ALC_ANTICLIP_EN_SHIFT 7 +#define DA7218_ALC_ANTICLIP_EN_MASK (0x1 << 7) + +/* DA7218_AGS_ENABLE = 0x3C */ +#define DA7218_AGS_ENABLE_SHIFT 0 +#define DA7218_AGS_ENABLE_MASK (0x3 << 0) +#define DA7218_AGS_ENABLE_CHAN1_SHIFT 0 +#define DA7218_AGS_ENABLE_CHAN2_SHIFT 1 + +/* DA7218_AGS_TRIGGER = 0x3D */ +#define DA7218_AGS_TRIGGER_SHIFT 0 +#define DA7218_AGS_TRIGGER_MASK (0xF << 0) +#define DA7218_AGS_TRIGGER_MAX 0xF + +/* DA7218_AGS_ATT_MAX = 0x3E */ +#define DA7218_AGS_ATT_MAX_SHIFT 0 +#define DA7218_AGS_ATT_MAX_MASK (0x7 << 0) +#define DA7218_AGS_ATT_MAX_MAX 0x7 + +/* DA7218_AGS_TIMEOUT = 0x3F */ +#define DA7218_AGS_TIMEOUT_EN_SHIFT 0 +#define DA7218_AGS_TIMEOUT_EN_MASK (0x1 << 0) + +/* DA7218_AGS_ANTICLIP_CTRL = 0x40 */ +#define DA7218_AGS_ANTICLIP_EN_SHIFT 7 +#define DA7218_AGS_ANTICLIP_EN_MASK (0x1 << 7) + +/* DA7218_CALIB_CTRL = 0x44 */ +#define DA7218_CALIB_OFFSET_EN_SHIFT 0 +#define DA7218_CALIB_OFFSET_EN_MASK (0x1 << 0) +#define DA7218_CALIB_AUTO_EN_SHIFT 2 +#define DA7218_CALIB_AUTO_EN_MASK (0x1 << 2) +#define DA7218_CALIB_OVERFLOW_SHIFT 3 +#define DA7218_CALIB_OVERFLOW_MASK (0x1 << 3) + +/* DA7218_CALIB_OFFSET_AUTO_M_1 = 0x45 */ +#define DA7218_CALIB_OFFSET_AUTO_M_1_SHIFT 0 +#define DA7218_CALIB_OFFSET_AUTO_M_1_MASK (0xFF << 0) + +/* DA7218_CALIB_OFFSET_AUTO_U_1 = 0x46 */ +#define DA7218_CALIB_OFFSET_AUTO_U_1_SHIFT 0 +#define DA7218_CALIB_OFFSET_AUTO_U_1_MASK (0xF << 0) + +/* DA7218_CALIB_OFFSET_AUTO_M_2 = 0x47 */ +#define DA7218_CALIB_OFFSET_AUTO_M_2_SHIFT 0 +#define DA7218_CALIB_OFFSET_AUTO_M_2_MASK (0xFF << 0) + +/* DA7218_CALIB_OFFSET_AUTO_U_2 = 0x48 */ +#define DA7218_CALIB_OFFSET_AUTO_U_2_SHIFT 0 +#define DA7218_CALIB_OFFSET_AUTO_U_2_MASK (0xF << 0) + +/* DA7218_ENV_TRACK_CTRL = 0x4C */ +#define DA7218_INTEG_ATTACK_SHIFT 0 +#define DA7218_INTEG_ATTACK_MASK (0x3 << 0) +#define DA7218_INTEG_RELEASE_SHIFT 4 +#define DA7218_INTEG_RELEASE_MASK (0x3 << 4) +#define DA7218_INTEG_MAX 4 + +/* DA7218_LVL_DET_CTRL = 0x50 */ +#define DA7218_LVL_DET_EN_SHIFT 0 +#define DA7218_LVL_DET_EN_MASK (0xF << 0) +#define DA7218_LVL_DET_EN_CHAN1L_SHIFT 0 +#define DA7218_LVL_DET_EN_CHAN1R_SHIFT 1 +#define DA7218_LVL_DET_EN_CHAN2L_SHIFT 2 +#define DA7218_LVL_DET_EN_CHAN2R_SHIFT 3 + +/* DA7218_LVL_DET_LEVEL = 0x51 */ +#define DA7218_LVL_DET_LEVEL_SHIFT 0 +#define DA7218_LVL_DET_LEVEL_MASK (0x7F << 0) +#define DA7218_LVL_DET_LEVEL_MAX 0x7F + +/* DA7218_DGS_TRIGGER = 0x54 */ +#define DA7218_DGS_TRIGGER_LVL_SHIFT 0 +#define DA7218_DGS_TRIGGER_LVL_MASK (0x3F << 0) +#define DA7218_DGS_TRIGGER_MAX 0x3F + +/* DA7218_DGS_ENABLE = 0x55 */ +#define DA7218_DGS_ENABLE_SHIFT 0 +#define DA7218_DGS_ENABLE_MASK (0x3 << 0) +#define DA7218_DGS_ENABLE_L_SHIFT 0 +#define DA7218_DGS_ENABLE_R_SHIFT 1 + +/* DA7218_DGS_RISE_FALL = 0x56 */ +#define DA7218_DGS_RISE_COEFF_SHIFT 0 +#define DA7218_DGS_RISE_COEFF_MASK (0x7 << 0) +#define DA7218_DGS_RISE_COEFF_MAX 7 +#define DA7218_DGS_FALL_COEFF_SHIFT 4 +#define DA7218_DGS_FALL_COEFF_MASK (0x7 << 4) +#define DA7218_DGS_FALL_COEFF_MAX 8 + +/* DA7218_DGS_SYNC_DELAY = 0x57 */ +#define DA7218_DGS_SYNC_DELAY_SHIFT 0 +#define DA7218_DGS_SYNC_DELAY_MASK (0xFF << 0) +#define DA7218_DGS_SYNC_DELAY_MAX 0xFF + +/* DA7218_DGS_SYNC_DELAY2 = 0x58 */ +#define DA7218_DGS_SYNC_DELAY2_SHIFT 0 +#define DA7218_DGS_SYNC_DELAY2_MASK (0xFF << 0) + +/* DA7218_DGS_SYNC_DELAY3 = 0x59 */ +#define DA7218_DGS_SYNC_DELAY3_SHIFT 0 +#define DA7218_DGS_SYNC_DELAY3_MASK (0x7F << 0) +#define DA7218_DGS_SYNC_DELAY3_MAX 0x7F + +/* DA7218_DGS_LEVELS = 0x5A */ +#define DA7218_DGS_ANTICLIP_LVL_SHIFT 0 +#define DA7218_DGS_ANTICLIP_LVL_MASK (0x7 << 0) +#define DA7218_DGS_ANTICLIP_LVL_MAX 0x7 +#define DA7218_DGS_SIGNAL_LVL_SHIFT 4 +#define DA7218_DGS_SIGNAL_LVL_MASK (0xF << 4) +#define DA7218_DGS_SIGNAL_LVL_MAX 0xF + +/* DA7218_DGS_GAIN_CTRL = 0x5B */ +#define DA7218_DGS_STEPS_SHIFT 0 +#define DA7218_DGS_STEPS_MASK (0x1F << 0) +#define DA7218_DGS_STEPS_MAX 0x1F +#define DA7218_DGS_RAMP_EN_SHIFT 5 +#define DA7218_DGS_RAMP_EN_MASK (0x1 << 5) +#define DA7218_DGS_SUBR_EN_SHIFT 6 +#define DA7218_DGS_SUBR_EN_MASK (0x1 << 6) + +/* DA7218_DROUTING_OUTDAI_1L = 0x5C */ +#define DA7218_OUTDAI_1L_SRC_SHIFT 0 +#define DA7218_OUTDAI_1L_SRC_MASK (0x7F << 0) +#define DA7218_DMIX_SRC_INFILT1L 0 +#define DA7218_DMIX_SRC_INFILT1R 1 +#define DA7218_DMIX_SRC_INFILT2L 2 +#define DA7218_DMIX_SRC_INFILT2R 3 +#define DA7218_DMIX_SRC_TONEGEN 4 +#define DA7218_DMIX_SRC_DAIL 5 +#define DA7218_DMIX_SRC_DAIR 6 + +/* DA7218_DMIX_OUTDAI_1L_INFILT_1L_GAIN = 0x5D */ +#define DA7218_OUTDAI_1L_INFILT_1L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1L_INFILT_1L_GAIN_MASK (0x1F << 0) +#define DA7218_DMIX_GAIN_MAX 0x1F + +/* DA7218_DMIX_OUTDAI_1L_INFILT_1R_GAIN = 0x5E */ +#define DA7218_OUTDAI_1L_INFILT_1R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1L_INFILT_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1L_INFILT_2L_GAIN = 0x5F */ +#define DA7218_OUTDAI_1L_INFILT_2L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1L_INFILT_2L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1L_INFILT_2R_GAIN = 0x60 */ +#define DA7218_OUTDAI_1L_INFILT_2R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1L_INFILT_2R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1L_TONEGEN_GAIN = 0x61 */ +#define DA7218_OUTDAI_1L_TONEGEN_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1L_TONEGEN_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1L_INDAI_1L_GAIN = 0x62 */ +#define DA7218_OUTDAI_1L_INDAI_1L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1L_INDAI_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1L_INDAI_1R_GAIN = 0x63 */ +#define DA7218_OUTDAI_1L_INDAI_1R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1L_INDAI_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DROUTING_OUTDAI_1R = 0x64 */ +#define DA7218_OUTDAI_1R_SRC_SHIFT 0 +#define DA7218_OUTDAI_1R_SRC_MASK (0x7F << 0) + +/* DA7218_DMIX_OUTDAI_1R_INFILT_1L_GAIN = 0x65 */ +#define DA7218_OUTDAI_1R_INFILT_1L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1R_INFILT_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1R_INFILT_1R_GAIN = 0x66 */ +#define DA7218_OUTDAI_1R_INFILT_1R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1R_INFILT_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1R_INFILT_2L_GAIN = 0x67 */ +#define DA7218_OUTDAI_1R_INFILT_2L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1R_INFILT_2L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1R_INFILT_2R_GAIN = 0x68 */ +#define DA7218_OUTDAI_1R_INFILT_2R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1R_INFILT_2R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1R_TONEGEN_GAIN = 0x69 */ +#define DA7218_OUTDAI_1R_TONEGEN_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1R_TONEGEN_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1R_INDAI_1L_GAIN = 0x6A */ +#define DA7218_OUTDAI_1R_INDAI_1L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1R_INDAI_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1R_INDAI_1R_GAIN = 0x6B */ +#define DA7218_OUTDAI_1R_INDAI_1R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1R_INDAI_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DROUTING_OUTFILT_1L = 0x6C */ +#define DA7218_OUTFILT_1L_SRC_SHIFT 0 +#define DA7218_OUTFILT_1L_SRC_MASK (0x7F << 0) + +/* DA7218_DMIX_OUTFILT_1L_INFILT_1L_GAIN = 0x6D */ +#define DA7218_OUTFILT_1L_INFILT_1L_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1L_INFILT_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1L_INFILT_1R_GAIN = 0x6E */ +#define DA7218_OUTFILT_1L_INFILT_1R_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1L_INFILT_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1L_INFILT_2L_GAIN = 0x6F */ +#define DA7218_OUTFILT_1L_INFILT_2L_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1L_INFILT_2L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1L_INFILT_2R_GAIN = 0x70 */ +#define DA7218_OUTFILT_1L_INFILT_2R_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1L_INFILT_2R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1L_TONEGEN_GAIN = 0x71 */ +#define DA7218_OUTFILT_1L_TONEGEN_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1L_TONEGEN_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1L_INDAI_1L_GAIN = 0x72 */ +#define DA7218_OUTFILT_1L_INDAI_1L_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1L_INDAI_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1L_INDAI_1R_GAIN = 0x73 */ +#define DA7218_OUTFILT_1L_INDAI_1R_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1L_INDAI_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DROUTING_OUTFILT_1R = 0x74 */ +#define DA7218_OUTFILT_1R_SRC_SHIFT 0 +#define DA7218_OUTFILT_1R_SRC_MASK (0x7F << 0) + +/* DA7218_DMIX_OUTFILT_1R_INFILT_1L_GAIN = 0x75 */ +#define DA7218_OUTFILT_1R_INFILT_1L_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1R_INFILT_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1R_INFILT_1R_GAIN = 0x76 */ +#define DA7218_OUTFILT_1R_INFILT_1R_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1R_INFILT_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1R_INFILT_2L_GAIN = 0x77 */ +#define DA7218_OUTFILT_1R_INFILT_2L_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1R_INFILT_2L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1R_INFILT_2R_GAIN = 0x78 */ +#define DA7218_OUTFILT_1R_INFILT_2R_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1R_INFILT_2R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1R_TONEGEN_GAIN = 0x79 */ +#define DA7218_OUTFILT_1R_TONEGEN_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1R_TONEGEN_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1R_INDAI_1L_GAIN = 0x7A */ +#define DA7218_OUTFILT_1R_INDAI_1L_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1R_INDAI_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1R_INDAI_1R_GAIN = 0x7B */ +#define DA7218_OUTFILT_1R_INDAI_1R_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1R_INDAI_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DROUTING_OUTDAI_2L = 0x7C */ +#define DA7218_OUTDAI_2L_SRC_SHIFT 0 +#define DA7218_OUTDAI_2L_SRC_MASK (0x7F << 0) + +/* DA7218_DMIX_OUTDAI_2L_INFILT_1L_GAIN = 0x7D */ +#define DA7218_OUTDAI_2L_INFILT_1L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2L_INFILT_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2L_INFILT_1R_GAIN = 0x7E */ +#define DA7218_OUTDAI_2L_INFILT_1R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2L_INFILT_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2L_INFILT_2L_GAIN = 0x7F */ +#define DA7218_OUTDAI_2L_INFILT_2L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2L_INFILT_2L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2L_INFILT_2R_GAIN = 0x80 */ +#define DA7218_OUTDAI_2L_INFILT_2R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2L_INFILT_2R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2L_TONEGEN_GAIN = 0x81 */ +#define DA7218_OUTDAI_2L_TONEGEN_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2L_TONEGEN_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2L_INDAI_1L_GAIN = 0x82 */ +#define DA7218_OUTDAI_2L_INDAI_1L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2L_INDAI_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2L_INDAI_1R_GAIN = 0x83 */ +#define DA7218_OUTDAI_2L_INDAI_1R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2L_INDAI_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DROUTING_OUTDAI_2R = 0x84 */ +#define DA7218_OUTDAI_2R_SRC_SHIFT 0 +#define DA7218_OUTDAI_2R_SRC_MASK (0x7F << 0) + +/* DA7218_DMIX_OUTDAI_2R_INFILT_1L_GAIN = 0x85 */ +#define DA7218_OUTDAI_2R_INFILT_1L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2R_INFILT_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2R_INFILT_1R_GAIN = 0x86 */ +#define DA7218_OUTDAI_2R_INFILT_1R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2R_INFILT_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2R_INFILT_2L_GAIN = 0x87 */ +#define DA7218_OUTDAI_2R_INFILT_2L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2R_INFILT_2L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2R_INFILT_2R_GAIN = 0x88 */ +#define DA7218_OUTDAI_2R_INFILT_2R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2R_INFILT_2R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2R_TONEGEN_GAIN = 0x89 */ +#define DA7218_OUTDAI_2R_TONEGEN_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2R_TONEGEN_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2R_INDAI_1L_GAIN = 0x8A */ +#define DA7218_OUTDAI_2R_INDAI_1L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2R_INDAI_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2R_INDAI_1R_GAIN = 0x8B */ +#define DA7218_OUTDAI_2R_INDAI_1R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2R_INDAI_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DAI_CTRL = 0x8C */ +#define DA7218_DAI_FORMAT_SHIFT 0 +#define DA7218_DAI_FORMAT_MASK (0x3 << 0) +#define DA7218_DAI_FORMAT_I2S (0x0 << 0) +#define DA7218_DAI_FORMAT_LEFT_J (0x1 << 0) +#define DA7218_DAI_FORMAT_RIGHT_J (0x2 << 0) +#define DA7218_DAI_FORMAT_DSP (0x3 << 0) +#define DA7218_DAI_WORD_LENGTH_SHIFT 2 +#define DA7218_DAI_WORD_LENGTH_MASK (0x3 << 2) +#define DA7218_DAI_WORD_LENGTH_S16_LE (0x0 << 2) +#define DA7218_DAI_WORD_LENGTH_S20_LE (0x1 << 2) +#define DA7218_DAI_WORD_LENGTH_S24_LE (0x2 << 2) +#define DA7218_DAI_WORD_LENGTH_S32_LE (0x3 << 2) +#define DA7218_DAI_CH_NUM_SHIFT 4 +#define DA7218_DAI_CH_NUM_MASK (0x7 << 4) +#define DA7218_DAI_CH_NUM_MAX 4 +#define DA7218_DAI_EN_SHIFT 7 +#define DA7218_DAI_EN_MASK (0x1 << 7) + +/* DA7218_DAI_TDM_CTRL = 0x8D */ +#define DA7218_DAI_TDM_CH_EN_SHIFT 0 +#define DA7218_DAI_TDM_CH_EN_MASK (0xF << 0) +#define DA7218_DAI_TDM_MAX_SLOTS 4 +#define DA7218_DAI_OE_SHIFT 6 +#define DA7218_DAI_OE_MASK (0x1 << 6) +#define DA7218_DAI_TDM_MODE_EN_SHIFT 7 +#define DA7218_DAI_TDM_MODE_EN_MASK (0x1 << 7) + +/* DA7218_DAI_OFFSET_LOWER = 0x8E */ +#define DA7218_DAI_OFFSET_LOWER_SHIFT 0 +#define DA7218_DAI_OFFSET_LOWER_MASK (0xFF << 0) + +/* DA7218_DAI_OFFSET_UPPER = 0x8F */ +#define DA7218_DAI_OFFSET_UPPER_SHIFT 0 +#define DA7218_DAI_OFFSET_UPPER_MASK (0x7 << 0) + +/* DA7218_DAI_CLK_MODE = 0x90 */ +#define DA7218_DAI_BCLKS_PER_WCLK_SHIFT 0 +#define DA7218_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0) +#define DA7218_DAI_BCLKS_PER_WCLK_32 (0x0 << 0) +#define DA7218_DAI_BCLKS_PER_WCLK_64 (0x1 << 0) +#define DA7218_DAI_BCLKS_PER_WCLK_128 (0x2 << 0) +#define DA7218_DAI_BCLKS_PER_WCLK_256 (0x3 << 0) +#define DA7218_DAI_CLK_POL_SHIFT 2 +#define DA7218_DAI_CLK_POL_MASK (0x1 << 2) +#define DA7218_DAI_CLK_POL_INV (0x1 << 2) +#define DA7218_DAI_WCLK_POL_SHIFT 3 +#define DA7218_DAI_WCLK_POL_MASK (0x1 << 3) +#define DA7218_DAI_WCLK_POL_INV (0x1 << 3) +#define DA7218_DAI_WCLK_TRI_STATE_SHIFT 4 +#define DA7218_DAI_WCLK_TRI_STATE_MASK (0x1 << 4) +#define DA7218_DAI_CLK_EN_SHIFT 7 +#define DA7218_DAI_CLK_EN_MASK (0x1 << 7) + +/* 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_MCLK_SQR_EN_SHIFT 4 +#define DA7218_PLL_MCLK_SQR_EN_MASK (0x1 << 4) +#define DA7218_PLL_MODE_SHIFT 6 +#define DA7218_PLL_MODE_MASK (0x3 << 6) +#define DA7218_PLL_MODE_BYPASS (0x0 << 6) +#define DA7218_PLL_MODE_NORMAL (0x1 << 6) +#define DA7218_PLL_MODE_SRM (0x2 << 6) +#define DA7218_PLL_MODE_32KHZ (0x3 << 6) + +/* DA7218_PLL_FRAC_TOP = 0x92 */ +#define DA7218_PLL_FBDIV_FRAC_TOP_SHIFT 0 +#define DA7218_PLL_FBDIV_FRAC_TOP_MASK (0x1F << 0) + +/* DA7218_PLL_FRAC_BOT = 0x93 */ +#define DA7218_PLL_FBDIV_FRAC_BOT_SHIFT 0 +#define DA7218_PLL_FBDIV_FRAC_BOT_MASK (0xFF << 0) + +/* DA7218_PLL_INTEGER = 0x94 */ +#define DA7218_PLL_FBDIV_INTEGER_SHIFT 0 +#define DA7218_PLL_FBDIV_INTEGER_MASK (0x7F << 0) + +/* DA7218_PLL_STATUS = 0x95 */ +#define DA7218_PLL_SRM_STATUS_SHIFT 0 +#define DA7218_PLL_SRM_STATUS_MASK (0xFF << 0) +#define DA7218_PLL_SRM_STATUS_SRM_LOCK (0x1 << 7) + +/* DA7218_PLL_REFOSC_CAL = 0x98 */ +#define DA7218_PLL_REFOSC_CAL_CTRL_SHIFT 0 +#define DA7218_PLL_REFOSC_CAL_CTRL_MASK (0x1F << 0) +#define DA7218_PLL_REFOSC_CAL_START_SHIFT 6 +#define DA7218_PLL_REFOSC_CAL_START_MASK (0x1 << 6) +#define DA7218_PLL_REFOSC_CAL_EN_SHIFT 7 +#define DA7218_PLL_REFOSC_CAL_EN_MASK (0x1 << 7) + +/* DA7218_DAC_NG_CTRL = 0x9C */ +#define DA7218_DAC_NG_EN_SHIFT 7 +#define DA7218_DAC_NG_EN_MASK (0x1 << 7) + +/* DA7218_DAC_NG_SETUP_TIME = 0x9D */ +#define DA7218_DAC_NG_SETUP_TIME_SHIFT 0 +#define DA7218_DAC_NG_SETUP_TIME_MASK (0x3 << 0) +#define DA7218_DAC_NG_SETUP_TIME_MAX 4 +#define DA7218_DAC_NG_RAMPUP_RATE_SHIFT 2 +#define DA7218_DAC_NG_RAMPUP_RATE_MASK (0x1 << 2) +#define DA7218_DAC_NG_RAMPUP_RATE_MAX 2 +#define DA7218_DAC_NG_RAMPDN_RATE_SHIFT 3 +#define DA7218_DAC_NG_RAMPDN_RATE_MASK (0x1 << 3) +#define DA7218_DAC_NG_RAMPDN_RATE_MAX 2 + +/* DA7218_DAC_NG_OFF_THRESH = 0x9E */ +#define DA7218_DAC_NG_OFF_THRESHOLD_SHIFT 0 +#define DA7218_DAC_NG_OFF_THRESHOLD_MASK (0x7 << 0) +#define DA7218_DAC_NG_THRESHOLD_MAX 0x7 + +/* DA7218_DAC_NG_ON_THRESH = 0x9F */ +#define DA7218_DAC_NG_ON_THRESHOLD_SHIFT 0 +#define DA7218_DAC_NG_ON_THRESHOLD_MASK (0x7 << 0) + +/* DA7218_TONE_GEN_CFG1 = 0xA0 */ +#define DA7218_DTMF_REG_SHIFT 0 +#define DA7218_DTMF_REG_MASK (0xF << 0) +#define DA7218_DTMF_REG_MAX 16 +#define DA7218_DTMF_EN_SHIFT 4 +#define DA7218_DTMF_EN_MASK (0x1 << 4) +#define DA7218_START_STOPN_SHIFT 7 +#define DA7218_START_STOPN_MASK (0x1 << 7) + +/* DA7218_TONE_GEN_CFG2 = 0xA1 */ +#define DA7218_SWG_SEL_SHIFT 0 +#define DA7218_SWG_SEL_MASK (0x3 << 0) +#define DA7218_SWG_SEL_MAX 4 + +/* DA7218_TONE_GEN_FREQ1_L = 0xA2 */ +#define DA7218_FREQ1_L_SHIFT 0 +#define DA7218_FREQ1_L_MASK (0xFF << 0) +#define DA7218_FREQ_MAX 0xFFFF + +/* DA7218_TONE_GEN_FREQ1_U = 0xA3 */ +#define DA7218_FREQ1_U_SHIFT 0 +#define DA7218_FREQ1_U_MASK (0xFF << 0) + +/* DA7218_TONE_GEN_FREQ2_L = 0xA4 */ +#define DA7218_FREQ2_L_SHIFT 0 +#define DA7218_FREQ2_L_MASK (0xFF << 0) + +/* DA7218_TONE_GEN_FREQ2_U = 0xA5 */ +#define DA7218_FREQ2_U_SHIFT 0 +#define DA7218_FREQ2_U_MASK (0xFF << 0) + +/* DA7218_TONE_GEN_CYCLES = 0xA6 */ +#define DA7218_BEEP_CYCLES_SHIFT 0 +#define DA7218_BEEP_CYCLES_MASK (0x7 << 0) + +/* DA7218_TONE_GEN_ON_PER = 0xA7 */ +#define DA7218_BEEP_ON_PER_SHIFT 0 +#define DA7218_BEEP_ON_PER_MASK (0x3F << 0) + +/* DA7218_TONE_GEN_OFF_PER = 0xA8 */ +#define DA7218_BEEP_OFF_PER_SHIFT 0 +#define DA7218_BEEP_OFF_PER_MASK (0x3F << 0) +#define DA7218_BEEP_ON_OFF_MAX 0x3F + +/* DA7218_CP_CTRL = 0xAC */ +#define DA7218_CP_MOD_SHIFT 2 +#define DA7218_CP_MOD_MASK (0x3 << 2) +#define DA7218_CP_MCHANGE_SHIFT 4 +#define DA7218_CP_MCHANGE_MASK (0x3 << 4) +#define DA7218_CP_MCHANGE_REL_MASK 0x3 +#define DA7218_CP_MCHANGE_MAX 3 +#define DA7218_CP_MCHANGE_LARGEST_VOL 0x1 +#define DA7218_CP_MCHANGE_DAC_VOL 0x2 +#define DA7218_CP_MCHANGE_SIG_MAG 0x3 +#define DA7218_CP_SMALL_SWITCH_FREQ_EN_SHIFT 6 +#define DA7218_CP_SMALL_SWITCH_FREQ_EN_MASK (0x1 << 6) +#define DA7218_CP_EN_SHIFT 7 +#define DA7218_CP_EN_MASK (0x1 << 7) + +/* DA7218_CP_DELAY = 0xAD */ +#define DA7218_CP_FCONTROL_SHIFT 0 +#define DA7218_CP_FCONTROL_MASK (0x7 << 0) +#define DA7218_CP_FCONTROL_MAX 6 +#define DA7218_CP_TAU_DELAY_SHIFT 3 +#define DA7218_CP_TAU_DELAY_MASK (0x7 << 3) +#define DA7218_CP_TAU_DELAY_MAX 8 + +/* DA7218_CP_VOL_THRESHOLD1 = 0xAE */ +#define DA7218_CP_THRESH_VDD2_SHIFT 0 +#define DA7218_CP_THRESH_VDD2_MASK (0x3F << 0) +#define DA7218_CP_THRESH_VDD2_MAX 0x3F + +/* DA7218_MIC_1_CTRL = 0xB4 */ +#define DA7218_MIC_1_AMP_MUTE_EN_SHIFT 6 +#define DA7218_MIC_1_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7218_MIC_1_AMP_EN_SHIFT 7 +#define DA7218_MIC_1_AMP_EN_MASK (0x1 << 7) + +/* DA7218_MIC_1_GAIN = 0xB5 */ +#define DA7218_MIC_1_AMP_GAIN_SHIFT 0 +#define DA7218_MIC_1_AMP_GAIN_MASK (0x7 << 0) +#define DA7218_MIC_AMP_GAIN_MAX 0x7 + +/* DA7218_MIC_1_SELECT = 0xB7 */ +#define DA7218_MIC_1_AMP_IN_SEL_SHIFT 0 +#define DA7218_MIC_1_AMP_IN_SEL_MASK (0x3 << 0) + +/* DA7218_MIC_2_CTRL = 0xB8 */ +#define DA7218_MIC_2_AMP_MUTE_EN_SHIFT 6 +#define DA7218_MIC_2_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7218_MIC_2_AMP_EN_SHIFT 7 +#define DA7218_MIC_2_AMP_EN_MASK (0x1 << 7) + +/* DA7218_MIC_2_GAIN = 0xB9 */ +#define DA7218_MIC_2_AMP_GAIN_SHIFT 0 +#define DA7218_MIC_2_AMP_GAIN_MASK (0x7 << 0) + +/* DA7218_MIC_2_SELECT = 0xBB */ +#define DA7218_MIC_2_AMP_IN_SEL_SHIFT 0 +#define DA7218_MIC_2_AMP_IN_SEL_MASK (0x3 << 0) + +/* DA7218_IN_1_HPF_FILTER_CTRL = 0xBC */ +#define DA7218_IN_1_VOICE_HPF_CORNER_SHIFT 0 +#define DA7218_IN_1_VOICE_HPF_CORNER_MASK (0x7 << 0) +#define DA7218_IN_VOICE_HPF_CORNER_MAX 8 +#define DA7218_IN_1_VOICE_EN_SHIFT 3 +#define DA7218_IN_1_VOICE_EN_MASK (0x1 << 3) +#define DA7218_IN_1_AUDIO_HPF_CORNER_SHIFT 4 +#define DA7218_IN_1_AUDIO_HPF_CORNER_MASK (0x3 << 4) +#define DA7218_IN_1_HPF_EN_SHIFT 7 +#define DA7218_IN_1_HPF_EN_MASK (0x1 << 7) + +/* DA7218_IN_2_HPF_FILTER_CTRL = 0xBD */ +#define DA7218_IN_2_VOICE_HPF_CORNER_SHIFT 0 +#define DA7218_IN_2_VOICE_HPF_CORNER_MASK (0x7 << 0) +#define DA7218_IN_2_VOICE_EN_SHIFT 3 +#define DA7218_IN_2_VOICE_EN_MASK (0x1 << 3) +#define DA7218_IN_2_AUDIO_HPF_CORNER_SHIFT 4 +#define DA7218_IN_2_AUDIO_HPF_CORNER_MASK (0x3 << 4) +#define DA7218_IN_2_HPF_EN_SHIFT 7 +#define DA7218_IN_2_HPF_EN_MASK (0x1 << 7) + +/* DA7218_ADC_1_CTRL = 0xC0 */ +#define DA7218_ADC_1_AAF_EN_SHIFT 2 +#define DA7218_ADC_1_AAF_EN_MASK (0x1 << 2) + +/* DA7218_ADC_2_CTRL = 0xC1 */ +#define DA7218_ADC_2_AAF_EN_SHIFT 2 +#define DA7218_ADC_2_AAF_EN_MASK (0x1 << 2) + +/* DA7218_ADC_MODE = 0xC2 */ +#define DA7218_ADC_LP_MODE_SHIFT 0 +#define DA7218_ADC_LP_MODE_MASK (0x1 << 0) +#define DA7218_ADC_LVLDET_MODE_SHIFT 1 +#define DA7218_ADC_LVLDET_MODE_MASK (0x1 << 1) +#define DA7218_ADC_LVLDET_AUTO_EXIT_SHIFT 2 +#define DA7218_ADC_LVLDET_AUTO_EXIT_MASK (0x1 << 2) + +/* DA7218_MIXOUT_L_CTRL = 0xCC */ +#define DA7218_MIXOUT_L_AMP_EN_SHIFT 7 +#define DA7218_MIXOUT_L_AMP_EN_MASK (0x1 << 7) + +/* DA7218_MIXOUT_L_GAIN = 0xCD */ +#define DA7218_MIXOUT_L_AMP_GAIN_SHIFT 0 +#define DA7218_MIXOUT_L_AMP_GAIN_MASK (0x3 << 0) +#define DA7218_MIXOUT_AMP_GAIN_MIN 0x1 +#define DA7218_MIXOUT_AMP_GAIN_MAX 0x3 + +/* DA7218_MIXOUT_R_CTRL = 0xCE */ +#define DA7218_MIXOUT_R_AMP_EN_SHIFT 7 +#define DA7218_MIXOUT_R_AMP_EN_MASK (0x1 << 7) + +/* DA7218_MIXOUT_R_GAIN = 0xCF */ +#define DA7218_MIXOUT_R_AMP_GAIN_SHIFT 0 +#define DA7218_MIXOUT_R_AMP_GAIN_MASK (0x3 << 0) + +/* DA7218_HP_L_CTRL = 0xD0 */ +#define DA7218_HP_L_AMP_MIN_GAIN_EN_SHIFT 2 +#define DA7218_HP_L_AMP_MIN_GAIN_EN_MASK (0x1 << 2) +#define DA7218_HP_L_AMP_OE_SHIFT 3 +#define DA7218_HP_L_AMP_OE_MASK (0x1 << 3) +#define DA7218_HP_L_AMP_ZC_EN_SHIFT 4 +#define DA7218_HP_L_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7218_HP_L_AMP_RAMP_EN_SHIFT 5 +#define DA7218_HP_L_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7218_HP_L_AMP_MUTE_EN_SHIFT 6 +#define DA7218_HP_L_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7218_HP_L_AMP_EN_SHIFT 7 +#define DA7218_HP_L_AMP_EN_MASK (0x1 << 7) +#define DA7218_HP_AMP_OE_MASK (0x1 << 3) + +/* DA7218_HP_L_GAIN = 0xD1 */ +#define DA7218_HP_L_AMP_GAIN_SHIFT 0 +#define DA7218_HP_L_AMP_GAIN_MASK (0x3F << 0) +#define DA7218_HP_AMP_GAIN_MIN 0x15 +#define DA7218_HP_AMP_GAIN_MAX 0x3F + +/* DA7218_HP_R_CTRL = 0xD2 */ +#define DA7218_HP_R_AMP_MIN_GAIN_EN_SHIFT 2 +#define DA7218_HP_R_AMP_MIN_GAIN_EN_MASK (0x1 << 2) +#define DA7218_HP_R_AMP_OE_SHIFT 3 +#define DA7218_HP_R_AMP_OE_MASK (0x1 << 3) +#define DA7218_HP_R_AMP_ZC_EN_SHIFT 4 +#define DA7218_HP_R_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7218_HP_R_AMP_RAMP_EN_SHIFT 5 +#define DA7218_HP_R_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7218_HP_R_AMP_MUTE_EN_SHIFT 6 +#define DA7218_HP_R_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7218_HP_R_AMP_EN_SHIFT 7 +#define DA7218_HP_R_AMP_EN_MASK (0x1 << 7) + +/* DA7218_HP_R_GAIN = 0xD3 */ +#define DA7218_HP_R_AMP_GAIN_SHIFT 0 +#define DA7218_HP_R_AMP_GAIN_MASK (0x3F << 0) + +/* DA7218_HP_SNGL_CTRL = 0xD4 */ +#define DA7218_HP_AMP_STEREO_DETECT_STATUS_SHIFT 0 +#define DA7218_HP_AMP_STEREO_DETECT_STATUS_MASK (0x1 << 0) +#define DA7218_HPL_AMP_LOAD_DETECT_STATUS_SHIFT 1 +#define DA7218_HPL_AMP_LOAD_DETECT_STATUS_MASK (0x1 << 1) +#define DA7218_HPR_AMP_LOAD_DETECT_STATUS_SHIFT 2 +#define DA7218_HPR_AMP_LOAD_DETECT_STATUS_MASK (0x1 << 2) +#define DA7218_HP_AMP_LOAD_DETECT_EN_SHIFT 6 +#define DA7218_HP_AMP_LOAD_DETECT_EN_MASK (0x1 << 6) +#define DA7218_HP_AMP_STEREO_DETECT_EN_SHIFT 7 +#define DA7218_HP_AMP_STEREO_DETECT_EN_MASK (0x1 << 7) + +/* DA7218_HP_DIFF_CTRL = 0xD5 */ +#define DA7218_HP_AMP_DIFF_MODE_EN_SHIFT 0 +#define DA7218_HP_AMP_DIFF_MODE_EN_MASK (0x1 << 0) +#define DA7218_HP_AMP_SINGLE_SUPPLY_EN_SHIFT 4 +#define DA7218_HP_AMP_SINGLE_SUPPLY_EN_MASK (0x1 << 4) + +/* DA7218_HP_DIFF_UNLOCK = 0xD7 */ +#define DA7218_HP_DIFF_UNLOCK_SHIFT 0 +#define DA7218_HP_DIFF_UNLOCK_MASK (0x1 << 0) +#define DA7218_HP_DIFF_UNLOCK_VAL 0xC3 + +/* DA7218_HPLDET_JACK = 0xD8 */ +#define DA7218_HPLDET_JACK_RATE_SHIFT 0 +#define DA7218_HPLDET_JACK_RATE_MASK (0x7 << 0) +#define DA7218_HPLDET_JACK_DEBOUNCE_SHIFT 3 +#define DA7218_HPLDET_JACK_DEBOUNCE_MASK (0x3 << 3) +#define DA7218_HPLDET_JACK_THR_SHIFT 5 +#define DA7218_HPLDET_JACK_THR_MASK (0x3 << 5) +#define DA7218_HPLDET_JACK_EN_SHIFT 7 +#define DA7218_HPLDET_JACK_EN_MASK (0x1 << 7) + +/* DA7218_HPLDET_CTRL = 0xD9 */ +#define DA7218_HPLDET_COMP_INV_SHIFT 0 +#define DA7218_HPLDET_COMP_INV_MASK (0x1 << 0) +#define DA7218_HPLDET_HYST_EN_SHIFT 1 +#define DA7218_HPLDET_HYST_EN_MASK (0x1 << 1) +#define DA7218_HPLDET_DISCHARGE_EN_SHIFT 7 +#define DA7218_HPLDET_DISCHARGE_EN_MASK (0x1 << 7) + +/* DA7218_HPLDET_TEST = 0xDA */ +#define DA7218_HPLDET_COMP_STS_SHIFT 4 +#define DA7218_HPLDET_COMP_STS_MASK (0x1 << 4) + +/* DA7218_REFERENCES = 0xDC */ +#define DA7218_BIAS_EN_SHIFT 3 +#define DA7218_BIAS_EN_MASK (0x1 << 3) + +/* DA7218_IO_CTRL = 0xE0 */ +#define DA7218_IO_VOLTAGE_LEVEL_SHIFT 0 +#define DA7218_IO_VOLTAGE_LEVEL_MASK (0x1 << 0) +#define DA7218_IO_VOLTAGE_LEVEL_2_5V_3_6V 0 +#define DA7218_IO_VOLTAGE_LEVEL_1_5V_2_5V 1 + +/* DA7218_LDO_CTRL = 0xE1 */ +#define DA7218_LDO_LEVEL_SELECT_SHIFT 4 +#define DA7218_LDO_LEVEL_SELECT_MASK (0x3 << 4) +#define DA7218_LDO_EN_SHIFT 7 +#define DA7218_LDO_EN_MASK (0x1 << 7) + +/* DA7218_SIDETONE_CTRL = 0xE4 */ +#define DA7218_SIDETONE_MUTE_EN_SHIFT 6 +#define DA7218_SIDETONE_MUTE_EN_MASK (0x1 << 6) +#define DA7218_SIDETONE_FILTER_EN_SHIFT 7 +#define DA7218_SIDETONE_FILTER_EN_MASK (0x1 << 7) + +/* DA7218_SIDETONE_IN_SELECT = 0xE5 */ +#define DA7218_SIDETONE_IN_SELECT_SHIFT 0 +#define DA7218_SIDETONE_IN_SELECT_MASK (0x3 << 0) +#define DA7218_SIDETONE_IN_SELECT_MAX 4 + +/* DA7218_SIDETONE_GAIN = 0xE6 */ +#define DA7218_SIDETONE_GAIN_SHIFT 0 +#define DA7218_SIDETONE_GAIN_MASK (0x1F << 0) + +/* DA7218_DROUTING_ST_OUTFILT_1L = 0xE8 */ +#define DA7218_OUTFILT_ST_1L_SRC_SHIFT 0 +#define DA7218_OUTFILT_ST_1L_SRC_MASK (0x7 << 0) +#define DA7218_DMIX_ST_SRC_OUTFILT1L 0 +#define DA7218_DMIX_ST_SRC_OUTFILT1R 1 +#define DA7218_DMIX_ST_SRC_SIDETONE 2 + +/* DA7218_DROUTING_ST_OUTFILT_1R = 0xE9 */ +#define DA7218_OUTFILT_ST_1R_SRC_SHIFT 0 +#define DA7218_OUTFILT_ST_1R_SRC_MASK (0x7 << 0) + +/* DA7218_SIDETONE_BIQ_3STAGE_DATA = 0xEA */ +#define DA7218_SIDETONE_BIQ_3STAGE_DATA_SHIFT 0 +#define DA7218_SIDETONE_BIQ_3STAGE_DATA_MASK (0xFF << 0) + +/* DA7218_SIDETONE_BIQ_3STAGE_ADDR = 0xEB */ +#define DA7218_SIDETONE_BIQ_3STAGE_ADDR_SHIFT 0 +#define DA7218_SIDETONE_BIQ_3STAGE_ADDR_MASK (0x1F << 0) +#define DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE 30 + +/* DA7218_EVENT_STATUS = 0xEC */ +#define DA7218_HPLDET_JACK_STS_SHIFT 7 +#define DA7218_HPLDET_JACK_STS_MASK (0x1 << 7) + +/* DA7218_EVENT = 0xED */ +#define DA7218_LVL_DET_EVENT_SHIFT 0 +#define DA7218_LVL_DET_EVENT_MASK (0x1 << 0) +#define DA7218_HPLDET_JACK_EVENT_SHIFT 7 +#define DA7218_HPLDET_JACK_EVENT_MASK (0x1 << 7) + +/* DA7218_EVENT_MASK = 0xEE */ +#define DA7218_LVL_DET_EVENT_MSK_SHIFT 0 +#define DA7218_LVL_DET_EVENT_MSK_MASK (0x1 << 0) +#define DA7218_HPLDET_JACK_EVENT_IRQ_MSK_SHIFT 7 +#define DA7218_HPLDET_JACK_EVENT_IRQ_MSK_MASK (0x1 << 7) + +/* DA7218_DMIC_1_CTRL = 0xF0 */ +#define DA7218_DMIC_1_DATA_SEL_SHIFT 0 +#define DA7218_DMIC_1_DATA_SEL_MASK (0x1 << 0) +#define DA7218_DMIC_1_SAMPLEPHASE_SHIFT 1 +#define DA7218_DMIC_1_SAMPLEPHASE_MASK (0x1 << 1) +#define DA7218_DMIC_1_CLK_RATE_SHIFT 2 +#define DA7218_DMIC_1_CLK_RATE_MASK (0x1 << 2) +#define DA7218_DMIC_1L_EN_SHIFT 6 +#define DA7218_DMIC_1L_EN_MASK (0x1 << 6) +#define DA7218_DMIC_1R_EN_SHIFT 7 +#define DA7218_DMIC_1R_EN_MASK (0x1 << 7) + +/* DA7218_DMIC_2_CTRL = 0xF1 */ +#define DA7218_DMIC_2_DATA_SEL_SHIFT 0 +#define DA7218_DMIC_2_DATA_SEL_MASK (0x1 << 0) +#define DA7218_DMIC_2_SAMPLEPHASE_SHIFT 1 +#define DA7218_DMIC_2_SAMPLEPHASE_MASK (0x1 << 1) +#define DA7218_DMIC_2_CLK_RATE_SHIFT 2 +#define DA7218_DMIC_2_CLK_RATE_MASK (0x1 << 2) +#define DA7218_DMIC_2L_EN_SHIFT 6 +#define DA7218_DMIC_2L_EN_MASK (0x1 << 6) +#define DA7218_DMIC_2R_EN_SHIFT 7 +#define DA7218_DMIC_2R_EN_MASK (0x1 << 7) + +/* DA7218_IN_1L_GAIN = 0xF4 */ +#define DA7218_IN_1L_DIGITAL_GAIN_SHIFT 0 +#define DA7218_IN_1L_DIGITAL_GAIN_MASK (0x7F << 0) +#define DA7218_IN_DIGITAL_GAIN_MAX 0x7F + +/* DA7218_IN_1R_GAIN = 0xF5 */ +#define DA7218_IN_1R_DIGITAL_GAIN_SHIFT 0 +#define DA7218_IN_1R_DIGITAL_GAIN_MASK (0x7F << 0) + +/* DA7218_IN_2L_GAIN = 0xF6 */ +#define DA7218_IN_2L_DIGITAL_GAIN_SHIFT 0 +#define DA7218_IN_2L_DIGITAL_GAIN_MASK (0x7F << 0) + +/* DA7218_IN_2R_GAIN = 0xF7 */ +#define DA7218_IN_2R_DIGITAL_GAIN_SHIFT 0 +#define DA7218_IN_2R_DIGITAL_GAIN_MASK (0x7F << 0) + +/* DA7218_OUT_1L_GAIN = 0xF8 */ +#define DA7218_OUT_1L_DIGITAL_GAIN_SHIFT 0 +#define DA7218_OUT_1L_DIGITAL_GAIN_MASK (0xFF << 0) +#define DA7218_OUT_DIGITAL_GAIN_MIN 0x0 +#define DA7218_OUT_DIGITAL_GAIN_MAX 0x97 + +/* DA7218_OUT_1R_GAIN = 0xF9 */ +#define DA7218_OUT_1R_DIGITAL_GAIN_SHIFT 0 +#define DA7218_OUT_1R_DIGITAL_GAIN_MASK (0xFF << 0) + +/* DA7218_MICBIAS_CTRL = 0xFC */ +#define DA7218_MICBIAS_1_LEVEL_SHIFT 0 +#define DA7218_MICBIAS_1_LEVEL_MASK (0x7 << 0) +#define DA7218_MICBIAS_1_LP_MODE_SHIFT 3 +#define DA7218_MICBIAS_1_LP_MODE_MASK (0x1 << 3) +#define DA7218_MICBIAS_2_LEVEL_SHIFT 4 +#define DA7218_MICBIAS_2_LEVEL_MASK (0x7 << 4) +#define DA7218_MICBIAS_2_LP_MODE_SHIFT 7 +#define DA7218_MICBIAS_2_LP_MODE_MASK (0x1 << 7) + +/* DA7218_MICBIAS_EN = 0xFD */ +#define DA7218_MICBIAS_1_EN_SHIFT 0 +#define DA7218_MICBIAS_1_EN_MASK (0x1 << 0) +#define DA7218_MICBIAS_2_EN_SHIFT 4 +#define DA7218_MICBIAS_2_EN_MASK (0x1 << 4) + + +/* + * General defines & data + */ + +/* Register inversion */ +#define DA7218_NO_INVERT 0 +#define DA7218_INVERT 1 + +/* Byte related defines */ +#define DA7218_BYTE_SHIFT 8 +#define DA7218_BYTE_MASK 0xFF +#define DA7218_2BYTE_SHIFT 16 +#define DA7218_2BYTE_MASK 0xFFFF + +/* PLL Output Frequencies */ +#define DA7218_PLL_FREQ_OUT_90316 90316800 +#define DA7218_PLL_FREQ_OUT_98304 98304000 + +/* ALC Calibration */ +#define DA7218_ALC_CALIB_DELAY_MIN 2500 +#define DA7218_ALC_CALIB_DELAY_MAX 5000 +#define DA7218_ALC_CALIB_MAX_TRIES 5 + +/* Ref Oscillator */ +#define DA7218_REF_OSC_CHECK_DELAY_MIN 5000 +#define DA7218_REF_OSC_CHECK_DELAY_MAX 10000 +#define DA7218_REF_OSC_CHECK_TRIES 4 + +/* SRM */ +#define DA7218_SRM_CHECK_DELAY 50 +#define DA7218_SRM_CHECK_TRIES 8 + +/* Mic Level Detect */ +#define DA7218_MIC_LVL_DET_DELAY 50 + +enum da7218_biq_cfg { + DA7218_BIQ_CFG_DATA = 0, + DA7218_BIQ_CFG_ADDR, + DA7218_BIQ_CFG_SIZE, +}; + +enum da7218_clk_src { + DA7218_CLKSRC_MCLK = 0, + DA7218_CLKSRC_MCLK_SQR, +}; + +enum da7218_sys_clk { + DA7218_SYSCLK_MCLK = 0, + DA7218_SYSCLK_PLL, + DA7218_SYSCLK_PLL_SRM, + DA7218_SYSCLK_PLL_32KHZ +}; + +enum da7218_dev_id { + DA7217_DEV_ID = 0, + DA7218_DEV_ID, +}; + +/* Regulators */ +enum da7218_supplies { + DA7218_SUPPLY_VDD = 0, + DA7218_SUPPLY_VDDMIC, + DA7218_SUPPLY_VDDIO, + DA7218_NUM_SUPPLIES, +}; + +/* Private data */ +struct da7218_priv { + struct da7218_pdata *pdata; + + struct regulator_bulk_data supplies[DA7218_NUM_SUPPLIES]; + struct regmap *regmap; + int dev_id; + + struct snd_soc_jack *jack; + int irq; + + struct clk *mclk; + unsigned int mclk_rate; + + bool hp_single_supply; + bool master; + u8 alc_en; + u8 in_filt_en; + u8 mic_lvl_det_en; + + u8 biq_5stage_coeff[DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE]; + u8 stbiq_3stage_coeff[DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE]; +}; + +/* HP detect control */ +int da7218_hpldet(struct snd_soc_codec *codec, struct snd_soc_jack *jack); + +#endif /* _DA7218_H */ diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index f238c1e8a69c..c6d3b32bb4ae 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -968,10 +968,11 @@ static const struct snd_soc_dapm_route da7219_audio_map[] = { {"Mixin PGA", NULL, "Mic PGA"}, {"ADC", NULL, "Mixin PGA"}, - {"Sidetone Filter", NULL, "ADC"}, {"Mixer In", NULL, "Mixer In Supply"}, {"Mixer In", "Mic Switch", "ADC"}, + {"Sidetone Filter", NULL, "Mixer In"}, + {"Tone Generator", NULL, "TONE"}, DA7219_OUT_DAI_MUX_ROUTES("Out DAIL Mux"), @@ -1073,11 +1074,8 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, u32 freq_ref; u64 frac_div; - /* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */ - if (da7219->mclk_rate == 32768) { - indiv_bits = DA7219_PLL_INDIV_2_5_MHZ; - indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL; - } else if (da7219->mclk_rate < 2000000) { + /* Verify 2MHz - 54MHz MCLK provided, and set input divider */ + if (da7219->mclk_rate < 2000000) { dev_err(codec->dev, "PLL input clock %d below valid range\n", da7219->mclk_rate); return -EINVAL; @@ -1118,9 +1116,6 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, case DA7219_SYSCLK_PLL_SRM: pll_ctrl |= DA7219_PLL_MODE_SRM; break; - case DA7219_SYSCLK_PLL_32KHZ: - pll_ctrl |= DA7219_PLL_MODE_32KHZ; - break; default: dev_err(codec->dev, "Invalid PLL config\n"); return -EINVAL; @@ -1306,7 +1301,7 @@ static int da7219_hw_params(struct snd_pcm_substream *substream, } channels = params_channels(params); - if ((channels < 1) | (channels > DA7219_DAI_CH_NUM_MAX)) { + if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) { dev_err(codec->dev, "Invalid number of channels, only 1 to %d supported\n", DA7219_DAI_CH_NUM_MAX); @@ -1405,28 +1400,12 @@ static const struct of_device_id da7219_of_match[] = { }; MODULE_DEVICE_TABLE(of, da7219_of_match); -static enum da7219_ldo_lvl_sel da7219_of_ldo_lvl(struct snd_soc_codec *codec, - u32 val) -{ - switch (val) { - case 1050: - return DA7219_LDO_LVL_SEL_1_05V; - case 1100: - return DA7219_LDO_LVL_SEL_1_10V; - case 1200: - return DA7219_LDO_LVL_SEL_1_20V; - case 1400: - return DA7219_LDO_LVL_SEL_1_40V; - default: - dev_warn(codec->dev, "Invalid LDO level"); - return DA7219_LDO_LVL_SEL_1_05V; - } -} - static enum da7219_micbias_voltage da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val) { switch (val) { + case 1600: + return DA7219_MICBIAS_1_6V; case 1800: return DA7219_MICBIAS_1_8V; case 2000: @@ -1469,9 +1448,6 @@ static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec) if (!pdata) return NULL; - if (of_property_read_u32(np, "dlg,ldo-lvl", &of_val32) >= 0) - pdata->ldo_lvl_sel = da7219_of_ldo_lvl(codec, of_val32); - if (of_property_read_u32(np, "dlg,micbias-lvl", &of_val32) >= 0) pdata->micbias_lvl = da7219_of_micbias_lvl(codec, of_val32); else @@ -1516,24 +1492,13 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, DA7219_REFERENCES, DA7219_BIAS_EN_MASK, DA7219_BIAS_EN_MASK); - - /* Enable Internal Digital LDO */ - snd_soc_update_bits(codec, DA7219_LDO_CTRL, - DA7219_LDO_EN_MASK, - DA7219_LDO_EN_MASK); } break; case SND_SOC_BIAS_OFF: - /* Only disable if jack detection not active */ - if (!da7219->aad->jack) { - /* Bypass Internal Digital LDO */ - snd_soc_update_bits(codec, DA7219_LDO_CTRL, - DA7219_LDO_EN_MASK, 0); - - /* Master bias */ + /* Only disable master bias if jack detection not active */ + if (!da7219->aad->jack) snd_soc_update_bits(codec, DA7219_REFERENCES, DA7219_BIAS_EN_MASK, 0); - } /* MCLK */ if (da7219->mclk) @@ -1600,21 +1565,9 @@ static void da7219_handle_pdata(struct snd_soc_codec *codec) if (pdata) { u8 micbias_lvl = 0; - /* Internal LDO */ - switch (pdata->ldo_lvl_sel) { - case DA7219_LDO_LVL_SEL_1_05V: - case DA7219_LDO_LVL_SEL_1_10V: - case DA7219_LDO_LVL_SEL_1_20V: - case DA7219_LDO_LVL_SEL_1_40V: - snd_soc_update_bits(codec, DA7219_LDO_CTRL, - DA7219_LDO_LEVEL_SELECT_MASK, - (pdata->ldo_lvl_sel << - DA7219_LDO_LEVEL_SELECT_SHIFT)); - break; - } - /* Mic Bias voltages */ switch (pdata->micbias_lvl) { + case DA7219_MICBIAS_1_6V: case DA7219_MICBIAS_1_8V: case DA7219_MICBIAS_2_0V: case DA7219_MICBIAS_2_2V: @@ -1662,10 +1615,12 @@ static int da7219_probe(struct snd_soc_codec *codec) /* Check if MCLK provided */ da7219->mclk = devm_clk_get(codec->dev, "mclk"); if (IS_ERR(da7219->mclk)) { - if (PTR_ERR(da7219->mclk) != -ENOENT) - return PTR_ERR(da7219->mclk); - else + if (PTR_ERR(da7219->mclk) != -ENOENT) { + ret = PTR_ERR(da7219->mclk); + goto err_disable_reg; + } else { da7219->mclk = NULL; + } } /* Default PC counter to free-running */ @@ -1693,7 +1648,16 @@ static int da7219_probe(struct snd_soc_codec *codec) snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK); /* Initialise AAD block */ - return da7219_aad_init(codec); + ret = da7219_aad_init(codec); + if (ret) + goto err_disable_reg; + + return 0; + +err_disable_reg: + regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies); + + return ret; } static int da7219_remove(struct snd_soc_codec *codec) @@ -1776,7 +1740,7 @@ static struct reg_default da7219_reg_defaults[] = { { DA7219_DIG_ROUTING_DAC, 0x32 }, { DA7219_DAI_OFFSET_LOWER, 0x00 }, { DA7219_DAI_OFFSET_UPPER, 0x00 }, - { DA7219_REFERENCES, 0x00 }, + { DA7219_REFERENCES, 0x08 }, { DA7219_MIXIN_L_SELECT, 0x00 }, { DA7219_MIXIN_L_GAIN, 0x03 }, { DA7219_ADC_L_GAIN, 0x6F }, @@ -1811,7 +1775,6 @@ static struct reg_default da7219_reg_defaults[] = { { DA7219_CHIP_ID1, 0x23 }, { DA7219_CHIP_ID2, 0x93 }, { DA7219_CHIP_REVISION, 0x00 }, - { DA7219_LDO_CTRL, 0x00 }, { DA7219_IO_CTRL, 0x00 }, { DA7219_GAIN_RAMP_CTRL, 0x00 }, { DA7219_PC_COUNT, 0x02 }, diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h index b514268c6c56..5a787e738084 100644 --- a/sound/soc/codecs/da7219.h +++ b/sound/soc/codecs/da7219.h @@ -85,7 +85,6 @@ #define DA7219_CHIP_ID1 0x81 #define DA7219_CHIP_ID2 0x82 #define DA7219_CHIP_REVISION 0x83 -#define DA7219_LDO_CTRL 0x90 #define DA7219_IO_CTRL 0x91 #define DA7219_GAIN_RAMP_CTRL 0x92 #define DA7219_PC_COUNT 0x94 @@ -207,7 +206,6 @@ #define DA7219_PLL_MODE_BYPASS (0x0 << 6) #define DA7219_PLL_MODE_NORMAL (0x1 << 6) #define DA7219_PLL_MODE_SRM (0x2 << 6) -#define DA7219_PLL_MODE_32KHZ (0x3 << 6) /* DA7219_PLL_FRAC_TOP = 0x22 */ #define DA7219_PLL_FBDIV_FRAC_TOP_SHIFT 0 @@ -569,12 +567,6 @@ #define DA7219_CHIP_MAJOR_SHIFT 4 #define DA7219_CHIP_MAJOR_MASK (0xF << 4) -/* DA7219_LDO_CTRL = 0x90 */ -#define DA7219_LDO_LEVEL_SELECT_SHIFT 4 -#define DA7219_LDO_LEVEL_SELECT_MASK (0x3 << 4) -#define DA7219_LDO_EN_SHIFT 7 -#define DA7219_LDO_EN_MASK (0x1 << 7) - /* DA7219_IO_CTRL = 0x91 */ #define DA7219_IO_VOLTAGE_LEVEL_SHIFT 0 #define DA7219_IO_VOLTAGE_LEVEL_MASK (0x1 << 0) @@ -787,7 +779,6 @@ enum da7219_sys_clk { DA7219_SYSCLK_MCLK = 0, DA7219_SYSCLK_PLL, DA7219_SYSCLK_PLL_SRM, - DA7219_SYSCLK_PLL_32KHZ }; /* Regulators */ diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 969e337dc17c..afa6c5db9dcc 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -85,7 +85,15 @@ static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0); static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0); static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0); -static const int deemph_settings[] = { 0, 32000, 44100, 48000 }; +static const struct { + int rate; + unsigned int val; +} deemph_settings[] = { + { 0, ES8328_DACCONTROL6_DEEMPH_OFF }, + { 32000, ES8328_DACCONTROL6_DEEMPH_32k }, + { 44100, ES8328_DACCONTROL6_DEEMPH_44_1k }, + { 48000, ES8328_DACCONTROL6_DEEMPH_48k }, +}; static int es8328_set_deemph(struct snd_soc_codec *codec) { @@ -97,21 +105,22 @@ static int es8328_set_deemph(struct snd_soc_codec *codec) * rate. */ if (es8328->deemph) { - best = 1; - for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { - if (abs(deemph_settings[i] - es8328->playback_fs) < - abs(deemph_settings[best] - es8328->playback_fs)) + best = 0; + for (i = 1; i < ARRAY_SIZE(deemph_settings); i++) { + if (abs(deemph_settings[i].rate - es8328->playback_fs) < + abs(deemph_settings[best].rate - es8328->playback_fs)) best = i; } - val = best << 1; + val = deemph_settings[best].val; } else { - val = 0; + val = ES8328_DACCONTROL6_DEEMPH_OFF; } dev_dbg(codec->dev, "Set deemphasis %d\n", val); - return snd_soc_update_bits(codec, ES8328_DACCONTROL6, 0x6, val); + return snd_soc_update_bits(codec, ES8328_DACCONTROL6, + ES8328_DACCONTROL6_DEEMPH_MASK, val); } static int es8328_get_deemph(struct snd_kcontrol *kcontrol, @@ -205,18 +214,18 @@ static const struct snd_kcontrol_new es8328_right_line_controls = /* Left Mixer */ static const struct snd_kcontrol_new es8328_left_mixer_controls[] = { - SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 8, 1, 0), - SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 7, 1, 0), - SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 8, 1, 0), - SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 7, 1, 0), + SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 7, 1, 0), + SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 6, 1, 0), + SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 7, 1, 0), + SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 6, 1, 0), }; /* Right Mixer */ static const struct snd_kcontrol_new es8328_right_mixer_controls[] = { - SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 8, 1, 0), - SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 7, 1, 0), - SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 8, 1, 0), - SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 7, 1, 0), + SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 7, 1, 0), + SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 6, 1, 0), + SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 7, 1, 0), + SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 6, 1, 0), }; static const char * const es8328_pga_sel[] = { diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h index cb36afe10c0e..156c748c89c7 100644 --- a/sound/soc/codecs/es8328.h +++ b/sound/soc/codecs/es8328.h @@ -153,6 +153,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap); #define ES8328_DACCONTROL6_CLICKFREE (1 << 3) #define ES8328_DACCONTROL6_DAC_INVR (1 << 4) #define ES8328_DACCONTROL6_DAC_INVL (1 << 5) +#define ES8328_DACCONTROL6_DEEMPH_MASK (3 << 6) #define ES8328_DACCONTROL6_DEEMPH_OFF (0 << 6) #define ES8328_DACCONTROL6_DEEMPH_32k (1 << 6) #define ES8328_DACCONTROL6_DEEMPH_44_1k (2 << 6) diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c new file mode 100644 index 000000000000..1a2f33b4abfc --- /dev/null +++ b/sound/soc/codecs/hdac_hdmi.c @@ -0,0 +1,659 @@ +/* + * hdac_hdmi.c - ASoc HDA-HDMI codec driver for Intel platforms + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Samreen Nilofer <samreen.nilofer@intel.com> + * Subhransu S. Prusty <subhransu.s.prusty@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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/delay.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/hdmi.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/hdaudio_ext.h> +#include <sound/hda_i915.h> +#include "../../hda/local.h" + +#define AMP_OUT_MUTE 0xb080 +#define AMP_OUT_UNMUTE 0xb000 +#define PIN_OUT (AC_PINCTL_OUT_EN) + +#define HDA_MAX_CONNECTIONS 32 + +struct hdac_hdmi_cvt_params { + unsigned int channels_min; + unsigned int channels_max; + u32 rates; + u64 formats; + unsigned int maxbps; +}; + +struct hdac_hdmi_cvt { + hda_nid_t nid; + struct hdac_hdmi_cvt_params params; +}; + +struct hdac_hdmi_pin { + hda_nid_t nid; + int num_mux_nids; + hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; +}; + +struct hdac_hdmi_dai_pin_map { + int dai_id; + struct hdac_hdmi_pin pin; + struct hdac_hdmi_cvt cvt; +}; + +struct hdac_hdmi_priv { + hda_nid_t pin_nid[3]; + hda_nid_t cvt_nid[3]; + struct hdac_hdmi_dai_pin_map dai_map[3]; +}; + +static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev) +{ + struct hdac_device *hdac = container_of(dev, struct hdac_device, dev); + + return container_of(hdac, struct hdac_ext_device, hdac); +} + +static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac, + hda_nid_t cvt_nid, hda_nid_t pin_nid, + u32 stream_tag, int format) +{ + unsigned int val; + + dev_dbg(&hdac->hdac.dev, "cvt nid %d pnid %d stream %d format 0x%x\n", + cvt_nid, pin_nid, stream_tag, format); + + val = (stream_tag << 4); + + snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0, + AC_VERB_SET_CHANNEL_STREAMID, val); + snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0, + AC_VERB_SET_STREAM_FORMAT, format); + + return 0; +} + +static void +hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid, + int packet_index, int byte_index) +{ + int val; + + val = (packet_index << 5) | (byte_index & 0x1f); + + snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, + AC_VERB_SET_HDMI_DIP_INDEX, val); +} + +static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, + hda_nid_t cvt_nid, hda_nid_t pin_nid) +{ + uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE]; + struct hdmi_audio_infoframe frame; + u8 *dip = (u8 *)&frame; + int ret; + int i; + + hdmi_audio_infoframe_init(&frame); + + /* Default stereo for now */ + frame.channels = 2; + + /* setup channel count */ + snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0, + AC_VERB_SET_CVT_CHAN_COUNT, frame.channels - 1); + + ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + /* stop infoframe transmission */ + hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0); + snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, + AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE); + + + /* Fill infoframe. Index auto-incremented */ + hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0); + for (i = 0; i < sizeof(frame); i++) + snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, + AC_VERB_SET_HDMI_DIP_DATA, dip[i]); + + /* Start infoframe */ + hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0); + snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, + AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST); + + return 0; +} + +static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev, + struct hdac_hdmi_dai_pin_map *dai_map, unsigned int pwr_state) +{ + /* Power up pin widget */ + if (!snd_hdac_check_power_state(&edev->hdac, dai_map->pin.nid, pwr_state)) + snd_hdac_codec_write(&edev->hdac, dai_map->pin.nid, 0, + AC_VERB_SET_POWER_STATE, pwr_state); + + /* Power up converter */ + if (!snd_hdac_check_power_state(&edev->hdac, dai_map->cvt.nid, pwr_state)) + snd_hdac_codec_write(&edev->hdac, dai_map->cvt.nid, 0, + AC_VERB_SET_POWER_STATE, pwr_state); +} + +static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + 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_ext_dma_params *dd; + int ret; + + if (dai->id > 0) { + dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n"); + return -ENODEV; + } + + dai_map = &hdmi->dai_map[dai->id]; + + 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); + + ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt.nid, + dai_map->pin.nid); + if (ret < 0) + return ret; + + return hdac_hdmi_setup_stream(hdac, dai_map->cvt.nid, dai_map->pin.nid, + dd->stream_tag, dd->format); +} + +static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai) +{ + struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); + struct hdac_ext_dma_params *dd; + + if (dai->id > 0) { + dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n"); + return -ENODEV; + } + + dd = kzalloc(sizeof(*dd), GFP_KERNEL); + if (!dd) + return -ENOMEM; + dd->format = snd_hdac_calc_stream_format(params_rate(hparams), + params_channels(hparams), params_format(hparams), + 24, 0); + + snd_soc_dai_set_dma_data(dai, substream, (void *)dd); + + return 0; +} + +static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai); + struct hdac_ext_dma_params *dd; + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_dai_pin_map *dai_map; + + dai_map = &hdmi->dai_map[dai->id]; + + snd_hdac_codec_write(&edev->hdac, dai_map->cvt.nid, 0, + AC_VERB_SET_CHANNEL_STREAMID, 0); + snd_hdac_codec_write(&edev->hdac, dai_map->cvt.nid, 0, + AC_VERB_SET_STREAM_FORMAT, 0); + + dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream); + snd_soc_dai_set_dma_data(dai, substream, NULL); + + kfree(dd); + + return 0; +} + +static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + 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; + int val; + + if (dai->id > 0) { + dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n"); + return -ENODEV; + } + + dai_map = &hdmi->dai_map[dai->id]; + + val = snd_hdac_codec_read(&hdac->hdac, dai_map->pin.nid, 0, + AC_VERB_GET_PIN_SENSE, 0); + dev_info(&hdac->hdac.dev, "Val for AC_VERB_GET_PIN_SENSE: %x\n", val); + + if ((!(val & AC_PINSENSE_PRESENCE)) || (!(val & AC_PINSENSE_ELDV))) { + dev_err(&hdac->hdac.dev, "Monitor presence invalid with val: %x\n", val); + return -ENODEV; + } + + hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0); + + snd_hdac_codec_write(&hdac->hdac, dai_map->pin.nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, 2); + + return 0; +} + +static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + 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; + + dai_map = &hdmi->dai_map[dai->id]; + + hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3); + + snd_hdac_codec_write(&hdac->hdac, dai_map->pin.nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); +} + +static int +hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt) +{ + int err; + + /* Only stereo supported as of now */ + cvt->params.channels_min = cvt->params.channels_max = 2; + + err = snd_hdac_query_supported_pcm(hdac, cvt->nid, + &cvt->params.rates, + &cvt->params.formats, + &cvt->params.maxbps); + if (err < 0) + dev_err(&hdac->dev, + "Failed to query pcm params for nid %d: %d\n", + cvt->nid, err); + + return err; +} + +static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac, + struct hdac_hdmi_pin *pin) +{ + if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) { + dev_warn(&hdac->hdac.dev, + "HDMI: pin %d wcaps %#x does not support connection list\n", + pin->nid, get_wcaps(&hdac->hdac, pin->nid)); + return -EINVAL; + } + + pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid, + pin->mux_nids, HDA_MAX_CONNECTIONS); + if (pin->num_mux_nids == 0) { + dev_err(&hdac->hdac.dev, "No connections found\n"); + return -ENODEV; + } + + return pin->num_mux_nids; +} + +static void hdac_hdmi_fill_widget_info(struct snd_soc_dapm_widget *w, + enum snd_soc_dapm_type id, + const char *wname, const char *stream) +{ + w->id = id; + w->name = wname; + w->sname = stream; + w->reg = SND_SOC_NOPM; + w->shift = 0; + w->kcontrol_news = NULL; + w->num_kcontrols = 0; + w->priv = NULL; +} + +static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route, + const char *sink, const char *control, const char *src) +{ + route->sink = sink; + route->source = src; + route->control = control; + route->connected = NULL; +} + +static void create_fill_widget_route_map(struct snd_soc_dapm_context *dapm, + struct hdac_hdmi_dai_pin_map *dai_map) +{ + struct snd_soc_dapm_route route[1]; + struct snd_soc_dapm_widget widgets[2] = { {0} }; + + memset(&route, 0, sizeof(route)); + + hdac_hdmi_fill_widget_info(&widgets[0], snd_soc_dapm_output, + "hif1 Output", NULL); + hdac_hdmi_fill_widget_info(&widgets[1], snd_soc_dapm_aif_in, + "Coverter 1", "hif1"); + + hdac_hdmi_fill_route(&route[0], "hif1 Output", NULL, "Coverter 1"); + + snd_soc_dapm_new_controls(dapm, widgets, ARRAY_SIZE(widgets)); + snd_soc_dapm_add_routes(dapm, route, ARRAY_SIZE(route)); +} + +static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev, + struct hdac_hdmi_dai_pin_map *dai_map, + hda_nid_t pin_nid, hda_nid_t cvt_nid, int dai_id) +{ + int ret; + + dai_map->dai_id = dai_id; + dai_map->pin.nid = pin_nid; + + ret = hdac_hdmi_query_pin_connlist(edev, &dai_map->pin); + if (ret < 0) { + dev_err(&edev->hdac.dev, + "Error querying connection list: %d\n", ret); + return ret; + } + + dai_map->cvt.nid = cvt_nid; + + /* Enable out path for this pin widget */ + snd_hdac_codec_write(&edev->hdac, pin_nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + + /* Enable transmission */ + snd_hdac_codec_write(&edev->hdac, cvt_nid, 0, + AC_VERB_SET_DIGI_CONVERT_1, 1); + + /* Category Code (CC) to zero */ + snd_hdac_codec_write(&edev->hdac, cvt_nid, 0, + AC_VERB_SET_DIGI_CONVERT_2, 0); + + snd_hdac_codec_write(&edev->hdac, pin_nid, 0, + AC_VERB_SET_CONNECT_SEL, 0); + + return hdac_hdmi_query_cvt_params(&edev->hdac, &dai_map->cvt); +} + +/* + * Parse all nodes and store the cvt/pin nids in array + * Add one time initialization for pin and cvt widgets + */ +static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev) +{ + hda_nid_t nid; + int i, num_nodes; + struct hdac_device *hdac = &edev->hdac; + struct hdac_hdmi_priv *hdmi = edev->private_data; + int cvt_nid = 0, pin_nid = 0; + + num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid); + if (!nid || num_nodes < 0) { + dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n"); + return -EINVAL; + } + + hdac->num_nodes = num_nodes; + hdac->start_nid = nid; + + for (i = 0; i < hdac->num_nodes; i++, nid++) { + unsigned int caps; + unsigned int type; + + caps = get_wcaps(hdac, nid); + type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_DIGITAL)) + continue; + + switch (type) { + + case AC_WID_AUD_OUT: + hdmi->cvt_nid[cvt_nid] = nid; + cvt_nid++; + break; + + case AC_WID_PIN: + hdmi->pin_nid[pin_nid] = nid; + pin_nid++; + break; + } + } + + hdac->end_nid = nid; + + if (!pin_nid || !cvt_nid) + return -EIO; + + /* + * Currently on board only 1 pin and 1 converter is enabled for + * simplification, more will be added eventually + * So using fixed map for dai_id:pin:cvt + */ + return hdac_hdmi_init_dai_map(edev, &hdmi->dai_map[0], hdmi->pin_nid[0], + hdmi->cvt_nid[0], 0); +} + +static int hdmi_codec_probe(struct snd_soc_codec *codec) +{ + struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(&codec->component); + + edev->scodec = codec; + + create_fill_widget_route_map(dapm, &hdmi->dai_map[0]); + + /* Imp: Store the card pointer in hda_codec */ + edev->card = dapm->card->snd_card; + + /* + * hdac_device core already sets the state to active and calls + * get_noresume. So enable runtime and set the device to suspend. + */ + pm_runtime_enable(&edev->hdac.dev); + pm_runtime_put(&edev->hdac.dev); + pm_runtime_suspend(&edev->hdac.dev); + + return 0; +} + +static int hdmi_codec_remove(struct snd_soc_codec *codec) +{ + struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); + + pm_runtime_disable(&edev->hdac.dev); + return 0; +} + +static struct snd_soc_codec_driver hdmi_hda_codec = { + .probe = hdmi_codec_probe, + .remove = hdmi_codec_remove, + .idle_bias_off = true, +}; + +static struct snd_soc_dai_ops hdmi_dai_ops = { + .startup = hdac_hdmi_pcm_open, + .shutdown = hdac_hdmi_pcm_close, + .hw_params = hdac_hdmi_set_hw_params, + .prepare = hdac_hdmi_playback_prepare, + .hw_free = hdac_hdmi_playback_cleanup, +}; + +static struct snd_soc_dai_driver hdmi_dais[] = { + { .name = "intel-hdmi-hif1", + .playback = { + .stream_name = "hif1", + .channels_min = 2, + .channels_max = 2, + .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, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + + }, + .ops = &hdmi_dai_ops, + }, +}; + +static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) +{ + struct hdac_device *codec = &edev->hdac; + struct hdac_hdmi_priv *hdmi_priv; + int ret = 0; + + hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL); + if (hdmi_priv == NULL) + return -ENOMEM; + + edev->private_data = hdmi_priv; + + dev_set_drvdata(&codec->dev, edev); + + ret = hdac_hdmi_parse_and_map_nid(edev); + if (ret < 0) + return ret; + + /* ASoC specific initialization */ + return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec, + hdmi_dais, ARRAY_SIZE(hdmi_dais)); +} + +static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) +{ + snd_soc_unregister_codec(&edev->hdac.dev); + + return 0; +} + +#ifdef CONFIG_PM +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; + int err; + + dev_dbg(dev, "Enter: %s\n", __func__); + + /* controller may not have been initialized for the first time */ + if (!bus) + return 0; + + /* Power down afg */ + if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)) + snd_hdac_codec_write(hdac, hdac->afg, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + + err = snd_hdac_display_power(bus, false); + if (err < 0) { + dev_err(bus->dev, "Cannot turn on display power on i915\n"); + return err; + } + + return 0; +} + +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; + int err; + + dev_dbg(dev, "Enter: %s\n", __func__); + + /* controller may not have been initialized for the first time */ + if (!bus) + return 0; + + err = snd_hdac_display_power(bus, true); + if (err < 0) { + dev_err(bus->dev, "Cannot turn on display power on i915\n"); + return err; + } + + /* Power up afg */ + if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)) + snd_hdac_codec_write(hdac, hdac->afg, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + + return 0; +} +#else +#define hdac_hdmi_runtime_suspend NULL +#define hdac_hdmi_runtime_resume NULL +#endif + +static const struct dev_pm_ops hdac_hdmi_pm = { + SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL) +}; + +static const struct hda_device_id hdmi_list[] = { + HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0), + {} +}; + +MODULE_DEVICE_TABLE(hdaudio, hdmi_list); + +static struct hdac_ext_driver hdmi_driver = { + . hdac = { + .driver = { + .name = "HDMI HDA Codec", + .pm = &hdac_hdmi_pm, + }, + .id_table = hdmi_list, + }, + .probe = hdac_hdmi_dev_probe, + .remove = hdac_hdmi_dev_remove, +}; + +static int __init hdmi_init(void) +{ + return snd_hda_ext_driver_register(&hdmi_driver); +} + +static void __exit hdmi_exit(void) +{ + snd_hda_ext_driver_unregister(&hdmi_driver); +} + +module_init(hdmi_init); +module_exit(hdmi_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("HDMI HD codec"); +MODULE_AUTHOR("Samreen Nilofer<samreen.nilofer@intel.com>"); +MODULE_AUTHOR("Subhransu S. Prusty<subhransu.s.prusty@intel.com>"); diff --git a/sound/soc/codecs/max98357a.c b/sound/soc/codecs/max98357a.c index f5e3dce2633a..5b1dfb1518fb 100644 --- a/sound/soc/codecs/max98357a.c +++ b/sound/soc/codecs/max98357a.c @@ -12,6 +12,7 @@ * max98357a.c -- MAX98357A ALSA SoC Codec driver */ +#include <linux/acpi.h> #include <linux/device.h> #include <linux/err.h> #include <linux/gpio.h> @@ -123,10 +124,19 @@ static const struct of_device_id max98357a_device_id[] = { MODULE_DEVICE_TABLE(of, max98357a_device_id); #endif +#ifdef CONFIG_ACPI +static const struct acpi_device_id max98357a_acpi_match[] = { + { "MX98357A", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, max98357a_acpi_match); +#endif + static struct platform_driver max98357a_platform_driver = { .driver = { .name = "max98357a", .of_match_table = of_match_ptr(max98357a_device_id), + .acpi_match_table = ACPI_PTR(max98357a_acpi_match), }, .probe = max98357a_platform_probe, .remove = max98357a_platform_remove, diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 7fc7b4e3f444..c1b87c5800b1 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -1271,6 +1271,36 @@ static int nau8825_i2c_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM_SLEEP +static int nau8825_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct nau8825 *nau8825 = dev_get_drvdata(dev); + + disable_irq(client->irq); + regcache_cache_only(nau8825->regmap, true); + regcache_mark_dirty(nau8825->regmap); + + return 0; +} + +static int nau8825_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct nau8825 *nau8825 = dev_get_drvdata(dev); + + regcache_cache_only(nau8825->regmap, false); + regcache_sync(nau8825->regmap); + enable_irq(client->irq); + + return 0; +} +#endif + +static const struct dev_pm_ops nau8825_pm = { + SET_SYSTEM_SLEEP_PM_OPS(nau8825_suspend, nau8825_resume) +}; + static const struct i2c_device_id nau8825_i2c_ids[] = { { "nau8825", 0 }, { } @@ -1297,6 +1327,7 @@ static struct i2c_driver nau8825_driver = { .name = "nau8825", .of_match_table = of_match_ptr(nau8825_of_ids), .acpi_match_table = ACPI_PTR(nau8825_acpi_match), + .pm = &nau8825_pm, }, .probe = nau8825_i2c_probe, .remove = nau8825_i2c_remove, diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c new file mode 100644 index 000000000000..6feb0901dfeb --- /dev/null +++ b/sound/soc/codecs/pcm3168a-i2c.c @@ -0,0 +1,66 @@ +/* + * PCM3168A codec i2c driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley <Damien.Horsley@imgtec.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> + +#include <sound/soc.h> + +#include "pcm3168a.h" + +static int pcm3168a_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(i2c, &pcm3168a_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return pcm3168a_probe(&i2c->dev, regmap); +} + +static int pcm3168a_i2c_remove(struct i2c_client *i2c) +{ + pcm3168a_remove(&i2c->dev); + + return 0; +} + +static const struct i2c_device_id pcm3168a_i2c_id[] = { + { "pcm3168a", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id); + +static const struct of_device_id pcm3168a_of_match[] = { + { .compatible = "ti,pcm3168a", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm3168a_of_match); + +static struct i2c_driver pcm3168a_i2c_driver = { + .probe = pcm3168a_i2c_probe, + .remove = pcm3168a_i2c_remove, + .id_table = pcm3168a_i2c_id, + .driver = { + .name = "pcm3168a", + .of_match_table = pcm3168a_of_match, + .pm = &pcm3168a_pm_ops, + }, +}; +module_i2c_driver(pcm3168a_i2c_driver); + +MODULE_DESCRIPTION("PCM3168A I2C codec driver"); +MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm3168a-spi.c b/sound/soc/codecs/pcm3168a-spi.c new file mode 100644 index 000000000000..03945a27ae40 --- /dev/null +++ b/sound/soc/codecs/pcm3168a-spi.c @@ -0,0 +1,65 @@ +/* + * PCM3168A codec spi driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley <Damien.Horsley@imgtec.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/spi/spi.h> + +#include <sound/soc.h> + +#include "pcm3168a.h" + +static int pcm3168a_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &pcm3168a_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return pcm3168a_probe(&spi->dev, regmap); +} + +static int pcm3168a_spi_remove(struct spi_device *spi) +{ + pcm3168a_remove(&spi->dev); + + return 0; +} + +static const struct spi_device_id pcm3168a_spi_id[] = { + { "pcm3168a", }, + { }, +}; +MODULE_DEVICE_TABLE(spi, pcm3168a_spi_id); + +static const struct of_device_id pcm3168a_of_match[] = { + { .compatible = "ti,pcm3168a", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm3168a_of_match); + +static struct spi_driver pcm3168a_spi_driver = { + .probe = pcm3168a_spi_probe, + .remove = pcm3168a_spi_remove, + .id_table = pcm3168a_spi_id, + .driver = { + .name = "pcm3168a", + .of_match_table = pcm3168a_of_match, + .pm = &pcm3168a_pm_ops, + }, +}; +module_spi_driver(pcm3168a_spi_driver); + +MODULE_DESCRIPTION("PCM3168A SPI codec driver"); +MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c new file mode 100644 index 000000000000..44b268aa4dd8 --- /dev/null +++ b/sound/soc/codecs/pcm3168a.c @@ -0,0 +1,767 @@ +/* + * PCM3168A codec driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley <Damien.Horsley@imgtec.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> + +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "pcm3168a.h" + +#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define PCM3168A_FMT_I2S 0x0 +#define PCM3168A_FMT_LEFT_J 0x1 +#define PCM3168A_FMT_RIGHT_J 0x2 +#define PCM3168A_FMT_RIGHT_J_16 0x3 +#define PCM3168A_FMT_DSP_A 0x4 +#define PCM3168A_FMT_DSP_B 0x5 +#define PCM3168A_FMT_DSP_MASK 0x4 + +#define PCM3168A_NUM_SUPPLIES 6 +static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = { + "VDD1", + "VDD2", + "VCCAD1", + "VCCAD2", + "VCCDA1", + "VCCDA2" +}; + +struct pcm3168a_priv { + struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES]; + struct regmap *regmap; + struct clk *scki; + bool adc_master_mode; + bool dac_master_mode; + unsigned long sysclk; + unsigned int adc_fmt; + unsigned int dac_fmt; +}; + +static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" }; + +static SOC_ENUM_SINGLE_DECL(pcm3168a_d1_roll_off, PCM3168A_DAC_OP_FLT, + PCM3168A_DAC_FLT_SHIFT, pcm3168a_roll_off); +static SOC_ENUM_SINGLE_DECL(pcm3168a_d2_roll_off, PCM3168A_DAC_OP_FLT, + PCM3168A_DAC_FLT_SHIFT + 1, pcm3168a_roll_off); +static SOC_ENUM_SINGLE_DECL(pcm3168a_d3_roll_off, PCM3168A_DAC_OP_FLT, + PCM3168A_DAC_FLT_SHIFT + 2, pcm3168a_roll_off); +static SOC_ENUM_SINGLE_DECL(pcm3168a_d4_roll_off, PCM3168A_DAC_OP_FLT, + PCM3168A_DAC_FLT_SHIFT + 3, pcm3168a_roll_off); + +static const char *const pcm3168a_volume_type[] = { + "Individual", "Master + Individual" }; + +static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_volume_type, PCM3168A_DAC_ATT_DEMP_ZF, + PCM3168A_DAC_ATMDDA_SHIFT, pcm3168a_volume_type); + +static const char *const pcm3168a_att_speed_mult[] = { "2048", "4096" }; + +static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_att_mult, PCM3168A_DAC_ATT_DEMP_ZF, + PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_att_speed_mult); + +static const char *const pcm3168a_demp[] = { + "Disabled", "48khz", "44.1khz", "32khz" }; + +static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_demp, PCM3168A_DAC_ATT_DEMP_ZF, + PCM3168A_DAC_DEMP_SHIFT, pcm3168a_demp); + +static const char *const pcm3168a_zf_func[] = { + "DAC 1/2/3/4 AND", "DAC 1/2/3/4 OR", "DAC 1/2/3 AND", + "DAC 1/2/3 OR", "DAC 4 AND", "DAC 4 OR" }; + +static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_func, PCM3168A_DAC_ATT_DEMP_ZF, + PCM3168A_DAC_AZRO_SHIFT, pcm3168a_zf_func); + +static const char *const pcm3168a_pol[] = { "Active High", "Active Low" }; + +static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_pol, PCM3168A_DAC_ATT_DEMP_ZF, + PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_pol); + +static const char *const pcm3168a_con[] = { "Differential", "Single-Ended" }; + +static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc1_con, PCM3168A_ADC_SEAD, + 0, 1, pcm3168a_con); +static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc2_con, PCM3168A_ADC_SEAD, + 2, 3, pcm3168a_con); +static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc3_con, PCM3168A_ADC_SEAD, + 4, 5, pcm3168a_con); + +static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_volume_type, PCM3168A_ADC_ATT_OVF, + PCM3168A_ADC_ATMDAD_SHIFT, pcm3168a_volume_type); + +static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_att_mult, PCM3168A_ADC_ATT_OVF, + PCM3168A_ADC_ATSPAD_SHIFT, pcm3168a_att_speed_mult); + +static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_ov_pol, PCM3168A_ADC_ATT_OVF, + PCM3168A_ADC_OVFP_SHIFT, pcm3168a_pol); + +/* -100db to 0db, register values 0-54 cause mute */ +static const DECLARE_TLV_DB_SCALE(pcm3168a_dac_tlv, -10050, 50, 1); + +/* -100db to 20db, register values 0-14 cause mute */ +static const DECLARE_TLV_DB_SCALE(pcm3168a_adc_tlv, -10050, 50, 1); + +static const struct snd_kcontrol_new pcm3168a_snd_controls[] = { + SOC_SINGLE("DAC Power-Save Switch", PCM3168A_DAC_PWR_MST_FMT, + PCM3168A_DAC_PSMDA_SHIFT, 1, 1), + SOC_ENUM("DAC1 Digital Filter roll-off", pcm3168a_d1_roll_off), + SOC_ENUM("DAC2 Digital Filter roll-off", pcm3168a_d2_roll_off), + SOC_ENUM("DAC3 Digital Filter roll-off", pcm3168a_d3_roll_off), + SOC_ENUM("DAC4 Digital Filter roll-off", pcm3168a_d4_roll_off), + SOC_DOUBLE("DAC1 Invert Switch", PCM3168A_DAC_INV, 0, 1, 1, 0), + SOC_DOUBLE("DAC2 Invert Switch", PCM3168A_DAC_INV, 2, 3, 1, 0), + SOC_DOUBLE("DAC3 Invert Switch", PCM3168A_DAC_INV, 4, 5, 1, 0), + SOC_DOUBLE("DAC4 Invert Switch", PCM3168A_DAC_INV, 6, 7, 1, 0), + SOC_DOUBLE_STS("DAC1 Zero Flag", PCM3168A_DAC_ZERO, 0, 1, 1, 0), + SOC_DOUBLE_STS("DAC2 Zero Flag", PCM3168A_DAC_ZERO, 2, 3, 1, 0), + SOC_DOUBLE_STS("DAC3 Zero Flag", PCM3168A_DAC_ZERO, 4, 5, 1, 0), + SOC_DOUBLE_STS("DAC4 Zero Flag", PCM3168A_DAC_ZERO, 6, 7, 1, 0), + SOC_ENUM("DAC Volume Control Type", pcm3168a_dac_volume_type), + SOC_ENUM("DAC Volume Rate Multiplier", pcm3168a_dac_att_mult), + SOC_ENUM("DAC De-Emphasis", pcm3168a_dac_demp), + SOC_ENUM("DAC Zero Flag Function", pcm3168a_dac_zf_func), + SOC_ENUM("DAC Zero Flag Polarity", pcm3168a_dac_zf_pol), + SOC_SINGLE_RANGE_TLV("Master Playback Volume", + PCM3168A_DAC_VOL_MASTER, 0, 54, 255, 0, + pcm3168a_dac_tlv), + SOC_DOUBLE_R_RANGE_TLV("DAC1 Playback Volume", + PCM3168A_DAC_VOL_CHAN_START, + PCM3168A_DAC_VOL_CHAN_START + 1, + 0, 54, 255, 0, pcm3168a_dac_tlv), + SOC_DOUBLE_R_RANGE_TLV("DAC2 Playback Volume", + PCM3168A_DAC_VOL_CHAN_START + 2, + PCM3168A_DAC_VOL_CHAN_START + 3, + 0, 54, 255, 0, pcm3168a_dac_tlv), + SOC_DOUBLE_R_RANGE_TLV("DAC3 Playback Volume", + PCM3168A_DAC_VOL_CHAN_START + 4, + PCM3168A_DAC_VOL_CHAN_START + 5, + 0, 54, 255, 0, pcm3168a_dac_tlv), + SOC_DOUBLE_R_RANGE_TLV("DAC4 Playback Volume", + PCM3168A_DAC_VOL_CHAN_START + 6, + PCM3168A_DAC_VOL_CHAN_START + 7, + 0, 54, 255, 0, pcm3168a_dac_tlv), + SOC_SINGLE("ADC1 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB, + PCM3168A_ADC_BYP_SHIFT, 1, 1), + SOC_SINGLE("ADC2 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB, + PCM3168A_ADC_BYP_SHIFT + 1, 1, 1), + SOC_SINGLE("ADC3 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB, + PCM3168A_ADC_BYP_SHIFT + 2, 1, 1), + SOC_ENUM("ADC1 Connection Type", pcm3168a_adc1_con), + SOC_ENUM("ADC2 Connection Type", pcm3168a_adc2_con), + SOC_ENUM("ADC3 Connection Type", pcm3168a_adc3_con), + SOC_DOUBLE("ADC1 Invert Switch", PCM3168A_ADC_INV, 0, 1, 1, 0), + SOC_DOUBLE("ADC2 Invert Switch", PCM3168A_ADC_INV, 2, 3, 1, 0), + SOC_DOUBLE("ADC3 Invert Switch", PCM3168A_ADC_INV, 4, 5, 1, 0), + SOC_DOUBLE("ADC1 Mute Switch", PCM3168A_ADC_MUTE, 0, 1, 1, 0), + SOC_DOUBLE("ADC2 Mute Switch", PCM3168A_ADC_MUTE, 2, 3, 1, 0), + SOC_DOUBLE("ADC3 Mute Switch", PCM3168A_ADC_MUTE, 4, 5, 1, 0), + SOC_DOUBLE_STS("ADC1 Overflow Flag", PCM3168A_ADC_OV, 0, 1, 1, 0), + SOC_DOUBLE_STS("ADC2 Overflow Flag", PCM3168A_ADC_OV, 2, 3, 1, 0), + SOC_DOUBLE_STS("ADC3 Overflow Flag", PCM3168A_ADC_OV, 4, 5, 1, 0), + SOC_ENUM("ADC Volume Control Type", pcm3168a_adc_volume_type), + SOC_ENUM("ADC Volume Rate Multiplier", pcm3168a_adc_att_mult), + SOC_ENUM("ADC Overflow Flag Polarity", pcm3168a_adc_ov_pol), + SOC_SINGLE_RANGE_TLV("Master Capture Volume", + PCM3168A_ADC_VOL_MASTER, 0, 14, 255, 0, + pcm3168a_adc_tlv), + SOC_DOUBLE_R_RANGE_TLV("ADC1 Capture Volume", + PCM3168A_ADC_VOL_CHAN_START, + PCM3168A_ADC_VOL_CHAN_START + 1, + 0, 14, 255, 0, pcm3168a_adc_tlv), + SOC_DOUBLE_R_RANGE_TLV("ADC2 Capture Volume", + PCM3168A_ADC_VOL_CHAN_START + 2, + PCM3168A_ADC_VOL_CHAN_START + 3, + 0, 14, 255, 0, pcm3168a_adc_tlv), + SOC_DOUBLE_R_RANGE_TLV("ADC3 Capture Volume", + PCM3168A_ADC_VOL_CHAN_START + 4, + PCM3168A_ADC_VOL_CHAN_START + 5, + 0, 14, 255, 0, pcm3168a_adc_tlv) +}; + +static const struct snd_soc_dapm_widget pcm3168a_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC1", "Playback", PCM3168A_DAC_OP_FLT, + PCM3168A_DAC_OPEDA_SHIFT, 1), + SND_SOC_DAPM_DAC("DAC2", "Playback", PCM3168A_DAC_OP_FLT, + PCM3168A_DAC_OPEDA_SHIFT + 1, 1), + SND_SOC_DAPM_DAC("DAC3", "Playback", PCM3168A_DAC_OP_FLT, + PCM3168A_DAC_OPEDA_SHIFT + 2, 1), + SND_SOC_DAPM_DAC("DAC4", "Playback", PCM3168A_DAC_OP_FLT, + PCM3168A_DAC_OPEDA_SHIFT + 3, 1), + + SND_SOC_DAPM_OUTPUT("AOUT1L"), + SND_SOC_DAPM_OUTPUT("AOUT1R"), + SND_SOC_DAPM_OUTPUT("AOUT2L"), + SND_SOC_DAPM_OUTPUT("AOUT2R"), + SND_SOC_DAPM_OUTPUT("AOUT3L"), + SND_SOC_DAPM_OUTPUT("AOUT3R"), + SND_SOC_DAPM_OUTPUT("AOUT4L"), + SND_SOC_DAPM_OUTPUT("AOUT4R"), + + SND_SOC_DAPM_ADC("ADC1", "Capture", PCM3168A_ADC_PWR_HPFB, + PCM3168A_ADC_PSVAD_SHIFT, 1), + SND_SOC_DAPM_ADC("ADC2", "Capture", PCM3168A_ADC_PWR_HPFB, + PCM3168A_ADC_PSVAD_SHIFT + 1, 1), + SND_SOC_DAPM_ADC("ADC3", "Capture", PCM3168A_ADC_PWR_HPFB, + PCM3168A_ADC_PSVAD_SHIFT + 2, 1), + + SND_SOC_DAPM_INPUT("AIN1L"), + SND_SOC_DAPM_INPUT("AIN1R"), + SND_SOC_DAPM_INPUT("AIN2L"), + SND_SOC_DAPM_INPUT("AIN2R"), + SND_SOC_DAPM_INPUT("AIN3L"), + SND_SOC_DAPM_INPUT("AIN3R") +}; + +static const struct snd_soc_dapm_route pcm3168a_dapm_routes[] = { + /* Playback */ + { "AOUT1L", NULL, "DAC1" }, + { "AOUT1R", NULL, "DAC1" }, + + { "AOUT2L", NULL, "DAC2" }, + { "AOUT2R", NULL, "DAC2" }, + + { "AOUT3L", NULL, "DAC3" }, + { "AOUT3R", NULL, "DAC3" }, + + { "AOUT4L", NULL, "DAC4" }, + { "AOUT4R", NULL, "DAC4" }, + + /* Capture */ + { "ADC1", NULL, "AIN1L" }, + { "ADC1", NULL, "AIN1R" }, + + { "ADC2", NULL, "AIN2L" }, + { "ADC2", NULL, "AIN2R" }, + + { "ADC3", NULL, "AIN3L" }, + { "ADC3", NULL, "AIN3R" } +}; + +static unsigned int pcm3168a_scki_ratios[] = { + 768, + 512, + 384, + 256, + 192, + 128 +}; + +#define PCM3168A_NUM_SCKI_RATIOS_DAC ARRAY_SIZE(pcm3168a_scki_ratios) +#define PCM3168A_NUM_SCKI_RATIOS_ADC (ARRAY_SIZE(pcm3168a_scki_ratios) - 2) + +#define PCM1368A_MAX_SYSCLK 36864000 + +static int pcm3168a_reset(struct pcm3168a_priv *pcm3168a) +{ + int ret; + + ret = regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE, 0); + if (ret) + return ret; + + /* Internal reset is de-asserted after 3846 SCKI cycles */ + msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk)); + + return regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE, + PCM3168A_MRST_MASK | PCM3168A_SRST_MASK); +} + +static int pcm3168a_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec); + + regmap_write(pcm3168a->regmap, PCM3168A_DAC_MUTE, mute ? 0xff : 0); + + return 0; +} + +static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(dai->codec); + + if (freq > PCM1368A_MAX_SYSCLK) + return -EINVAL; + + pcm3168a->sysclk = freq; + + return 0; +} + +static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, + unsigned int format, bool dac) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec); + u32 fmt, reg, mask, shift; + bool master_mode; + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + fmt = PCM3168A_FMT_LEFT_J; + break; + case SND_SOC_DAIFMT_I2S: + fmt = PCM3168A_FMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + fmt = PCM3168A_FMT_RIGHT_J; + break; + case SND_SOC_DAIFMT_DSP_A: + fmt = PCM3168A_FMT_DSP_A; + break; + case SND_SOC_DAIFMT_DSP_B: + fmt = PCM3168A_FMT_DSP_B; + break; + default: + dev_err(codec->dev, "unsupported dai format\n"); + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + master_mode = false; + break; + case SND_SOC_DAIFMT_CBM_CFM: + master_mode = true; + break; + default: + dev_err(codec->dev, "unsupported master/slave mode\n"); + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + if (dac) { + reg = PCM3168A_DAC_PWR_MST_FMT; + mask = PCM3168A_DAC_FMT_MASK; + shift = PCM3168A_DAC_FMT_SHIFT; + pcm3168a->dac_master_mode = master_mode; + pcm3168a->dac_fmt = fmt; + } else { + reg = PCM3168A_ADC_MST_FMT; + mask = PCM3168A_ADC_FMTAD_MASK; + shift = PCM3168A_ADC_FMTAD_SHIFT; + pcm3168a->adc_master_mode = master_mode; + pcm3168a->adc_fmt = fmt; + } + + regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift); + + return 0; +} + +static int pcm3168a_set_dai_fmt_dac(struct snd_soc_dai *dai, + unsigned int format) +{ + return pcm3168a_set_dai_fmt(dai, format, true); +} + +static int pcm3168a_set_dai_fmt_adc(struct snd_soc_dai *dai, + unsigned int format) +{ + return pcm3168a_set_dai_fmt(dai, format, false); +} + +static int pcm3168a_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 pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec); + bool tx, master_mode; + u32 val, mask, shift, reg; + unsigned int rate, channels, fmt, ratio, max_ratio; + int i, min_frame_size; + snd_pcm_format_t format; + + rate = params_rate(params); + format = params_format(params); + channels = params_channels(params); + + ratio = pcm3168a->sysclk / rate; + + tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + if (tx) { + max_ratio = PCM3168A_NUM_SCKI_RATIOS_DAC; + reg = PCM3168A_DAC_PWR_MST_FMT; + mask = PCM3168A_DAC_MSDA_MASK; + shift = PCM3168A_DAC_MSDA_SHIFT; + master_mode = pcm3168a->dac_master_mode; + fmt = pcm3168a->dac_fmt; + } else { + max_ratio = PCM3168A_NUM_SCKI_RATIOS_ADC; + reg = PCM3168A_ADC_MST_FMT; + mask = PCM3168A_ADC_MSAD_MASK; + shift = PCM3168A_ADC_MSAD_SHIFT; + master_mode = pcm3168a->adc_master_mode; + fmt = pcm3168a->adc_fmt; + } + + for (i = 0; i < max_ratio; i++) { + if (pcm3168a_scki_ratios[i] == ratio) + break; + } + + if (i == max_ratio) { + dev_err(codec->dev, "unsupported sysclk ratio\n"); + return -EINVAL; + } + + min_frame_size = params_width(params) * 2; + switch (min_frame_size) { + case 32: + if (master_mode || (fmt != PCM3168A_FMT_RIGHT_J)) { + dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n"); + return -EINVAL; + } + fmt = PCM3168A_FMT_RIGHT_J_16; + break; + case 48: + if (master_mode || (fmt & PCM3168A_FMT_DSP_MASK)) { + dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n"); + return -EINVAL; + } + break; + case 64: + break; + default: + dev_err(codec->dev, "unsupported frame size: %d\n", min_frame_size); + return -EINVAL; + } + + if (master_mode) + val = ((i + 1) << shift); + else + val = 0; + + regmap_update_bits(pcm3168a->regmap, reg, mask, val); + + if (tx) { + mask = PCM3168A_DAC_FMT_MASK; + shift = PCM3168A_DAC_FMT_SHIFT; + } else { + mask = PCM3168A_ADC_FMTAD_MASK; + shift = PCM3168A_ADC_FMTAD_SHIFT; + } + + regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift); + + return 0; +} + +static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = { + .set_fmt = pcm3168a_set_dai_fmt_dac, + .set_sysclk = pcm3168a_set_dai_sysclk, + .hw_params = pcm3168a_hw_params, + .digital_mute = pcm3168a_digital_mute +}; + +static const struct snd_soc_dai_ops pcm3168a_adc_dai_ops = { + .set_fmt = pcm3168a_set_dai_fmt_adc, + .set_sysclk = pcm3168a_set_dai_sysclk, + .hw_params = pcm3168a_hw_params +}; + +static struct snd_soc_dai_driver pcm3168a_dais[] = { + { + .name = "pcm3168a-dac", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = PCM3168A_FORMATS + }, + .ops = &pcm3168a_dac_dai_ops + }, + { + .name = "pcm3168a-adc", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 6, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = PCM3168A_FORMATS + }, + .ops = &pcm3168a_adc_dai_ops + }, +}; + +static const struct reg_default pcm3168a_reg_default[] = { + { PCM3168A_RST_SMODE, PCM3168A_MRST_MASK | PCM3168A_SRST_MASK }, + { PCM3168A_DAC_PWR_MST_FMT, 0x00 }, + { PCM3168A_DAC_OP_FLT, 0x00 }, + { PCM3168A_DAC_INV, 0x00 }, + { PCM3168A_DAC_MUTE, 0x00 }, + { PCM3168A_DAC_ZERO, 0x00 }, + { PCM3168A_DAC_ATT_DEMP_ZF, 0x00 }, + { PCM3168A_DAC_VOL_MASTER, 0xff }, + { PCM3168A_DAC_VOL_CHAN_START, 0xff }, + { PCM3168A_DAC_VOL_CHAN_START + 1, 0xff }, + { PCM3168A_DAC_VOL_CHAN_START + 2, 0xff }, + { PCM3168A_DAC_VOL_CHAN_START + 3, 0xff }, + { PCM3168A_DAC_VOL_CHAN_START + 4, 0xff }, + { PCM3168A_DAC_VOL_CHAN_START + 5, 0xff }, + { PCM3168A_DAC_VOL_CHAN_START + 6, 0xff }, + { PCM3168A_DAC_VOL_CHAN_START + 7, 0xff }, + { PCM3168A_ADC_SMODE, 0x00 }, + { PCM3168A_ADC_MST_FMT, 0x00 }, + { PCM3168A_ADC_PWR_HPFB, 0x00 }, + { PCM3168A_ADC_SEAD, 0x00 }, + { PCM3168A_ADC_INV, 0x00 }, + { PCM3168A_ADC_MUTE, 0x00 }, + { PCM3168A_ADC_OV, 0x00 }, + { PCM3168A_ADC_ATT_OVF, 0x00 }, + { PCM3168A_ADC_VOL_MASTER, 0xd3 }, + { PCM3168A_ADC_VOL_CHAN_START, 0xd3 }, + { PCM3168A_ADC_VOL_CHAN_START + 1, 0xd3 }, + { PCM3168A_ADC_VOL_CHAN_START + 2, 0xd3 }, + { PCM3168A_ADC_VOL_CHAN_START + 3, 0xd3 }, + { PCM3168A_ADC_VOL_CHAN_START + 4, 0xd3 }, + { PCM3168A_ADC_VOL_CHAN_START + 5, 0xd3 } +}; + +static bool pcm3168a_readable_register(struct device *dev, unsigned int reg) +{ + if (reg >= PCM3168A_RST_SMODE) + return true; + else + return false; +} + +static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PCM3168A_DAC_ZERO: + case PCM3168A_ADC_OV: + return true; + default: + return false; + } +} + +static bool pcm3168a_writeable_register(struct device *dev, unsigned int reg) +{ + if (reg < PCM3168A_RST_SMODE) + return false; + + switch (reg) { + case PCM3168A_DAC_ZERO: + case PCM3168A_ADC_OV: + return false; + default: + return true; + } +} + +const struct regmap_config pcm3168a_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = PCM3168A_ADC_VOL_CHAN_START + 5, + .reg_defaults = pcm3168a_reg_default, + .num_reg_defaults = ARRAY_SIZE(pcm3168a_reg_default), + .readable_reg = pcm3168a_readable_register, + .volatile_reg = pcm3168a_volatile_register, + .writeable_reg = pcm3168a_writeable_register, + .cache_type = REGCACHE_FLAT +}; +EXPORT_SYMBOL_GPL(pcm3168a_regmap); + +static const struct snd_soc_codec_driver pcm3168a_driver = { + .idle_bias_off = true, + .controls = pcm3168a_snd_controls, + .num_controls = ARRAY_SIZE(pcm3168a_snd_controls), + .dapm_widgets = pcm3168a_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm3168a_dapm_widgets), + .dapm_routes = pcm3168a_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm3168a_dapm_routes) +}; + +int pcm3168a_probe(struct device *dev, struct regmap *regmap) +{ + struct pcm3168a_priv *pcm3168a; + int ret, i; + + pcm3168a = devm_kzalloc(dev, sizeof(*pcm3168a), GFP_KERNEL); + if (pcm3168a == NULL) + return -ENOMEM; + + dev_set_drvdata(dev, pcm3168a); + + pcm3168a->scki = devm_clk_get(dev, "scki"); + if (IS_ERR(pcm3168a->scki)) { + ret = PTR_ERR(pcm3168a->scki); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to acquire clock 'scki': %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(pcm3168a->scki); + if (ret) { + dev_err(dev, "Failed to enable mclk: %d\n", ret); + return ret; + } + + pcm3168a->sysclk = clk_get_rate(pcm3168a->scki); + + for (i = 0; i < ARRAY_SIZE(pcm3168a->supplies); i++) + pcm3168a->supplies[i].supply = pcm3168a_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, + ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to request supplies: %d\n", ret); + goto err_clk; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies), + pcm3168a->supplies); + if (ret) { + dev_err(dev, "failed to enable supplies: %d\n", ret); + goto err_clk; + } + + pcm3168a->regmap = regmap; + if (IS_ERR(pcm3168a->regmap)) { + ret = PTR_ERR(pcm3168a->regmap); + dev_err(dev, "failed to allocate regmap: %d\n", ret); + goto err_regulator; + } + + ret = pcm3168a_reset(pcm3168a); + if (ret) { + dev_err(dev, "Failed to reset device: %d\n", ret); + goto err_regulator; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + ret = snd_soc_register_codec(dev, &pcm3168a_driver, pcm3168a_dais, + ARRAY_SIZE(pcm3168a_dais)); + if (ret) { + dev_err(dev, "failed to register codec: %d\n", ret); + goto err_regulator; + } + + return 0; + +err_regulator: + regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), + pcm3168a->supplies); +err_clk: + clk_disable_unprepare(pcm3168a->scki); + + return ret; +} +EXPORT_SYMBOL_GPL(pcm3168a_probe); + +void pcm3168a_remove(struct device *dev) +{ + struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev); + + snd_soc_unregister_codec(dev); + pm_runtime_disable(dev); + regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), + pcm3168a->supplies); + clk_disable_unprepare(pcm3168a->scki); +} +EXPORT_SYMBOL_GPL(pcm3168a_remove); + +#ifdef CONFIG_PM +static int pcm3168a_rt_resume(struct device *dev) +{ + struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(pcm3168a->scki); + if (ret) { + dev_err(dev, "Failed to enable mclk: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies), + pcm3168a->supplies); + if (ret) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + goto err_clk; + } + + ret = pcm3168a_reset(pcm3168a); + if (ret) { + dev_err(dev, "Failed to reset device: %d\n", ret); + goto err_regulator; + } + + regcache_cache_only(pcm3168a->regmap, false); + + regcache_mark_dirty(pcm3168a->regmap); + + ret = regcache_sync(pcm3168a->regmap); + if (ret) { + dev_err(dev, "Failed to sync regmap: %d\n", ret); + goto err_regulator; + } + + return 0; + +err_regulator: + regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), + pcm3168a->supplies); +err_clk: + clk_disable_unprepare(pcm3168a->scki); + + return ret; +} + +static int pcm3168a_rt_suspend(struct device *dev) +{ + struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev); + + regcache_cache_only(pcm3168a->regmap, true); + + regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), + pcm3168a->supplies); + + clk_disable_unprepare(pcm3168a->scki); + + return 0; +} +#endif + +const struct dev_pm_ops pcm3168a_pm_ops = { + SET_RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL) +}; +EXPORT_SYMBOL_GPL(pcm3168a_pm_ops); + +MODULE_DESCRIPTION("PCM3168A codec driver"); +MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm3168a.h b/sound/soc/codecs/pcm3168a.h new file mode 100644 index 000000000000..56c8332d82fb --- /dev/null +++ b/sound/soc/codecs/pcm3168a.h @@ -0,0 +1,100 @@ +/* + * PCM3168A codec driver header + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley <Damien.Horsley@imgtec.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#ifndef __PCM3168A_H__ +#define __PCM3168A_H__ + +extern const struct dev_pm_ops pcm3168a_pm_ops; +extern const struct regmap_config pcm3168a_regmap; + +extern int pcm3168a_probe(struct device *dev, struct regmap *regmap); +extern void pcm3168a_remove(struct device *dev); + +#define PCM3168A_RST_SMODE 0x40 +#define PCM3168A_MRST_MASK 0x80 +#define PCM3168A_SRST_MASK 0x40 +#define PCM3168A_DAC_SRDA_SHIFT 0 +#define PCM3168A_DAC_SRDA_MASK 0x3 + +#define PCM3168A_DAC_PWR_MST_FMT 0x41 +#define PCM3168A_DAC_PSMDA_SHIFT 7 +#define PCM3168A_DAC_PSMDA_MASK 0x80 +#define PCM3168A_DAC_MSDA_SHIFT 4 +#define PCM3168A_DAC_MSDA_MASK 0x70 +#define PCM3168A_DAC_FMT_SHIFT 0 +#define PCM3168A_DAC_FMT_MASK 0xf + +#define PCM3168A_DAC_OP_FLT 0x42 +#define PCM3168A_DAC_OPEDA_SHIFT 4 +#define PCM3168A_DAC_OPEDA_MASK 0xf0 +#define PCM3168A_DAC_FLT_SHIFT 0 +#define PCM3168A_DAC_FLT_MASK 0xf + +#define PCM3168A_DAC_INV 0x43 + +#define PCM3168A_DAC_MUTE 0x44 + +#define PCM3168A_DAC_ZERO 0x45 + +#define PCM3168A_DAC_ATT_DEMP_ZF 0x46 +#define PCM3168A_DAC_ATMDDA_MASK 0x80 +#define PCM3168A_DAC_ATMDDA_SHIFT 7 +#define PCM3168A_DAC_ATSPDA_MASK 0x40 +#define PCM3168A_DAC_ATSPDA_SHIFT 6 +#define PCM3168A_DAC_DEMP_SHIFT 4 +#define PCM3168A_DAC_DEMP_MASK 0x30 +#define PCM3168A_DAC_AZRO_SHIFT 1 +#define PCM3168A_DAC_AZRO_MASK 0xe +#define PCM3168A_DAC_ZREV_MASK 0x1 +#define PCM3168A_DAC_ZREV_SHIFT 0 + +#define PCM3168A_DAC_VOL_MASTER 0x47 + +#define PCM3168A_DAC_VOL_CHAN_START 0x48 + +#define PCM3168A_ADC_SMODE 0x50 +#define PCM3168A_ADC_SRAD_SHIFT 0 +#define PCM3168A_ADC_SRAD_MASK 0x3 + +#define PCM3168A_ADC_MST_FMT 0x51 +#define PCM3168A_ADC_MSAD_SHIFT 4 +#define PCM3168A_ADC_MSAD_MASK 0x70 +#define PCM3168A_ADC_FMTAD_SHIFT 0 +#define PCM3168A_ADC_FMTAD_MASK 0x7 + +#define PCM3168A_ADC_PWR_HPFB 0x52 +#define PCM3168A_ADC_PSVAD_SHIFT 4 +#define PCM3168A_ADC_PSVAD_MASK 0x70 +#define PCM3168A_ADC_BYP_SHIFT 0 +#define PCM3168A_ADC_BYP_MASK 0x7 + +#define PCM3168A_ADC_SEAD 0x53 + +#define PCM3168A_ADC_INV 0x54 + +#define PCM3168A_ADC_MUTE 0x55 + +#define PCM3168A_ADC_OV 0x56 + +#define PCM3168A_ADC_ATT_OVF 0x57 +#define PCM3168A_ADC_ATMDAD_MASK 0x80 +#define PCM3168A_ADC_ATMDAD_SHIFT 7 +#define PCM3168A_ADC_ATSPAD_MASK 0x40 +#define PCM3168A_ADC_ATSPAD_SHIFT 6 +#define PCM3168A_ADC_OVFP_MASK 0x1 +#define PCM3168A_ADC_OVFP_SHIFT 0 + +#define PCM3168A_ADC_VOL_MASTER 0x58 + +#define PCM3168A_ADC_VOL_CHAN_START 0x59 + +#endif diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c index aca479fa7670..1dc68ab08a17 100644 --- a/sound/soc/codecs/rl6231.c +++ b/sound/soc/codecs/rl6231.c @@ -80,8 +80,10 @@ int rl6231_calc_dmic_clk(int rate) } for (i = 0; i < ARRAY_SIZE(div); i++) { - /* find divider that gives DMIC frequency below 3MHz */ - if (3000000 * div[i] >= rate) + if ((div[i] % 3) == 0) + continue; + /* find divider that gives DMIC frequency below 3.072MHz */ + if (3072000 * div[i] >= rate) return i; } diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 28132375e427..3e8d66661b7e 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -226,6 +226,163 @@ static const struct reg_default rt5645_reg[] = { { 0xff, 0x6308 }, }; +static const struct reg_default rt5650_reg[] = { + { 0x00, 0x0000 }, + { 0x01, 0xc8c8 }, + { 0x02, 0xc8c8 }, + { 0x03, 0xc8c8 }, + { 0x0a, 0x0002 }, + { 0x0b, 0x2827 }, + { 0x0c, 0xe000 }, + { 0x0d, 0x0000 }, + { 0x0e, 0x0000 }, + { 0x0f, 0x0808 }, + { 0x14, 0x3333 }, + { 0x16, 0x4b00 }, + { 0x18, 0x018b }, + { 0x19, 0xafaf }, + { 0x1a, 0xafaf }, + { 0x1b, 0x0001 }, + { 0x1c, 0x2f2f }, + { 0x1d, 0x2f2f }, + { 0x1e, 0x0000 }, + { 0x20, 0x0000 }, + { 0x27, 0x7060 }, + { 0x28, 0x7070 }, + { 0x29, 0x8080 }, + { 0x2a, 0x5656 }, + { 0x2b, 0x5454 }, + { 0x2c, 0xaaa0 }, + { 0x2d, 0x0000 }, + { 0x2f, 0x1002 }, + { 0x31, 0x5000 }, + { 0x32, 0x0000 }, + { 0x33, 0x0000 }, + { 0x34, 0x0000 }, + { 0x35, 0x0000 }, + { 0x3b, 0x0000 }, + { 0x3c, 0x007f }, + { 0x3d, 0x0000 }, + { 0x3e, 0x007f }, + { 0x3f, 0x0000 }, + { 0x40, 0x001f }, + { 0x41, 0x0000 }, + { 0x42, 0x001f }, + { 0x45, 0x6000 }, + { 0x46, 0x003e }, + { 0x47, 0x003e }, + { 0x48, 0xf807 }, + { 0x4a, 0x0004 }, + { 0x4d, 0x0000 }, + { 0x4e, 0x0000 }, + { 0x4f, 0x01ff }, + { 0x50, 0x0000 }, + { 0x51, 0x0000 }, + { 0x52, 0x01ff }, + { 0x53, 0xf000 }, + { 0x56, 0x0111 }, + { 0x57, 0x0064 }, + { 0x58, 0xef0e }, + { 0x59, 0xf0f0 }, + { 0x5a, 0xef0e }, + { 0x5b, 0xf0f0 }, + { 0x5c, 0xef0e }, + { 0x5d, 0xf0f0 }, + { 0x5e, 0xf000 }, + { 0x5f, 0x0000 }, + { 0x61, 0x0300 }, + { 0x62, 0x0000 }, + { 0x63, 0x00c2 }, + { 0x64, 0x0000 }, + { 0x65, 0x0000 }, + { 0x66, 0x0000 }, + { 0x6a, 0x0000 }, + { 0x6c, 0x0aaa }, + { 0x70, 0x8000 }, + { 0x71, 0x8000 }, + { 0x72, 0x8000 }, + { 0x73, 0x7770 }, + { 0x74, 0x3e00 }, + { 0x75, 0x2409 }, + { 0x76, 0x000a }, + { 0x77, 0x0c00 }, + { 0x78, 0x0000 }, + { 0x79, 0x0123 }, + { 0x7a, 0x0123 }, + { 0x80, 0x0000 }, + { 0x81, 0x0000 }, + { 0x82, 0x0000 }, + { 0x83, 0x0000 }, + { 0x84, 0x0000 }, + { 0x85, 0x0000 }, + { 0x8a, 0x0000 }, + { 0x8e, 0x0004 }, + { 0x8f, 0x1100 }, + { 0x90, 0x0646 }, + { 0x91, 0x0c06 }, + { 0x93, 0x0000 }, + { 0x94, 0x0200 }, + { 0x95, 0x0000 }, + { 0x9a, 0x2184 }, + { 0x9b, 0x010a }, + { 0x9c, 0x0aea }, + { 0x9d, 0x000c }, + { 0x9e, 0x0400 }, + { 0xa0, 0xa0a8 }, + { 0xa1, 0x0059 }, + { 0xa2, 0x0001 }, + { 0xae, 0x6000 }, + { 0xaf, 0x0000 }, + { 0xb0, 0x6000 }, + { 0xb1, 0x0000 }, + { 0xb2, 0x0000 }, + { 0xb3, 0x001f }, + { 0xb4, 0x020c }, + { 0xb5, 0x1f00 }, + { 0xb6, 0x0000 }, + { 0xbb, 0x0000 }, + { 0xbc, 0x0000 }, + { 0xbd, 0x0000 }, + { 0xbe, 0x0000 }, + { 0xbf, 0x3100 }, + { 0xc0, 0x0000 }, + { 0xc1, 0x0000 }, + { 0xc2, 0x0000 }, + { 0xc3, 0x2000 }, + { 0xcd, 0x0000 }, + { 0xce, 0x0000 }, + { 0xcf, 0x1813 }, + { 0xd0, 0x0690 }, + { 0xd1, 0x1c17 }, + { 0xd3, 0xb320 }, + { 0xd4, 0x0000 }, + { 0xd6, 0x0400 }, + { 0xd9, 0x0809 }, + { 0xda, 0x0000 }, + { 0xdb, 0x0003 }, + { 0xdc, 0x0049 }, + { 0xdd, 0x001b }, + { 0xdf, 0x0008 }, + { 0xe0, 0x4000 }, + { 0xe6, 0x8000 }, + { 0xe7, 0x0200 }, + { 0xec, 0xb300 }, + { 0xed, 0x0000 }, + { 0xf0, 0x001f }, + { 0xf1, 0x020c }, + { 0xf2, 0x1f00 }, + { 0xf3, 0x0000 }, + { 0xf4, 0x4000 }, + { 0xf8, 0x0000 }, + { 0xf9, 0x0000 }, + { 0xfa, 0x2060 }, + { 0xfb, 0x4040 }, + { 0xfc, 0x0000 }, + { 0xfd, 0x0002 }, + { 0xfe, 0x10ec }, + { 0xff, 0x6308 }, +}; + struct rt5645_eq_param_s { unsigned short reg; unsigned short val; @@ -245,7 +402,7 @@ struct rt5645_priv { struct snd_soc_jack *hp_jack; struct snd_soc_jack *mic_jack; struct snd_soc_jack *btn_jack; - struct delayed_work jack_detect_work; + struct delayed_work jack_detect_work, rcclock_work; struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)]; struct rt5645_eq_param_s *eq_param; @@ -565,12 +722,31 @@ static int rt5645_hweq_put(struct snd_kcontrol *kcontrol, .put = rt5645_hweq_put \ } +static int rt5645_spk_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + int ret; + + regmap_update_bits(rt5645->regmap, RT5645_MICBIAS, + RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PU); + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + + mod_delayed_work(system_power_efficient_wq, &rt5645->rcclock_work, + msecs_to_jiffies(200)); + + return ret; +} + static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* Speaker Output Volume */ SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL, RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1), - SOC_DOUBLE_TLV("Speaker Playback Volume", RT5645_SPK_VOL, - RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv), + SOC_DOUBLE_EXT_TLV("Speaker Playback Volume", RT5645_SPK_VOL, + RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, snd_soc_get_volsw, + rt5645_spk_put_volsw, out_vol_tlv), /* ClassD modulator Speaker Gain Ratio */ SOC_SINGLE_TLV("Speaker ClassD Playback Volume", RT5645_SPO_CLSD_RATIO, @@ -1498,7 +1674,7 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on) regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00); snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140); - msleep(40); + msleep(70); rt5645->hp_on = true; } else { /* depop parameters */ @@ -3122,6 +3298,15 @@ static void rt5645_jack_detect_work(struct work_struct *work) SND_JACK_BTN_2 | SND_JACK_BTN_3); } +static void rt5645_rcclock_work(struct work_struct *work) +{ + struct rt5645_priv *rt5645 = + container_of(work, struct rt5645_priv, rcclock_work.work); + + regmap_update_bits(rt5645->regmap, RT5645_MICBIAS, + RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PD); +} + static irqreturn_t rt5645_irq(int irq, void *data) { struct rt5645_priv *rt5645 = data; @@ -3288,6 +3473,31 @@ static const struct regmap_config rt5645_regmap = { .num_ranges = ARRAY_SIZE(rt5645_ranges), }; +static const struct regmap_config rt5650_regmap = { + .reg_bits = 8, + .val_bits = 16, + .use_single_rw = true, + .max_register = RT5645_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5645_ranges) * + RT5645_PR_SPACING), + .volatile_reg = rt5645_volatile_register, + .readable_reg = rt5645_readable_register, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5650_reg, + .num_reg_defaults = ARRAY_SIZE(rt5650_reg), + .ranges = rt5645_ranges, + .num_ranges = ARRAY_SIZE(rt5645_ranges), +}; + +static const struct regmap_config temp_regmap = { + .name="nocache", + .reg_bits = 8, + .val_bits = 16, + .use_single_rw = true, + .max_register = RT5645_VENDOR_ID2 + 1, + .cache_type = REGCACHE_NONE, +}; + static const struct i2c_device_id rt5645_i2c_id[] = { { "rt5645", 0 }, { "rt5650", 0 }, @@ -3304,48 +3514,23 @@ static struct acpi_device_id rt5645_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match); #endif -static struct rt5645_platform_data *rt5645_pdata; - -static struct rt5645_platform_data strago_platform_data = { +static struct rt5645_platform_data general_platform_data = { .dmic1_data_pin = RT5645_DMIC1_DISABLE, .dmic2_data_pin = RT5645_DMIC_DATA_IN2P, .jd_mode = 3, }; -static int strago_quirk_cb(const struct dmi_system_id *id) -{ - rt5645_pdata = &strago_platform_data; - - return 1; -} - static const struct dmi_system_id dmi_platform_intel_braswell[] = { { .ident = "Intel Strago", - .callback = strago_quirk_cb, .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "Strago"), }, }, { - .ident = "Google Celes", - .callback = strago_quirk_cb, - .matches = { - DMI_MATCH(DMI_PRODUCT_NAME, "Celes"), - }, - }, - { - .ident = "Google Ultima", - .callback = strago_quirk_cb, - .matches = { - DMI_MATCH(DMI_PRODUCT_NAME, "Ultima"), - }, - }, - { - .ident = "Google Reks", - .callback = strago_quirk_cb, + .ident = "Google Chrome", .matches = { - DMI_MATCH(DMI_PRODUCT_NAME, "Reks"), + DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), }, }, { } @@ -3358,17 +3543,9 @@ static struct rt5645_platform_data buddy_platform_data = { .jd_invert = true, }; -static int buddy_quirk_cb(const struct dmi_system_id *id) -{ - rt5645_pdata = &buddy_platform_data; - - return 1; -} - static struct dmi_system_id dmi_platform_intel_broadwell[] = { { .ident = "Chrome Buddy", - .callback = buddy_quirk_cb, .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"), }, @@ -3376,6 +3553,16 @@ static struct dmi_system_id dmi_platform_intel_broadwell[] = { { } }; +static bool rt5645_check_dp(struct device *dev) +{ + if (device_property_present(dev, "realtek,in2-differential") || + device_property_present(dev, "realtek,dmic1-data-pin") || + device_property_present(dev, "realtek,dmic2-data-pin") || + device_property_present(dev, "realtek,jd-mode")) + return true; + + return false; +} static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev) { @@ -3398,6 +3585,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, struct rt5645_priv *rt5645; int ret, i; unsigned int val; + struct regmap *regmap; rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv), GFP_KERNEL); @@ -3409,11 +3597,12 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, if (pdata) rt5645->pdata = *pdata; - else if (dmi_check_system(dmi_platform_intel_braswell) || - dmi_check_system(dmi_platform_intel_broadwell)) - rt5645->pdata = *rt5645_pdata; - else + else if (dmi_check_system(dmi_platform_intel_broadwell)) + rt5645->pdata = buddy_platform_data; + else if (rt5645_check_dp(&i2c->dev)) rt5645_parse_dt(rt5645, &i2c->dev); + else if (dmi_check_system(dmi_platform_intel_braswell)) + rt5645->pdata = general_platform_data; rt5645->gpiod_hp_det = devm_gpiod_get_optional(&i2c->dev, "hp-detect", GPIOD_IN); @@ -3423,14 +3612,6 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, return PTR_ERR(rt5645->gpiod_hp_det); } - rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap); - if (IS_ERR(rt5645->regmap)) { - ret = PTR_ERR(rt5645->regmap); - dev_err(&i2c->dev, "Failed to allocate register map: %d\n", - ret); - return ret; - } - for (i = 0; i < ARRAY_SIZE(rt5645->supplies); i++) rt5645->supplies[i].supply = rt5645_supply_names[i]; @@ -3449,13 +3630,22 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, return ret; } - regmap_read(rt5645->regmap, RT5645_VENDOR_ID2, &val); + regmap = devm_regmap_init_i2c(i2c, &temp_regmap); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n", + ret); + return ret; + } + regmap_read(regmap, RT5645_VENDOR_ID2, &val); switch (val) { case RT5645_DEVICE_ID: + rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap); rt5645->codec_type = CODEC_TYPE_RT5645; break; case RT5650_DEVICE_ID: + rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5650_regmap); rt5645->codec_type = CODEC_TYPE_RT5650; break; default: @@ -3466,6 +3656,13 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, goto err_enable; } + if (IS_ERR(rt5645->regmap)) { + ret = PTR_ERR(rt5645->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + regmap_write(rt5645->regmap, RT5645_RESET, 0); ret = regmap_register_patch(rt5645->regmap, init_list, @@ -3587,6 +3784,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, } INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work); + INIT_DELAYED_WORK(&rt5645->rcclock_work, rt5645_rcclock_work); if (rt5645->i2c->irq) { ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq, @@ -3621,6 +3819,7 @@ static int rt5645_i2c_remove(struct i2c_client *i2c) free_irq(i2c->irq, rt5645); cancel_delayed_work_sync(&rt5645->jack_detect_work); + cancel_delayed_work_sync(&rt5645->rcclock_work); snd_soc_unregister_codec(&i2c->dev); regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies); diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h index dc2b46236c5c..3f1b0f1df809 100644 --- a/sound/soc/codecs/rt5670.h +++ b/sound/soc/codecs/rt5670.h @@ -973,12 +973,12 @@ #define RT5670_SCLK_SRC_MCLK (0x0 << 14) #define RT5670_SCLK_SRC_PLL1 (0x1 << 14) #define RT5670_SCLK_SRC_RCCLK (0x2 << 14) /* 15MHz */ -#define RT5670_PLL1_SRC_MASK (0x3 << 12) -#define RT5670_PLL1_SRC_SFT 12 -#define RT5670_PLL1_SRC_MCLK (0x0 << 12) -#define RT5670_PLL1_SRC_BCLK1 (0x1 << 12) -#define RT5670_PLL1_SRC_BCLK2 (0x2 << 12) -#define RT5670_PLL1_SRC_BCLK3 (0x3 << 12) +#define RT5670_PLL1_SRC_MASK (0x7 << 11) +#define RT5670_PLL1_SRC_SFT 11 +#define RT5670_PLL1_SRC_MCLK (0x0 << 11) +#define RT5670_PLL1_SRC_BCLK1 (0x1 << 11) +#define RT5670_PLL1_SRC_BCLK2 (0x2 << 11) +#define RT5670_PLL1_SRC_BCLK3 (0x3 << 11) #define RT5670_PLL1_PD_MASK (0x1 << 3) #define RT5670_PLL1_PD_SFT 3 #define RT5670_PLL1_PD_1 (0x0 << 3) diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index b4cd7e3bf5f8..69d987a9935c 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -1386,90 +1386,90 @@ static const struct snd_kcontrol_new rt5677_dac_r_mix[] = { }; static const struct snd_kcontrol_new rt5677_sto1_dac_l_mix[] = { - SOC_DAPM_SINGLE("ST L Switch", RT5677_STO1_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("ST L Switch", RT5677_STO1_DAC_MIXER, RT5677_M_ST_DAC1_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER, RT5677_M_DAC1_L_STO_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_STO1_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_STO1_DAC_MIXER, RT5677_M_DAC2_L_STO_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER, RT5677_M_DAC1_R_STO_L_SFT, 1, 1), }; static const struct snd_kcontrol_new rt5677_sto1_dac_r_mix[] = { - SOC_DAPM_SINGLE("ST R Switch", RT5677_STO1_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("ST R Switch", RT5677_STO1_DAC_MIXER, RT5677_M_ST_DAC1_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER, RT5677_M_DAC1_R_STO_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_STO1_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_STO1_DAC_MIXER, RT5677_M_DAC2_R_STO_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER, RT5677_M_DAC1_L_STO_R_SFT, 1, 1), }; static const struct snd_kcontrol_new rt5677_mono_dac_l_mix[] = { - SOC_DAPM_SINGLE("ST L Switch", RT5677_MONO_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("ST L Switch", RT5677_MONO_DAC_MIXER, RT5677_M_ST_DAC2_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_MONO_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_MONO_DAC_MIXER, RT5677_M_DAC1_L_MONO_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER, RT5677_M_DAC2_L_MONO_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER, RT5677_M_DAC2_R_MONO_L_SFT, 1, 1), }; static const struct snd_kcontrol_new rt5677_mono_dac_r_mix[] = { - SOC_DAPM_SINGLE("ST R Switch", RT5677_MONO_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("ST R Switch", RT5677_MONO_DAC_MIXER, RT5677_M_ST_DAC2_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_MONO_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_MONO_DAC_MIXER, RT5677_M_DAC1_R_MONO_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER, RT5677_M_DAC2_R_MONO_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER, RT5677_M_DAC2_L_MONO_R_SFT, 1, 1), }; static const struct snd_kcontrol_new rt5677_dd1_l_mix[] = { - SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5677_DD1_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix L Switch", RT5677_DD1_MIXER, RT5677_M_STO_L_DD1_L_SFT, 1, 1), - SOC_DAPM_SINGLE("Mono DAC Mix L Switch", RT5677_DD1_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix L Switch", RT5677_DD1_MIXER, RT5677_M_MONO_L_DD1_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC3 L Switch", RT5677_DD1_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC3 L Switch", RT5677_DD1_MIXER, RT5677_M_DAC3_L_DD1_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC3 R Switch", RT5677_DD1_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC3 R Switch", RT5677_DD1_MIXER, RT5677_M_DAC3_R_DD1_L_SFT, 1, 1), }; static const struct snd_kcontrol_new rt5677_dd1_r_mix[] = { - SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5677_DD1_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix R Switch", RT5677_DD1_MIXER, RT5677_M_STO_R_DD1_R_SFT, 1, 1), - SOC_DAPM_SINGLE("Mono DAC Mix R Switch", RT5677_DD1_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix R Switch", RT5677_DD1_MIXER, RT5677_M_MONO_R_DD1_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC3 R Switch", RT5677_DD1_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC3 R Switch", RT5677_DD1_MIXER, RT5677_M_DAC3_R_DD1_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC3 L Switch", RT5677_DD1_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC3 L Switch", RT5677_DD1_MIXER, RT5677_M_DAC3_L_DD1_R_SFT, 1, 1), }; static const struct snd_kcontrol_new rt5677_dd2_l_mix[] = { - SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5677_DD2_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix L Switch", RT5677_DD2_MIXER, RT5677_M_STO_L_DD2_L_SFT, 1, 1), - SOC_DAPM_SINGLE("Mono DAC Mix L Switch", RT5677_DD2_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix L Switch", RT5677_DD2_MIXER, RT5677_M_MONO_L_DD2_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC4 L Switch", RT5677_DD2_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC4 L Switch", RT5677_DD2_MIXER, RT5677_M_DAC4_L_DD2_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC4 R Switch", RT5677_DD2_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC4 R Switch", RT5677_DD2_MIXER, RT5677_M_DAC4_R_DD2_L_SFT, 1, 1), }; static const struct snd_kcontrol_new rt5677_dd2_r_mix[] = { - SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5677_DD2_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix R Switch", RT5677_DD2_MIXER, RT5677_M_STO_R_DD2_R_SFT, 1, 1), - SOC_DAPM_SINGLE("Mono DAC Mix R Switch", RT5677_DD2_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix R Switch", RT5677_DD2_MIXER, RT5677_M_MONO_R_DD2_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC4 R Switch", RT5677_DD2_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC4 R Switch", RT5677_DD2_MIXER, RT5677_M_DAC4_R_DD2_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC4 L Switch", RT5677_DD2_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC4 L Switch", RT5677_DD2_MIXER, RT5677_M_DAC4_L_DD2_R_SFT, 1, 1), }; @@ -2596,6 +2596,21 @@ static int rt5677_vref_event(struct snd_soc_dapm_widget *w, return 0; } +static int rt5677_filter_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msleep(50); + break; + + default: + return 0; + } + + return 0; +} + static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("PLL1", RT5677_PWR_ANLG2, RT5677_PWR_PLL1_BIT, 0, rt5677_set_pll1_event, SND_SOC_DAPM_PRE_PMU | @@ -3072,19 +3087,26 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { /* DAC Mixer */ SND_SOC_DAPM_SUPPLY("dac stereo1 filter", RT5677_PWR_DIG2, - RT5677_PWR_DAC_S1F_BIT, 0, NULL, 0), + RT5677_PWR_DAC_S1F_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("dac mono2 left filter", RT5677_PWR_DIG2, - RT5677_PWR_DAC_M2F_L_BIT, 0, NULL, 0), + RT5677_PWR_DAC_M2F_L_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("dac mono2 right filter", RT5677_PWR_DIG2, - RT5677_PWR_DAC_M2F_R_BIT, 0, NULL, 0), + RT5677_PWR_DAC_M2F_R_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("dac mono3 left filter", RT5677_PWR_DIG2, - RT5677_PWR_DAC_M3F_L_BIT, 0, NULL, 0), + RT5677_PWR_DAC_M3F_L_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("dac mono3 right filter", RT5677_PWR_DIG2, - RT5677_PWR_DAC_M3F_R_BIT, 0, NULL, 0), + RT5677_PWR_DAC_M3F_R_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("dac mono4 left filter", RT5677_PWR_DIG2, - RT5677_PWR_DAC_M4F_L_BIT, 0, NULL, 0), + RT5677_PWR_DAC_M4F_L_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("dac mono4 right filter", RT5677_PWR_DIG2, - RT5677_PWR_DAC_M4F_R_BIT, 0, NULL, 0), + RT5677_PWR_DAC_M4F_R_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, rt5677_sto1_dac_l_mix, ARRAY_SIZE(rt5677_sto1_dac_l_mix)), diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index f540f82b1f27..08b40460663c 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -189,6 +189,7 @@ static int power_vag_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP); + msleep(400); break; case SND_SOC_DAPM_PRE_PMD: diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index c04c0bc6f58a..c36409601835 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -575,6 +575,33 @@ static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \ SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0) +#define WM5110_RXANC_INPUT_ROUTES(widget, name) \ + { widget, NULL, name " NG Mux" }, \ + { name " NG Internal", NULL, "RXANC NG Clock" }, \ + { name " NG Internal", NULL, name " Channel" }, \ + { name " NG External", NULL, "RXANC NG External Clock" }, \ + { name " NG External", NULL, name " Channel" }, \ + { name " NG Mux", "None", name " Channel" }, \ + { name " NG Mux", "Internal", name " NG Internal" }, \ + { name " NG Mux", "External", name " NG External" }, \ + { name " Channel", "Left", name " Left Input" }, \ + { name " Channel", "Combine", name " Left Input" }, \ + { name " Channel", "Right", name " Right Input" }, \ + { name " Channel", "Combine", name " Right Input" }, \ + { name " Left Input", "IN1", "IN1L PGA" }, \ + { name " Right Input", "IN1", "IN1R PGA" }, \ + { name " Left Input", "IN2", "IN2L PGA" }, \ + { name " Right Input", "IN2", "IN2R PGA" }, \ + { name " Left Input", "IN3", "IN3L PGA" }, \ + { name " Right Input", "IN3", "IN3R PGA" }, \ + { name " Left Input", "IN4", "IN4L PGA" }, \ + { name " Right Input", "IN4", "IN4R PGA" } + +#define WM5110_RXANC_OUTPUT_ROUTES(widget, name) \ + { widget, NULL, name " ANC Source" }, \ + { name " ANC Source", "RXANCL", "RXANCL" }, \ + { name " ANC Source", "RXANCR", "RXANCR" } + static const struct snd_kcontrol_new wm5110_snd_controls[] = { SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]), SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]), @@ -639,6 +666,15 @@ SOC_SINGLE_TLV("IN4R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4R, SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), +SND_SOC_BYTES("RXANC Coefficients", ARIZONA_ANC_COEFF_START, + ARIZONA_ANC_COEFF_END - ARIZONA_ANC_COEFF_START + 1), +SND_SOC_BYTES("RXANCL Config", ARIZONA_FCL_FILTER_CONTROL, 1), +SND_SOC_BYTES("RXANCL Coefficients", ARIZONA_FCL_COEFF_START, + ARIZONA_FCL_COEFF_END - ARIZONA_FCL_COEFF_START + 1), +SND_SOC_BYTES("RXANCR Config", ARIZONA_FCR_FILTER_CONTROL, 1), +SND_SOC_BYTES("RXANCR Coefficients", ARIZONA_FCR_COEFF_START, + ARIZONA_FCR_COEFF_END - ARIZONA_FCR_COEFF_START + 1), + ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), @@ -995,6 +1031,31 @@ static const struct soc_enum wm5110_aec_loopback = static const struct snd_kcontrol_new wm5110_aec_loopback_mux = SOC_DAPM_ENUM("AEC Loopback", wm5110_aec_loopback); +static const struct snd_kcontrol_new wm5110_anc_input_mux[] = { + SOC_DAPM_ENUM("RXANCL Input", arizona_anc_input_src[0]), + SOC_DAPM_ENUM("RXANCL Channel", arizona_anc_input_src[1]), + SOC_DAPM_ENUM("RXANCR Input", arizona_anc_input_src[2]), + SOC_DAPM_ENUM("RXANCR Channel", arizona_anc_input_src[3]), +}; + +static const struct snd_kcontrol_new wm5110_anc_ng_mux = + SOC_DAPM_ENUM("RXANC NG Source", arizona_anc_ng_enum); + +static const struct snd_kcontrol_new wm5110_output_anc_src[] = { + SOC_DAPM_ENUM("HPOUT1L ANC Source", arizona_output_anc_src[0]), + SOC_DAPM_ENUM("HPOUT1R ANC Source", arizona_output_anc_src[1]), + SOC_DAPM_ENUM("HPOUT2L ANC Source", arizona_output_anc_src[2]), + SOC_DAPM_ENUM("HPOUT2R ANC Source", arizona_output_anc_src[3]), + SOC_DAPM_ENUM("HPOUT3L ANC Source", arizona_output_anc_src[4]), + SOC_DAPM_ENUM("HPOUT3R ANC Source", arizona_output_anc_src[5]), + SOC_DAPM_ENUM("SPKOUTL ANC Source", arizona_output_anc_src[6]), + SOC_DAPM_ENUM("SPKOUTR ANC Source", arizona_output_anc_src[7]), + SOC_DAPM_ENUM("SPKDAT1L ANC Source", arizona_output_anc_src[8]), + SOC_DAPM_ENUM("SPKDAT1R ANC Source", arizona_output_anc_src[9]), + SOC_DAPM_ENUM("SPKDAT2L ANC Source", arizona_output_anc_src[10]), + SOC_DAPM_ENUM("SPKDAT2R ANC Source", arizona_output_anc_src[11]), +}; + static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, 0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU), @@ -1185,6 +1246,65 @@ SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1, ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, &wm5110_aec_loopback_mux), +SND_SOC_DAPM_SUPPLY("RXANC NG External Clock", SND_SOC_NOPM, + ARIZONA_EXT_NG_SEL_SET_SHIFT, 0, arizona_anc_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA("RXANCL NG External", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("RXANCR NG External", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("RXANC NG Clock", SND_SOC_NOPM, + ARIZONA_CLK_NG_ENA_SET_SHIFT, 0, arizona_anc_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA("RXANCL NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("RXANCR NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_MUX("RXANCL Left Input", SND_SOC_NOPM, 0, 0, + &wm5110_anc_input_mux[0]), +SND_SOC_DAPM_MUX("RXANCL Right Input", SND_SOC_NOPM, 0, 0, + &wm5110_anc_input_mux[0]), +SND_SOC_DAPM_MUX("RXANCL Channel", SND_SOC_NOPM, 0, 0, + &wm5110_anc_input_mux[1]), +SND_SOC_DAPM_MUX("RXANCL NG Mux", SND_SOC_NOPM, 0, 0, &wm5110_anc_ng_mux), +SND_SOC_DAPM_MUX("RXANCR Left Input", SND_SOC_NOPM, 0, 0, + &wm5110_anc_input_mux[2]), +SND_SOC_DAPM_MUX("RXANCR Right Input", SND_SOC_NOPM, 0, 0, + &wm5110_anc_input_mux[2]), +SND_SOC_DAPM_MUX("RXANCR Channel", SND_SOC_NOPM, 0, 0, + &wm5110_anc_input_mux[3]), +SND_SOC_DAPM_MUX("RXANCR NG Mux", SND_SOC_NOPM, 0, 0, &wm5110_anc_ng_mux), + +SND_SOC_DAPM_PGA_E("RXANCL", SND_SOC_NOPM, ARIZONA_CLK_L_ENA_SET_SHIFT, + 0, NULL, 0, arizona_anc_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_E("RXANCR", SND_SOC_NOPM, ARIZONA_CLK_R_ENA_SET_SHIFT, + 0, NULL, 0, arizona_anc_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_MUX("HPOUT1L ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[0]), +SND_SOC_DAPM_MUX("HPOUT1R ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[1]), +SND_SOC_DAPM_MUX("HPOUT2L ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[2]), +SND_SOC_DAPM_MUX("HPOUT2R ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[3]), +SND_SOC_DAPM_MUX("HPOUT3L ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[4]), +SND_SOC_DAPM_MUX("HPOUT3R ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[5]), +SND_SOC_DAPM_MUX("SPKOUTL ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[6]), +SND_SOC_DAPM_MUX("SPKOUTR ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[7]), +SND_SOC_DAPM_MUX("SPKDAT1L ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[8]), +SND_SOC_DAPM_MUX("SPKDAT1R ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[9]), +SND_SOC_DAPM_MUX("SPKDAT2L ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[10]), +SND_SOC_DAPM_MUX("SPKDAT2R ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[11]), + SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, @@ -1690,6 +1810,9 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { { "Slim2 Capture", NULL, "SYSCLK" }, { "Slim3 Capture", NULL, "SYSCLK" }, + { "Voice Control DSP", NULL, "DSP3" }, + { "Voice Control DSP", NULL, "SYSCLK" }, + { "IN1L PGA", NULL, "IN1L" }, { "IN1R PGA", NULL, "IN1R" }, @@ -1838,6 +1961,22 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { { "SPKDAT2L", NULL, "OUT6L" }, { "SPKDAT2R", NULL, "OUT6R" }, + WM5110_RXANC_INPUT_ROUTES("RXANCL", "RXANCL"), + WM5110_RXANC_INPUT_ROUTES("RXANCR", "RXANCR"), + + WM5110_RXANC_OUTPUT_ROUTES("OUT1L", "HPOUT1L"), + WM5110_RXANC_OUTPUT_ROUTES("OUT1R", "HPOUT1R"), + WM5110_RXANC_OUTPUT_ROUTES("OUT2L", "HPOUT2L"), + WM5110_RXANC_OUTPUT_ROUTES("OUT2R", "HPOUT2R"), + WM5110_RXANC_OUTPUT_ROUTES("OUT3L", "HPOUT3L"), + WM5110_RXANC_OUTPUT_ROUTES("OUT3R", "HPOUT3R"), + WM5110_RXANC_OUTPUT_ROUTES("OUT4L", "SPKOUTL"), + WM5110_RXANC_OUTPUT_ROUTES("OUT4R", "SPKOUTR"), + WM5110_RXANC_OUTPUT_ROUTES("OUT5L", "SPKDAT1L"), + WM5110_RXANC_OUTPUT_ROUTES("OUT5R", "SPKDAT1R"), + WM5110_RXANC_OUTPUT_ROUTES("OUT6L", "SPKDAT2L"), + WM5110_RXANC_OUTPUT_ROUTES("OUT6R", "SPKDAT2R"), + { "MICSUPP", NULL, "SYSCLK" }, { "DRC1 Signal Activity", NULL, "DRC1L" }, @@ -1996,8 +2135,48 @@ static struct snd_soc_dai_driver wm5110_dai[] = { }, .ops = &arizona_simple_dai_ops, }, + { + .name = "wm5110-cpu-voicectrl", + .capture = { + .stream_name = "Voice Control CPU", + .channels_min = 1, + .channels_max = 1, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .compress_new = snd_soc_new_compress, + }, + { + .name = "wm5110-dsp-voicectrl", + .capture = { + .stream_name = "Voice Control DSP", + .channels_min = 1, + .channels_max = 1, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + }, }; +static int wm5110_open(struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct wm5110_priv *priv = snd_soc_codec_get_drvdata(rtd->codec); + struct arizona *arizona = priv->core.arizona; + int n_adsp; + + if (strcmp(rtd->codec_dai->name, "wm5110-dsp-voicectrl") == 0) { + n_adsp = 2; + } else { + dev_err(arizona->dev, + "No suitable compressed stream for DAI '%s'\n", + rtd->codec_dai->name); + return -EINVAL; + } + + return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream); +} + static int wm5110_codec_probe(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); @@ -2088,6 +2267,18 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5110 = { .num_dapm_routes = ARRAY_SIZE(wm5110_dapm_routes), }; +static struct snd_compr_ops wm5110_compr_ops = { + .open = wm5110_open, + .free = wm_adsp_compr_free, + .set_params = wm_adsp_compr_set_params, + .get_caps = wm_adsp_compr_get_caps, + .trigger = wm_adsp_compr_trigger, +}; + +static struct snd_soc_platform_driver wm5110_compr_platform = { + .compr_ops = &wm5110_compr_ops, +}; + static int wm5110_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); @@ -2148,8 +2339,21 @@ static int wm5110_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); - return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110, + ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register platform: %d\n", ret); + goto error; + } + + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110, wm5110_dai, ARRAY_SIZE(wm5110_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); + snd_soc_unregister_platform(&pdev->dev); + } + +error: + return ret; } static int wm5110_remove(struct platform_device *pdev) diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 056375339ea3..5380798883b5 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -229,7 +229,7 @@ SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL, SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL, 6, 1, 0), SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL, - 7, 1, 0), + 7, 1, 1), SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume", WM8960_INBMIX1, 4, 7, 0, lineinboost_tlv), diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 39ebd7bf4f53..a7e79784fc16 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -365,8 +365,8 @@ static const struct reg_default wm8962_reg[] = { { 16924, 0x0059 }, /* R16924 - HDBASS_PG_1 */ { 16925, 0x999A }, /* R16925 - HDBASS_PG_0 */ - { 17048, 0x0083 }, /* R17408 - HPF_C_1 */ - { 17049, 0x98AD }, /* R17409 - HPF_C_0 */ + { 17408, 0x0083 }, /* R17408 - HPF_C_1 */ + { 17409, 0x98AD }, /* R17409 - HPF_C_0 */ { 17920, 0x007F }, /* R17920 - ADCL_RETUNE_C1_1 */ { 17921, 0xFFFF }, /* R17921 - ADCL_RETUNE_C1_0 */ diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index 0a60677397b3..4c29bd2ae75c 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -574,6 +574,7 @@ static const struct regmap_config wm8974_regmap = { .max_register = WM8974_MONOMIX, .reg_defaults = wm8974_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8974_reg_defaults), + .cache_type = REGCACHE_FLAT, }; static int wm8974_probe(struct snd_soc_codec *codec) diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c index 8782dfb628ab..7719bc509e50 100644 --- a/sound/soc/codecs/wm8998.c +++ b/sound/soc/codecs/wm8998.c @@ -199,20 +199,20 @@ static const char * const wm8998_inmux_texts[] = { "B", }; -static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum, - ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_IN1L_SRC_SHIFT, - wm8998_inmux_texts); +static SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum, + ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_SRC_SHIFT, + wm8998_inmux_texts); -static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxr_enum, - ARIZONA_ADC_DIGITAL_VOLUME_1R, - ARIZONA_IN1R_SRC_SHIFT, - wm8998_inmux_texts); +static SOC_ENUM_SINGLE_DECL(wm8998_in1muxr_enum, + ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_SRC_SHIFT, + wm8998_inmux_texts); -static const SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum, - ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_IN2L_SRC_SHIFT, - wm8998_inmux_texts); +static SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum, + ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_SRC_SHIFT, + wm8998_inmux_texts); static const struct snd_kcontrol_new wm8998_in1mux[2] = { SOC_DAPM_ENUM_EXT("IN1L Mux", wm8998_in1muxl_enum, @@ -522,17 +522,17 @@ static const unsigned int wm8998_aec_loopback_values[] = { 0, 1, 2, 3, 4, 6, 7, 8, 9, }; -static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec1_loopback, - ARIZONA_DAC_AEC_CONTROL_1, - ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, - wm8998_aec_loopback_texts, - wm8998_aec_loopback_values); - -static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec2_loopback, - ARIZONA_DAC_AEC_CONTROL_2, - ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, - wm8998_aec_loopback_texts, - wm8998_aec_loopback_values); +static SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec1_loopback, + ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + wm8998_aec_loopback_texts, + wm8998_aec_loopback_values); + +static SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec2_loopback, + ARIZONA_DAC_AEC_CONTROL_2, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + wm8998_aec_loopback_texts, + wm8998_aec_loopback_values); static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = { SOC_DAPM_ENUM("AEC1 Loopback", wm8998_aec1_loopback), diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 0bb415a28723..ac879d16c6a6 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -201,27 +201,186 @@ static void wm_adsp_buf_free(struct list_head *list) } } -#define WM_ADSP_NUM_FW 4 - -#define WM_ADSP_FW_MBC_VSS 0 -#define WM_ADSP_FW_TX 1 -#define WM_ADSP_FW_TX_SPK 2 -#define WM_ADSP_FW_RX_ANC 3 +#define WM_ADSP_FW_MBC_VSS 0 +#define WM_ADSP_FW_HIFI 1 +#define WM_ADSP_FW_TX 2 +#define WM_ADSP_FW_TX_SPK 3 +#define WM_ADSP_FW_RX 4 +#define WM_ADSP_FW_RX_ANC 5 +#define WM_ADSP_FW_CTRL 6 +#define WM_ADSP_FW_ASR 7 +#define WM_ADSP_FW_TRACE 8 +#define WM_ADSP_FW_SPK_PROT 9 +#define WM_ADSP_FW_MISC 10 + +#define WM_ADSP_NUM_FW 11 static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { - [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", - [WM_ADSP_FW_TX] = "Tx", - [WM_ADSP_FW_TX_SPK] = "Tx Speaker", - [WM_ADSP_FW_RX_ANC] = "Rx ANC", + [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", + [WM_ADSP_FW_HIFI] = "MasterHiFi", + [WM_ADSP_FW_TX] = "Tx", + [WM_ADSP_FW_TX_SPK] = "Tx Speaker", + [WM_ADSP_FW_RX] = "Rx", + [WM_ADSP_FW_RX_ANC] = "Rx ANC", + [WM_ADSP_FW_CTRL] = "Voice Ctrl", + [WM_ADSP_FW_ASR] = "ASR Assist", + [WM_ADSP_FW_TRACE] = "Dbg Trace", + [WM_ADSP_FW_SPK_PROT] = "Protection", + [WM_ADSP_FW_MISC] = "Misc", +}; + +struct wm_adsp_system_config_xm_hdr { + __be32 sys_enable; + __be32 fw_id; + __be32 fw_rev; + __be32 boot_status; + __be32 watchdog; + __be32 dma_buffer_size; + __be32 rdma[6]; + __be32 wdma[8]; + __be32 build_job_name[3]; + __be32 build_job_number; +}; + +struct wm_adsp_alg_xm_struct { + __be32 magic; + __be32 smoothing; + __be32 threshold; + __be32 host_buf_ptr; + __be32 start_seq; + __be32 high_water_mark; + __be32 low_water_mark; + __be64 smoothed_power; +}; + +struct wm_adsp_buffer { + __be32 X_buf_base; /* XM base addr of first X area */ + __be32 X_buf_size; /* Size of 1st X area in words */ + __be32 X_buf_base2; /* XM base addr of 2nd X area */ + __be32 X_buf_brk; /* Total X size in words */ + __be32 Y_buf_base; /* YM base addr of Y area */ + __be32 wrap; /* Total size X and Y in words */ + __be32 high_water_mark; /* Point at which IRQ is asserted */ + __be32 irq_count; /* bits 1-31 count IRQ assertions */ + __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ + __be32 next_write_index; /* word index of next write */ + __be32 next_read_index; /* word index of next read */ + __be32 error; /* error if any */ + __be32 oldest_block_index; /* word index of oldest surviving */ + __be32 requested_rewind; /* how many blocks rewind was done */ + __be32 reserved_space; /* internal */ + __be32 min_free; /* min free space since stream start */ + __be32 blocks_written[2]; /* total blocks written (64 bit) */ + __be32 words_written[2]; /* total words written (64 bit) */ +}; + +struct wm_adsp_compr_buf { + struct wm_adsp *dsp; + + struct wm_adsp_buffer_region *regions; + u32 host_buf_ptr; +}; + +struct wm_adsp_compr { + struct wm_adsp *dsp; + struct wm_adsp_compr_buf *buf; + + struct snd_compr_stream *stream; + struct snd_compressed_buffer size; +}; + +#define WM_ADSP_DATA_WORD_SIZE 3 + +#define WM_ADSP_MIN_FRAGMENTS 1 +#define WM_ADSP_MAX_FRAGMENTS 256 +#define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE) +#define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE) + +#define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 + +#define HOST_BUFFER_FIELD(field) \ + (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32)) + +#define ALG_XM_FIELD(field) \ + (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) + +static int wm_adsp_buffer_init(struct wm_adsp *dsp); +static int wm_adsp_buffer_free(struct wm_adsp *dsp); + +struct wm_adsp_buffer_region { + unsigned int offset; + unsigned int cumulative_size; + unsigned int mem_type; + unsigned int base_addr; +}; + +struct wm_adsp_buffer_region_def { + unsigned int mem_type; + unsigned int base_offset; + unsigned int size_offset; +}; + +static struct wm_adsp_buffer_region_def ez2control_regions[] = { + { + .mem_type = WMFW_ADSP2_XM, + .base_offset = HOST_BUFFER_FIELD(X_buf_base), + .size_offset = HOST_BUFFER_FIELD(X_buf_size), + }, + { + .mem_type = WMFW_ADSP2_XM, + .base_offset = HOST_BUFFER_FIELD(X_buf_base2), + .size_offset = HOST_BUFFER_FIELD(X_buf_brk), + }, + { + .mem_type = WMFW_ADSP2_YM, + .base_offset = HOST_BUFFER_FIELD(Y_buf_base), + .size_offset = HOST_BUFFER_FIELD(wrap), + }, +}; + +struct wm_adsp_fw_caps { + u32 id; + struct snd_codec_desc desc; + int num_regions; + struct wm_adsp_buffer_region_def *region_defs; +}; + +static const struct wm_adsp_fw_caps ez2control_caps[] = { + { + .id = SND_AUDIOCODEC_BESPOKE, + .desc = { + .max_ch = 1, + .sample_rates = { 16000 }, + .num_sample_rates = 1, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .num_regions = ARRAY_SIZE(ez2control_regions), + .region_defs = ez2control_regions, + }, }; -static struct { +static const struct { const char *file; + int compr_direction; + int num_caps; + const struct wm_adsp_fw_caps *caps; } wm_adsp_fw[WM_ADSP_NUM_FW] = { - [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, - [WM_ADSP_FW_TX] = { .file = "tx" }, - [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, - [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, + [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, + [WM_ADSP_FW_HIFI] = { .file = "hifi" }, + [WM_ADSP_FW_TX] = { .file = "tx" }, + [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, + [WM_ADSP_FW_RX] = { .file = "rx" }, + [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, + [WM_ADSP_FW_CTRL] = { + .file = "ctrl", + .compr_direction = SND_COMPRESS_CAPTURE, + .num_caps = ARRAY_SIZE(ez2control_caps), + .caps = ez2control_caps, + }, + [WM_ADSP_FW_ASR] = { .file = "asr" }, + [WM_ADSP_FW_TRACE] = { .file = "trace" }, + [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, + [WM_ADSP_FW_MISC] = { .file = "misc" }, }; struct wm_coeff_ctl_ops { @@ -254,30 +413,24 @@ static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) { char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); - mutex_lock(&dsp->debugfs_lock); kfree(dsp->wmfw_file_name); dsp->wmfw_file_name = tmp; - mutex_unlock(&dsp->debugfs_lock); } static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) { char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); - mutex_lock(&dsp->debugfs_lock); kfree(dsp->bin_file_name); dsp->bin_file_name = tmp; - mutex_unlock(&dsp->debugfs_lock); } static void wm_adsp_debugfs_clear(struct wm_adsp *dsp) { - mutex_lock(&dsp->debugfs_lock); kfree(dsp->wmfw_file_name); kfree(dsp->bin_file_name); dsp->wmfw_file_name = NULL; dsp->bin_file_name = NULL; - mutex_unlock(&dsp->debugfs_lock); } static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, @@ -287,7 +440,7 @@ static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, struct wm_adsp *dsp = file->private_data; ssize_t ret; - mutex_lock(&dsp->debugfs_lock); + mutex_lock(&dsp->pwr_lock); if (!dsp->wmfw_file_name || !dsp->running) ret = 0; @@ -296,7 +449,7 @@ static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, dsp->wmfw_file_name, strlen(dsp->wmfw_file_name)); - mutex_unlock(&dsp->debugfs_lock); + mutex_unlock(&dsp->pwr_lock); return ret; } @@ -307,7 +460,7 @@ static ssize_t wm_adsp_debugfs_bin_read(struct file *file, struct wm_adsp *dsp = file->private_data; ssize_t ret; - mutex_lock(&dsp->debugfs_lock); + mutex_lock(&dsp->pwr_lock); if (!dsp->bin_file_name || !dsp->running) ret = 0; @@ -316,7 +469,7 @@ static ssize_t wm_adsp_debugfs_bin_read(struct file *file, dsp->bin_file_name, strlen(dsp->bin_file_name)); - mutex_unlock(&dsp->debugfs_lock); + mutex_unlock(&dsp->pwr_lock); return ret; } @@ -436,6 +589,7 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); + int ret = 0; if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw) return 0; @@ -443,12 +597,16 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW) return -EINVAL; - if (dsp[e->shift_l].running) - return -EBUSY; + mutex_lock(&dsp[e->shift_l].pwr_lock); - dsp[e->shift_l].fw = ucontrol->value.integer.value[0]; + if (dsp[e->shift_l].running || dsp[e->shift_l].compr) + ret = -EBUSY; + else + dsp[e->shift_l].fw = ucontrol->value.integer.value[0]; - return 0; + mutex_unlock(&dsp[e->shift_l].pwr_lock); + + return ret; } static const struct soc_enum wm_adsp_fw_enum[] = { @@ -523,10 +681,10 @@ static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) be16_to_cpu(scratch[3])); } -static int wm_coeff_info(struct snd_kcontrol *kcontrol, +static int wm_coeff_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { - struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; + struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; uinfo->count = ctl->len; @@ -572,19 +730,24 @@ static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, return 0; } -static int wm_coeff_put(struct snd_kcontrol *kcontrol, +static int wm_coeff_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { - struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; + struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; char *p = ucontrol->value.bytes.data; + int ret = 0; + + mutex_lock(&ctl->dsp->pwr_lock); memcpy(ctl->cache, p, ctl->len); ctl->set = 1; - if (!ctl->enabled) - return 0; + if (ctl->enabled) + ret = wm_coeff_write_control(ctl, p, ctl->len); + + mutex_unlock(&ctl->dsp->pwr_lock); - return wm_coeff_write_control(ctl, p, ctl->len); + return ret; } static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, @@ -626,22 +789,30 @@ static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, return 0; } -static int wm_coeff_get(struct snd_kcontrol *kcontrol, +static int wm_coeff_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { - struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; + struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; char *p = ucontrol->value.bytes.data; + int ret = 0; + + mutex_lock(&ctl->dsp->pwr_lock); if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { if (ctl->enabled) - return wm_coeff_read_control(ctl, p, ctl->len); + ret = wm_coeff_read_control(ctl, p, ctl->len); else - return -EPERM; + ret = -EPERM; + } else { + if (!ctl->flags && ctl->enabled) + ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); + + memcpy(p, ctl->cache, ctl->len); } - memcpy(p, ctl->cache, ctl->len); + mutex_unlock(&ctl->dsp->pwr_lock); - return 0; + return ret; } struct wmfw_ctl_work { @@ -808,8 +979,7 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, break; } - list_for_each_entry(ctl, &dsp->ctl_list, - list) { + list_for_each_entry(ctl, &dsp->ctl_list, list) { if (!strcmp(ctl->name, name)) { if (!ctl->enabled) ctl->enabled = 1; @@ -1088,7 +1258,7 @@ static int wm_adsp_load(struct wm_adsp *dsp) goto out_fw; } - header = (void*)&firmware->data[0]; + header = (void *)&firmware->data[0]; if (memcmp(&header->magic[0], "WMFW", 4) != 0) { adsp_err(dsp, "%s: invalid magic\n", file); @@ -1168,7 +1338,7 @@ static int wm_adsp_load(struct wm_adsp *dsp) offset = le32_to_cpu(region->offset) & 0xffffff; type = be32_to_cpu(region->type) & 0xff; mem = wm_adsp_find_region(dsp, type); - + switch (type) { case WMFW_NAME_TEXT: region_name = "Firmware name"; @@ -1333,6 +1503,19 @@ static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, return alg; } +static struct wm_adsp_alg_region * + wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id) +{ + struct wm_adsp_alg_region *alg_region; + + list_for_each_entry(alg_region, &dsp->alg_regions, list) { + if (id == alg_region->alg && type == alg_region->type) + return alg_region; + } + + return NULL; +} + static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, int type, __be32 id, __be32 base) @@ -1625,7 +1808,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) goto out_fw; } - hdr = (void*)&firmware->data[0]; + hdr = (void *)&firmware->data[0]; if (memcmp(hdr->magic, "WMDR", 4) != 0) { adsp_err(dsp, "%s: invalid magic\n", file); goto out_fw; @@ -1651,7 +1834,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) blocks = 0; while (pos < firmware->size && pos - firmware->size > sizeof(*blk)) { - blk = (void*)(&firmware->data[pos]); + blk = (void *)(&firmware->data[pos]); type = le16_to_cpu(blk->type); offset = le16_to_cpu(blk->offset); @@ -1705,22 +1888,16 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) break; } - reg = 0; - list_for_each_entry(alg_region, - &dsp->alg_regions, list) { - if (le32_to_cpu(blk->id) == alg_region->alg && - type == alg_region->type) { - reg = alg_region->base; - reg = wm_adsp_region_to_reg(mem, - reg); - reg += offset; - break; - } - } - - if (reg == 0) + alg_region = wm_adsp_find_alg_region(dsp, type, + le32_to_cpu(blk->id)); + if (alg_region) { + reg = alg_region->base; + reg = wm_adsp_region_to_reg(mem, reg); + reg += offset; + } else { adsp_err(dsp, "No %x for algorithm %x\n", type, le32_to_cpu(blk->id)); + } break; default: @@ -1778,9 +1955,8 @@ int wm_adsp1_init(struct wm_adsp *dsp) { INIT_LIST_HEAD(&dsp->alg_regions); -#ifdef CONFIG_DEBUG_FS - mutex_init(&dsp->debugfs_lock); -#endif + mutex_init(&dsp->pwr_lock); + return 0; } EXPORT_SYMBOL_GPL(wm_adsp1_init); @@ -1795,10 +1971,12 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct wm_adsp_alg_region *alg_region; struct wm_coeff_ctl *ctl; int ret; - int val; + unsigned int val; dsp->card = codec->component.card; + mutex_lock(&dsp->pwr_lock); + switch (event) { case SND_SOC_DAPM_POST_PMU: regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, @@ -1808,12 +1986,12 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, * For simplicity set the DSP clock rate to be the * SYSCLK rate rather than making it configurable. */ - if(dsp->sysclk_reg) { + if (dsp->sysclk_reg) { ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); if (ret != 0) { adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret); - return ret; + goto err_mutex; } val = (val & dsp->sysclk_mask) @@ -1825,31 +2003,31 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, if (ret != 0) { adsp_err(dsp, "Failed to set clock rate: %d\n", ret); - return ret; + goto err_mutex; } } ret = wm_adsp_load(dsp); if (ret != 0) - goto err; + goto err_ena; ret = wm_adsp1_setup_algs(dsp); if (ret != 0) - goto err; + goto err_ena; ret = wm_adsp_load_coeff(dsp); if (ret != 0) - goto err; + goto err_ena; /* Initialize caches for enabled and unset controls */ ret = wm_coeff_init_control_caches(dsp); if (ret != 0) - goto err; + goto err_ena; /* Sync set controls */ ret = wm_coeff_sync_controls(dsp); if (ret != 0) - goto err; + goto err_ena; /* Start the core running */ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, @@ -1884,11 +2062,16 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, break; } + mutex_unlock(&dsp->pwr_lock); + return 0; -err: +err_ena: regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ADSP1_SYS_ENA, 0); +err_mutex: + mutex_unlock(&dsp->pwr_lock); + return ret; } EXPORT_SYMBOL_GPL(wm_adsp1_event); @@ -1934,6 +2117,8 @@ static void wm_adsp2_boot_work(struct work_struct *work) int ret; unsigned int val; + mutex_lock(&dsp->pwr_lock); + /* * For simplicity set the DSP clock rate to be the * SYSCLK rate rather than making it configurable. @@ -1941,7 +2126,7 @@ static void wm_adsp2_boot_work(struct work_struct *work) ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val); if (ret != 0) { adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret); - return; + goto err_mutex; } val = (val & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT; @@ -1951,42 +2136,46 @@ static void wm_adsp2_boot_work(struct work_struct *work) ADSP2_CLK_SEL_MASK, val); if (ret != 0) { adsp_err(dsp, "Failed to set clock rate: %d\n", ret); - return; + goto err_mutex; } ret = wm_adsp2_ena(dsp); if (ret != 0) - return; + goto err_mutex; ret = wm_adsp_load(dsp); if (ret != 0) - goto err; + goto err_ena; ret = wm_adsp2_setup_algs(dsp); if (ret != 0) - goto err; + goto err_ena; ret = wm_adsp_load_coeff(dsp); if (ret != 0) - goto err; + goto err_ena; /* Initialize caches for enabled and unset controls */ ret = wm_coeff_init_control_caches(dsp); if (ret != 0) - goto err; + goto err_ena; /* Sync set controls */ ret = wm_coeff_sync_controls(dsp); if (ret != 0) - goto err; + goto err_ena; dsp->running = true; + mutex_unlock(&dsp->pwr_lock); + return; -err: +err_ena: regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); +err_mutex: + mutex_unlock(&dsp->pwr_lock); } int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, @@ -2033,12 +2222,18 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ADSP2_CORE_ENA | ADSP2_START); if (ret != 0) goto err; + + if (wm_adsp_fw[dsp->fw].num_caps != 0) + ret = wm_adsp_buffer_init(dsp); + break; case SND_SOC_DAPM_PRE_PMD: /* Log firmware state, it can be useful for analysis */ wm_adsp2_show_fw_status(dsp); + mutex_lock(&dsp->pwr_lock); + wm_adsp_debugfs_clear(dsp); dsp->fw_id = 0; @@ -2065,6 +2260,11 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, kfree(alg_region); } + if (wm_adsp_fw[dsp->fw].num_caps != 0) + wm_adsp_buffer_free(dsp); + + mutex_unlock(&dsp->pwr_lock); + adsp_dbg(dsp, "Shutdown complete\n"); break; @@ -2117,11 +2317,406 @@ int wm_adsp2_init(struct wm_adsp *dsp) INIT_LIST_HEAD(&dsp->ctl_list); INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work); -#ifdef CONFIG_DEBUG_FS - mutex_init(&dsp->debugfs_lock); -#endif + mutex_init(&dsp->pwr_lock); + return 0; } EXPORT_SYMBOL_GPL(wm_adsp2_init); +int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) +{ + struct wm_adsp_compr *compr; + int ret = 0; + + mutex_lock(&dsp->pwr_lock); + + if (wm_adsp_fw[dsp->fw].num_caps == 0) { + adsp_err(dsp, "Firmware does not support compressed API\n"); + ret = -ENXIO; + goto out; + } + + if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { + adsp_err(dsp, "Firmware does not support stream direction\n"); + ret = -EINVAL; + goto out; + } + + if (dsp->compr) { + /* It is expect this limitation will be removed in future */ + adsp_err(dsp, "Only a single stream supported per DSP\n"); + ret = -EBUSY; + goto out; + } + + compr = kzalloc(sizeof(*compr), GFP_KERNEL); + if (!compr) { + ret = -ENOMEM; + goto out; + } + + compr->dsp = dsp; + compr->stream = stream; + + dsp->compr = compr; + + stream->runtime->private_data = compr; + +out: + mutex_unlock(&dsp->pwr_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(wm_adsp_compr_open); + +int wm_adsp_compr_free(struct snd_compr_stream *stream) +{ + struct wm_adsp_compr *compr = stream->runtime->private_data; + struct wm_adsp *dsp = compr->dsp; + + mutex_lock(&dsp->pwr_lock); + + dsp->compr = NULL; + + kfree(compr); + + mutex_unlock(&dsp->pwr_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp_compr_free); + +static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, + struct snd_compr_params *params) +{ + struct wm_adsp_compr *compr = stream->runtime->private_data; + struct wm_adsp *dsp = compr->dsp; + const struct wm_adsp_fw_caps *caps; + const struct snd_codec_desc *desc; + int i, j; + + if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || + params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || + params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || + params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || + params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) { + adsp_err(dsp, "Invalid buffer fragsize=%d fragments=%d\n", + params->buffer.fragment_size, + params->buffer.fragments); + + return -EINVAL; + } + + for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { + caps = &wm_adsp_fw[dsp->fw].caps[i]; + desc = &caps->desc; + + if (caps->id != params->codec.id) + continue; + + if (stream->direction == SND_COMPRESS_PLAYBACK) { + if (desc->max_ch < params->codec.ch_out) + continue; + } else { + if (desc->max_ch < params->codec.ch_in) + continue; + } + + if (!(desc->formats & (1 << params->codec.format))) + continue; + + for (j = 0; j < desc->num_sample_rates; ++j) + if (desc->sample_rates[j] == params->codec.sample_rate) + return 0; + } + + adsp_err(dsp, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", + params->codec.id, params->codec.ch_in, params->codec.ch_out, + params->codec.sample_rate, params->codec.format); + return -EINVAL; +} + +int wm_adsp_compr_set_params(struct snd_compr_stream *stream, + struct snd_compr_params *params) +{ + struct wm_adsp_compr *compr = stream->runtime->private_data; + int ret; + + ret = wm_adsp_compr_check_params(stream, params); + if (ret) + return ret; + + compr->size = params->buffer; + + adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n", + compr->size.fragment_size, compr->size.fragments); + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); + +int wm_adsp_compr_get_caps(struct snd_compr_stream *stream, + struct snd_compr_caps *caps) +{ + struct wm_adsp_compr *compr = stream->runtime->private_data; + int fw = compr->dsp->fw; + int i; + + if (wm_adsp_fw[fw].caps) { + for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) + caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; + + caps->num_codecs = i; + caps->direction = wm_adsp_fw[fw].compr_direction; + + caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; + caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; + caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; + caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; + } + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); + +static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type, + unsigned int mem_addr, + unsigned int num_words, u32 *data) +{ + struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); + unsigned int i, reg; + int ret; + + if (!mem) + return -EINVAL; + + reg = wm_adsp_region_to_reg(mem, mem_addr); + + ret = regmap_raw_read(dsp->regmap, reg, data, + sizeof(*data) * num_words); + if (ret < 0) + return ret; + + for (i = 0; i < num_words; ++i) + data[i] = be32_to_cpu(data[i]) & 0x00ffffffu; + + return 0; +} + +static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type, + unsigned int mem_addr, u32 *data) +{ + return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data); +} + +static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type, + unsigned int mem_addr, u32 data) +{ + struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); + unsigned int reg; + + if (!mem) + return -EINVAL; + + reg = wm_adsp_region_to_reg(mem, mem_addr); + + data = cpu_to_be32(data & 0x00ffffffu); + + return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data)); +} + +static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, + unsigned int field_offset, u32 *data) +{ + return wm_adsp_read_data_word(buf->dsp, WMFW_ADSP2_XM, + buf->host_buf_ptr + field_offset, data); +} + +static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, + unsigned int field_offset, u32 data) +{ + return wm_adsp_write_data_word(buf->dsp, WMFW_ADSP2_XM, + buf->host_buf_ptr + field_offset, data); +} + +static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) +{ + struct wm_adsp_alg_region *alg_region; + struct wm_adsp *dsp = buf->dsp; + u32 xmalg, addr, magic; + int i, ret; + + alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); + xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32); + + addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); + ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic); + if (ret < 0) + return ret; + + if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) + return -EINVAL; + + addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); + for (i = 0; i < 5; ++i) { + ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, + &buf->host_buf_ptr); + if (ret < 0) + return ret; + + if (buf->host_buf_ptr) + break; + + usleep_range(1000, 2000); + } + + if (!buf->host_buf_ptr) + return -EIO; + + adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr); + + return 0; +} + +static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) +{ + const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; + struct wm_adsp_buffer_region *region; + u32 offset = 0; + int i, ret; + + for (i = 0; i < caps->num_regions; ++i) { + region = &buf->regions[i]; + + region->offset = offset; + region->mem_type = caps->region_defs[i].mem_type; + + ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, + ®ion->base_addr); + if (ret < 0) + return ret; + + ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, + &offset); + if (ret < 0) + return ret; + + region->cumulative_size = offset; + + adsp_dbg(buf->dsp, + "region=%d type=%d base=%04x off=%04x size=%04x\n", + i, region->mem_type, region->base_addr, + region->offset, region->cumulative_size); + } + + return 0; +} + +static int wm_adsp_buffer_init(struct wm_adsp *dsp) +{ + struct wm_adsp_compr_buf *buf; + int ret; + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf->dsp = dsp; + + ret = wm_adsp_buffer_locate(buf); + if (ret < 0) { + adsp_err(dsp, "Failed to acquire host buffer: %d\n", ret); + goto err_buffer; + } + + buf->regions = kcalloc(wm_adsp_fw[dsp->fw].caps->num_regions, + sizeof(*buf->regions), GFP_KERNEL); + if (!buf->regions) { + ret = -ENOMEM; + goto err_buffer; + } + + ret = wm_adsp_buffer_populate(buf); + if (ret < 0) { + adsp_err(dsp, "Failed to populate host buffer: %d\n", ret); + goto err_regions; + } + + dsp->buffer = buf; + + return 0; + +err_regions: + kfree(buf->regions); +err_buffer: + kfree(buf); + return ret; +} + +static int wm_adsp_buffer_free(struct wm_adsp *dsp) +{ + if (dsp->buffer) { + kfree(dsp->buffer->regions); + kfree(dsp->buffer); + + dsp->buffer = NULL; + } + + 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; + struct wm_adsp *dsp = compr->dsp; + int ret = 0; + + adsp_dbg(dsp, "Trigger: %d\n", cmd); + + mutex_lock(&dsp->pwr_lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (wm_adsp_compr_attached(compr)) + break; + + ret = wm_adsp_compr_attach(compr); + if (ret < 0) { + adsp_err(dsp, "Failed to link buffer and stream: %d\n", + ret); + break; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&dsp->pwr_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); + MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 2d117cf0e953..43af093fafcf 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -15,6 +15,7 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> +#include <sound/compress_driver.h> #include "wmfw.h" @@ -30,6 +31,9 @@ struct wm_adsp_alg_region { unsigned int base; }; +struct wm_adsp_compr; +struct wm_adsp_compr_buf; + struct wm_adsp { const char *part; int num; @@ -45,8 +49,8 @@ struct wm_adsp { struct list_head alg_regions; - int fw_id; - int fw_id_version; + unsigned int fw_id; + unsigned int fw_id_version; const struct wm_adsp_region *mem; int num_mems; @@ -59,9 +63,13 @@ struct wm_adsp { struct work_struct boot_work; + struct wm_adsp_compr *compr; + struct wm_adsp_compr_buf *buffer; + + struct mutex pwr_lock; + #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_root; - struct mutex debugfs_lock; char *wmfw_file_name; char *bin_file_name; #endif @@ -96,4 +104,13 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); +extern int wm_adsp_compr_open(struct wm_adsp *dsp, + struct snd_compr_stream *stream); +extern int wm_adsp_compr_free(struct snd_compr_stream *stream); +extern int wm_adsp_compr_set_params(struct snd_compr_stream *stream, + struct snd_compr_params *params); +extern int wm_adsp_compr_get_caps(struct snd_compr_stream *stream, + struct snd_compr_caps *caps); +extern int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd); + #endif diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 4495a40a9468..2ccb8bccc9d4 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -223,8 +223,8 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp) /* wait for XDATA to be cleared */ cnt = 0; - while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) & - ~XRDATA) && (cnt < 100000)) + while ((mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) & XRDATA) && + (cnt < 100000)) cnt++; /* Release TX state machine */ @@ -681,8 +681,8 @@ static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai, } mcasp->tdm_slots = slots; - mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = rx_mask; - mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = tx_mask; + mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask; + mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = rx_mask; mcasp->slot_width = slot_width; return davinci_mcasp_set_ch_constraints(mcasp); @@ -908,6 +908,14 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD); mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRMOD(total_slots), FSRMOD(0x1FF)); + /* + * If McASP is set to be TX/RX synchronous and the playback is + * not running already we need to configure the TX slots in + * order to have correct FSX on the bus + */ + if (mcasp_is_synchronous(mcasp) && !mcasp->channels) + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, + FSXMOD(total_slots), FSXMOD(0x1FF)); } return 0; diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 6e6a70c5c2bd..825a1f480aab 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -18,6 +18,7 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/pm_runtime.h> #include <sound/designware_i2s.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -93,7 +94,12 @@ struct dw_i2s_dev { struct clk *clk; int active; unsigned int capability; + unsigned int quirks; + unsigned int i2s_reg_comp1; + unsigned int i2s_reg_comp2; struct device *dev; + u32 ccr; + u32 xfer_resolution; /* data related to DMA transfers b/w i2s and DMAC */ union dw_i2s_snd_dma_data play_dma_data; @@ -213,31 +219,58 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream, return 0; } +static void dw_i2s_config(struct dw_i2s_dev *dev, int stream) +{ + u32 ch_reg, irq; + struct i2s_clk_config_data *config = &dev->config; + + + i2s_disable_channels(dev, stream); + + for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + i2s_write_reg(dev->i2s_base, TCR(ch_reg), + dev->xfer_resolution); + i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02); + irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); + i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30); + i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); + } else { + i2s_write_reg(dev->i2s_base, RCR(ch_reg), + dev->xfer_resolution); + i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07); + irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); + i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03); + i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); + } + + } +} + static int dw_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); struct i2s_clk_config_data *config = &dev->config; - u32 ccr, xfer_resolution, ch_reg, irq; int ret; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: config->data_width = 16; - ccr = 0x00; - xfer_resolution = 0x02; + dev->ccr = 0x00; + dev->xfer_resolution = 0x02; break; case SNDRV_PCM_FORMAT_S24_LE: config->data_width = 24; - ccr = 0x08; - xfer_resolution = 0x04; + dev->ccr = 0x08; + dev->xfer_resolution = 0x04; break; case SNDRV_PCM_FORMAT_S32_LE: config->data_width = 32; - ccr = 0x10; - xfer_resolution = 0x05; + dev->ccr = 0x10; + dev->xfer_resolution = 0x05; break; default: @@ -258,27 +291,9 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - i2s_disable_channels(dev, substream->stream); + dw_i2s_config(dev, substream->stream); - for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) { - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - i2s_write_reg(dev->i2s_base, TCR(ch_reg), - xfer_resolution); - i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02); - irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); - i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30); - i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); - } else { - i2s_write_reg(dev->i2s_base, RCR(ch_reg), - xfer_resolution); - i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07); - irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); - i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03); - i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); - } - } - - i2s_write_reg(dev->i2s_base, CCR, ccr); + i2s_write_reg(dev->i2s_base, CCR, dev->ccr); config->sample_rate = params_rate(params); @@ -394,6 +409,23 @@ static const struct snd_soc_component_driver dw_i2s_component = { }; #ifdef CONFIG_PM +static int dw_i2s_runtime_suspend(struct device *dev) +{ + struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev); + + if (dw_dev->capability & DW_I2S_MASTER) + clk_disable(dw_dev->clk); + return 0; +} + +static int dw_i2s_runtime_resume(struct device *dev) +{ + struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev); + + if (dw_dev->capability & DW_I2S_MASTER) + clk_enable(dw_dev->clk); + return 0; +} static int dw_i2s_suspend(struct snd_soc_dai *dai) { @@ -410,6 +442,11 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) if (dev->capability & DW_I2S_MASTER) clk_enable(dev->clk); + + if (dai->playback_active) + dw_i2s_config(dev, SNDRV_PCM_STREAM_PLAYBACK); + if (dai->capture_active) + dw_i2s_config(dev, SNDRV_PCM_STREAM_CAPTURE); return 0; } @@ -459,8 +496,8 @@ static int dw_configure_dai(struct dw_i2s_dev *dev, * Read component parameter registers to extract * the I2S block's configuration. */ - u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); - u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); + u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1); + u32 comp2 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp2); u32 idx; if (COMP1_TX_ENABLED(comp1)) { @@ -503,7 +540,7 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, struct resource *res, const struct i2s_platform_data *pdata) { - u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); + u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1); u32 idx = COMP1_APB_DATA_WIDTH(comp1); int ret; @@ -607,6 +644,14 @@ static int dw_i2s_probe(struct platform_device *pdev) if (pdata) { dev->capability = pdata->cap; clk_id = NULL; + dev->quirks = pdata->quirks; + if (dev->quirks & DW_I2S_QUIRK_COMP_REG_OFFSET) { + dev->i2s_reg_comp1 = pdata->i2s_reg_comp1; + dev->i2s_reg_comp2 = pdata->i2s_reg_comp2; + } else { + dev->i2s_reg_comp1 = I2S_COMP_PARAM_1; + dev->i2s_reg_comp2 = I2S_COMP_PARAM_2; + } ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); } else { clk_id = "i2sclk"; @@ -649,7 +694,7 @@ static int dw_i2s_probe(struct platform_device *pdev) goto err_clk_disable; } } - + pm_runtime_enable(&pdev->dev); return 0; err_clk_disable: @@ -665,6 +710,7 @@ static int dw_i2s_remove(struct platform_device *pdev) if (dev->capability & DW_I2S_MASTER) clk_disable_unprepare(dev->clk); + pm_runtime_disable(&pdev->dev); return 0; } @@ -677,12 +723,17 @@ static const struct of_device_id dw_i2s_of_match[] = { MODULE_DEVICE_TABLE(of, dw_i2s_of_match); #endif +static const struct dev_pm_ops dwc_pm_ops = { + SET_RUNTIME_PM_OPS(dw_i2s_runtime_suspend, dw_i2s_runtime_resume, NULL) +}; + static struct platform_driver dw_i2s_driver = { .probe = dw_i2s_probe, .remove = dw_i2s_remove, .driver = { .name = "designware-i2s", .of_match_table = of_match_ptr(dw_i2s_of_match), + .pm = &dwc_pm_ops, }, }; diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 19c302b0d763..14dfdee05fd5 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -283,6 +283,8 @@ config SND_SOC_IMX_MC13783 config SND_SOC_FSL_ASOC_CARD tristate "Generic ASoC Sound Card with ASRC support" depends on OF && I2C + # enforce SND_SOC_FSL_ASOC_CARD=m if SND_AC97_CODEC=m: + depends on SND_AC97_CODEC || SND_AC97_CODEC=n select SND_SOC_IMX_AUDMUX select SND_SOC_IMX_PCM_DMA select SND_SOC_FSL_ESAI diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 1b05d1c5d9fd..562b3bd22d9a 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -107,6 +107,13 @@ static const struct snd_soc_dapm_route audio_map[] = { {"CPU-Capture", NULL, "Capture"}, }; +static const struct snd_soc_dapm_route audio_map_ac97[] = { + {"AC97 Playback", NULL, "ASRC-Playback"}, + {"Playback", NULL, "AC97 Playback"}, + {"ASRC-Capture", NULL, "AC97 Capture"}, + {"AC97 Capture", NULL, "Capture"}, +}; + /* Add all possible widgets into here without being redundant */ static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = { SND_SOC_DAPM_LINE("Line Out Jack", NULL), @@ -222,12 +229,15 @@ static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card, enum snd_soc_bias_level level) { struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *codec_dai; struct codec_priv *codec_priv = &priv->codec_priv; struct device *dev = card->dev; unsigned int pll_out; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + codec_dai = rtd->codec_dai; if (dapm->dev != codec_dai->dev) return 0; @@ -414,14 +424,16 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, static int fsl_asoc_card_late_probe(struct snd_soc_card *card) { struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + struct snd_soc_pcm_runtime *rtd = list_first_entry( + &card->rtd_list, struct snd_soc_pcm_runtime, list); + struct snd_soc_dai *codec_dai = rtd->codec_dai; struct codec_priv *codec_priv = &priv->codec_priv; struct device *dev = card->dev; int ret; if (fsl_asoc_card_is_ac97(priv)) { #if IS_ENABLED(CONFIG_SND_AC97_CODEC) - struct snd_soc_codec *codec = card->rtd[0].codec; + struct snd_soc_codec *codec = rtd->codec; struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); /* @@ -574,7 +586,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->card.dev = &pdev->dev; priv->card.name = priv->name; priv->card.dai_link = priv->dai_link; - priv->card.dapm_routes = audio_map; + priv->card.dapm_routes = fsl_asoc_card_is_ac97(priv) ? + audio_map_ac97 : audio_map; priv->card.late_probe = fsl_asoc_card_late_probe; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map); priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets; diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 9f087d4f73ed..dd1263b95dc7 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -31,21 +31,21 @@ dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__) /* Sample rates are aligned with that defined in pcm.h file */ -static const u8 process_option[][8][2] = { - /* 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */ - {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */ - {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */ - {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */ - {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */ - {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */ - {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */ - {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */ - {{0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */ - {{1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */ - {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */ - {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */ - {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */ - {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */ +static const u8 process_option[][12][2] = { + /* 8kHz 11.025kHz 16kHz 22.05kHz 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */ + {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */ + {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */ + {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */ + {{1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */ + {{1, 2}, {1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */ + {{1, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */ + {{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */ + {{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */ + {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */ + {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */ + {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */ + {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */ + {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */ }; /* Corresponding to process_option */ @@ -55,7 +55,7 @@ static int supported_input_rate[] = { }; static int supported_asrc_rate[] = { - 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000, + 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000, }; /** @@ -286,6 +286,13 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) return -EINVAL; } + if ((outrate > 8000 && outrate < 30000) && + (outrate/inrate > 24 || inrate/outrate > 8)) { + pair_err("exceed supported ratio range [1/24, 8] for \ + inrate/outrate: %d/%d\n", inrate, outrate); + return -EINVAL; + } + /* Validate input and output clock sources */ clk_index[IN] = clk_map[IN][config->inclk]; clk_index[OUT] = clk_map[OUT][config->outclk]; @@ -447,7 +454,7 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai); - int width = snd_pcm_format_width(params_format(params)); + int width = params_width(params); struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_asrc_pair *pair = runtime->private_data; unsigned int channels = params_channels(params); @@ -859,6 +866,10 @@ static int fsl_asrc_probe(struct platform_device *pdev) return PTR_ERR(asrc_priv->ipg_clk); } + asrc_priv->spba_clk = devm_clk_get(&pdev->dev, "spba"); + if (IS_ERR(asrc_priv->spba_clk)) + dev_warn(&pdev->dev, "failed to get spba clock\n"); + for (i = 0; i < ASRC_CLK_MAX_NUM; i++) { sprintf(tmp, "asrck_%x", i); asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp); @@ -939,6 +950,11 @@ static int fsl_asrc_runtime_resume(struct device *dev) ret = clk_prepare_enable(asrc_priv->ipg_clk); if (ret) goto disable_mem_clk; + if (!IS_ERR(asrc_priv->spba_clk)) { + ret = clk_prepare_enable(asrc_priv->spba_clk); + if (ret) + goto disable_ipg_clk; + } for (i = 0; i < ASRC_CLK_MAX_NUM; i++) { ret = clk_prepare_enable(asrc_priv->asrck_clk[i]); if (ret) @@ -950,6 +966,9 @@ static int fsl_asrc_runtime_resume(struct device *dev) disable_asrck_clk: for (i--; i >= 0; i--) clk_disable_unprepare(asrc_priv->asrck_clk[i]); + if (!IS_ERR(asrc_priv->spba_clk)) + clk_disable_unprepare(asrc_priv->spba_clk); +disable_ipg_clk: clk_disable_unprepare(asrc_priv->ipg_clk); disable_mem_clk: clk_disable_unprepare(asrc_priv->mem_clk); @@ -963,6 +982,8 @@ static int fsl_asrc_runtime_suspend(struct device *dev) for (i = 0; i < ASRC_CLK_MAX_NUM; i++) clk_disable_unprepare(asrc_priv->asrck_clk[i]); + if (!IS_ERR(asrc_priv->spba_clk)) + clk_disable_unprepare(asrc_priv->spba_clk); clk_disable_unprepare(asrc_priv->ipg_clk); clk_disable_unprepare(asrc_priv->mem_clk); diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h index 4aed63c4b431..68802cdc3f28 100644 --- a/sound/soc/fsl/fsl_asrc.h +++ b/sound/soc/fsl/fsl_asrc.h @@ -426,6 +426,7 @@ struct fsl_asrc_pair { * @paddr: physical address to the base address of registers * @mem_clk: clock source to access register * @ipg_clk: clock source to drive peripheral + * @spba_clk: SPBA clock (optional, depending on SoC design) * @asrck_clk: clock sources to driver ASRC internal logic * @lock: spin lock for resource protection * @pair: pair pointers @@ -442,6 +443,7 @@ struct fsl_asrc { unsigned long paddr; struct clk *mem_clk; struct clk *ipg_clk; + struct clk *spba_clk; struct clk *asrck_clk[ASRC_CLK_MAX_NUM]; spinlock_t lock; diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 59f234e51971..26a90e12ede4 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -35,6 +35,7 @@ * @coreclk: clock source to access register * @extalclk: esai clock source to derive HCK, SCK and FS * @fsysclk: system clock source to derive HCK, SCK and FS + * @spbaclk: SPBA clock (optional, depending on SoC design) * @fifo_depth: depth of tx/rx FIFO * @slot_width: width of each DAI slot * @slots: number of slots @@ -54,6 +55,7 @@ struct fsl_esai { struct clk *coreclk; struct clk *extalclk; struct clk *fsysclk; + struct clk *spbaclk; u32 fifo_depth; u32 slot_width; u32 slots; @@ -469,6 +471,11 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream, ret = clk_prepare_enable(esai_priv->coreclk); if (ret) return ret; + if (!IS_ERR(esai_priv->spbaclk)) { + ret = clk_prepare_enable(esai_priv->spbaclk); + if (ret) + goto err_spbaclk; + } if (!IS_ERR(esai_priv->extalclk)) { ret = clk_prepare_enable(esai_priv->extalclk); if (ret) @@ -499,6 +506,9 @@ err_fsysclk: if (!IS_ERR(esai_priv->extalclk)) clk_disable_unprepare(esai_priv->extalclk); err_extalck: + if (!IS_ERR(esai_priv->spbaclk)) + clk_disable_unprepare(esai_priv->spbaclk); +err_spbaclk: clk_disable_unprepare(esai_priv->coreclk); return ret; @@ -510,7 +520,7 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, { struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - u32 width = snd_pcm_format_width(params_format(params)); + u32 width = params_width(params); u32 channels = params_channels(params); u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); u32 slot_width = width; @@ -564,6 +574,8 @@ static void fsl_esai_shutdown(struct snd_pcm_substream *substream, clk_disable_unprepare(esai_priv->fsysclk); if (!IS_ERR(esai_priv->extalclk)) clk_disable_unprepare(esai_priv->extalclk); + if (!IS_ERR(esai_priv->spbaclk)) + clk_disable_unprepare(esai_priv->spbaclk); clk_disable_unprepare(esai_priv->coreclk); } @@ -653,21 +665,28 @@ static const struct snd_soc_component_driver fsl_esai_component = { }; static const struct reg_default fsl_esai_reg_defaults[] = { - {0x8, 0x00000000}, - {0x10, 0x00000000}, - {0x18, 0x00000000}, - {0x98, 0x00000000}, - {0xd0, 0x00000000}, - {0xd4, 0x00000000}, - {0xd8, 0x00000000}, - {0xdc, 0x00000000}, - {0xe0, 0x00000000}, - {0xe4, 0x0000ffff}, - {0xe8, 0x0000ffff}, - {0xec, 0x0000ffff}, - {0xf0, 0x0000ffff}, - {0xf8, 0x00000000}, - {0xfc, 0x00000000}, + {REG_ESAI_ETDR, 0x00000000}, + {REG_ESAI_ECR, 0x00000000}, + {REG_ESAI_TFCR, 0x00000000}, + {REG_ESAI_RFCR, 0x00000000}, + {REG_ESAI_TX0, 0x00000000}, + {REG_ESAI_TX1, 0x00000000}, + {REG_ESAI_TX2, 0x00000000}, + {REG_ESAI_TX3, 0x00000000}, + {REG_ESAI_TX4, 0x00000000}, + {REG_ESAI_TX5, 0x00000000}, + {REG_ESAI_TSR, 0x00000000}, + {REG_ESAI_SAICR, 0x00000000}, + {REG_ESAI_TCR, 0x00000000}, + {REG_ESAI_TCCR, 0x00000000}, + {REG_ESAI_RCR, 0x00000000}, + {REG_ESAI_RCCR, 0x00000000}, + {REG_ESAI_TSMA, 0x0000ffff}, + {REG_ESAI_TSMB, 0x0000ffff}, + {REG_ESAI_RSMA, 0x0000ffff}, + {REG_ESAI_RSMB, 0x0000ffff}, + {REG_ESAI_PRRC, 0x00000000}, + {REG_ESAI_PCRC, 0x00000000}, }; static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg) @@ -705,17 +724,10 @@ static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg) static bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { - case REG_ESAI_ETDR: case REG_ESAI_ERDR: case REG_ESAI_ESR: case REG_ESAI_TFSR: case REG_ESAI_RFSR: - case REG_ESAI_TX0: - case REG_ESAI_TX1: - case REG_ESAI_TX2: - case REG_ESAI_TX3: - case REG_ESAI_TX4: - case REG_ESAI_TX5: case REG_ESAI_RX0: case REG_ESAI_RX1: case REG_ESAI_RX2: @@ -819,6 +831,11 @@ static int fsl_esai_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "failed to get fsys clock: %ld\n", PTR_ERR(esai_priv->fsysclk)); + esai_priv->spbaclk = devm_clk_get(&pdev->dev, "spba"); + if (IS_ERR(esai_priv->spbaclk)) + dev_warn(&pdev->dev, "failed to get spba clock: %ld\n", + PTR_ERR(esai_priv->spbaclk)); + irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index a4435f5e3be9..fef264d27fd3 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -126,6 +126,17 @@ out: return IRQ_HANDLED; } +static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, + u32 rx_mask, int slots, int slot_width) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + sai->slots = slots; + sai->slot_width = slot_width; + + return 0; +} + static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int fsl_dir) { @@ -354,13 +365,25 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) return -EINVAL; } - if ((tx && sai->synchronous[TX]) || (!tx && !sai->synchronous[RX])) { + /* + * 1) For Asynchronous mode, we must set RCR2 register for capture, and + * set TCR2 register for playback. + * 2) For Tx sync with Rx clock, we must set RCR2 register for playback + * and capture. + * 3) For Rx sync with Tx clock, we must set TCR2 register for playback + * and capture. + * 4) For Tx and Rx are both Synchronous with another SAI, we just + * ignore it. + */ + if ((sai->synchronous[TX] && !sai->synchronous[RX]) || + (!tx && !sai->synchronous[RX])) { regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_DIV_MASK, savediv - 1); - } else { + } else if ((sai->synchronous[RX] && !sai->synchronous[TX]) || + (tx && !sai->synchronous[TX])) { regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); @@ -381,13 +404,21 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; unsigned int channels = params_channels(params); - u32 word_width = snd_pcm_format_width(params_format(params)); + u32 word_width = params_width(params); u32 val_cr4 = 0, val_cr5 = 0; + u32 slots = (channels == 1) ? 2 : channels; + u32 slot_width = word_width; int ret; + if (sai->slots) + slots = sai->slots; + + if (sai->slot_width) + slot_width = sai->slot_width; + if (!sai->is_slave_mode) { ret = fsl_sai_set_bclk(cpu_dai, tx, - 2 * word_width * params_rate(params)); + slots * slot_width * params_rate(params)); if (ret) return ret; @@ -399,21 +430,49 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, sai->mclk_streams |= BIT(substream->stream); } - } if (!sai->is_dsp_mode) - val_cr4 |= FSL_SAI_CR4_SYWD(word_width); + val_cr4 |= FSL_SAI_CR4_SYWD(slot_width); - val_cr5 |= FSL_SAI_CR5_WNW(word_width); - val_cr5 |= FSL_SAI_CR5_W0W(word_width); + val_cr5 |= FSL_SAI_CR5_WNW(slot_width); + val_cr5 |= FSL_SAI_CR5_W0W(slot_width); if (sai->is_lsb_first) val_cr5 |= FSL_SAI_CR5_FBT(0); else val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); - val_cr4 |= FSL_SAI_CR4_FRSZ(channels); + val_cr4 |= FSL_SAI_CR4_FRSZ(slots); + + /* + * For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will + * generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4), + * RCR5(TCR5) and RMR(TMR) for playback(capture), or there will be sync + * error. + */ + + if (!sai->is_slave_mode) { + if (!sai->synchronous[TX] && sai->synchronous[RX] && !tx) { + regmap_update_bits(sai->regmap, FSL_SAI_TCR4, + FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, + val_cr4); + regmap_update_bits(sai->regmap, FSL_SAI_TCR5, + FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK | + FSL_SAI_CR5_FBT_MASK, val_cr5); + regmap_write(sai->regmap, FSL_SAI_TMR, + ~0UL - ((1 << channels) - 1)); + } else if (!sai->synchronous[RX] && sai->synchronous[TX] && tx) { + regmap_update_bits(sai->regmap, FSL_SAI_RCR4, + FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, + val_cr4); + regmap_update_bits(sai->regmap, FSL_SAI_RCR5, + FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK | + FSL_SAI_CR5_FBT_MASK, val_cr5); + regmap_write(sai->regmap, FSL_SAI_RMR, + ~0UL - ((1 << channels) - 1)); + } + } regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx), FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, @@ -454,7 +513,8 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, * Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx. * Tx sync with Rx clocks: Clear SYNC for Rx, set it for Tx. */ - regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, 0); + regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, + sai->synchronous[TX] ? FSL_SAI_CR2_SYNC : 0); regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC, sai->synchronous[RX] ? FSL_SAI_CR2_SYNC : 0); @@ -504,6 +564,24 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, FSL_SAI_CSR_FR, FSL_SAI_CSR_FR); regmap_update_bits(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_FR, FSL_SAI_CSR_FR); + + /* + * For sai master mode, after several open/close sai, + * there will be no frame clock, and can't recover + * anymore. Add software reset to fix this issue. + * This is a hardware bug, and will be fix in the + * next sai version. + */ + if (!sai->is_slave_mode) { + /* Software Reset for both Tx and Rx */ + regmap_write(sai->regmap, + FSL_SAI_TCSR, FSL_SAI_CSR_SR); + regmap_write(sai->regmap, + FSL_SAI_RCSR, FSL_SAI_CSR_SR); + /* Clear SR bit to finish the reset */ + regmap_write(sai->regmap, FSL_SAI_TCSR, 0); + regmap_write(sai->regmap, FSL_SAI_RCSR, 0); + } } break; default: @@ -550,6 +628,7 @@ static void fsl_sai_shutdown(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { .set_sysclk = fsl_sai_set_dai_sysclk, .set_fmt = fsl_sai_set_dai_fmt, + .set_tdm_slot = fsl_sai_set_dai_tdm_slot, .hw_params = fsl_sai_hw_params, .hw_free = fsl_sai_hw_free, .trigger = fsl_sai_trigger, @@ -608,6 +687,22 @@ static const struct snd_soc_component_driver fsl_component = { .name = "fsl-sai", }; +static struct reg_default fsl_sai_reg_defaults[] = { + {FSL_SAI_TCR1, 0}, + {FSL_SAI_TCR2, 0}, + {FSL_SAI_TCR3, 0}, + {FSL_SAI_TCR4, 0}, + {FSL_SAI_TCR5, 0}, + {FSL_SAI_TDR, 0}, + {FSL_SAI_TMR, 0}, + {FSL_SAI_RCR1, 0}, + {FSL_SAI_RCR2, 0}, + {FSL_SAI_RCR3, 0}, + {FSL_SAI_RCR4, 0}, + {FSL_SAI_RCR5, 0}, + {FSL_SAI_RMR, 0}, +}; + static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -641,13 +736,11 @@ static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg) case FSL_SAI_RCSR: case FSL_SAI_TFR: case FSL_SAI_RFR: - case FSL_SAI_TDR: case FSL_SAI_RDR: return true; default: return false; } - } static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg) @@ -680,6 +773,8 @@ static const struct regmap_config fsl_sai_regmap_config = { .val_bits = 32, .max_register = FSL_SAI_RMR, + .reg_defaults = fsl_sai_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(fsl_sai_reg_defaults), .readable_reg = fsl_sai_readable_reg, .volatile_reg = fsl_sai_volatile_reg, .writeable_reg = fsl_sai_writeable_reg, diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index b95fbc3f68eb..d9ed7be8cb34 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -143,6 +143,9 @@ struct fsl_sai { unsigned int mclk_id[2]; unsigned int mclk_streams; + unsigned int slots; + unsigned int slot_width; + struct snd_dmaengine_dai_dma_data dma_params_rx; struct snd_dmaengine_dai_dma_data dma_params_tx; }; diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 3d59bb6719f2..151849f79863 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -88,6 +88,7 @@ struct spdif_mixer_control { * @rxclk: rx clock sources for capture * @coreclk: core clock for register access via DMA * @sysclk: system clock for rx clock rate measurement + * @spbaclk: SPBA clock (optional, depending on SoC design) * @dma_params_tx: DMA parameters for transmit channel * @dma_params_rx: DMA parameters for receive channel */ @@ -106,6 +107,7 @@ struct fsl_spdif_priv { struct clk *rxclk; struct clk *coreclk; struct clk *sysclk; + struct clk *spbaclk; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; /* regcache for SRPC */ @@ -474,6 +476,14 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream, return ret; } + if (!IS_ERR(spdif_priv->spbaclk)) { + ret = clk_prepare_enable(spdif_priv->spbaclk); + if (ret) { + dev_err(&pdev->dev, "failed to enable spba clock\n"); + goto err_spbaclk; + } + } + ret = spdif_softreset(spdif_priv); if (ret) { dev_err(&pdev->dev, "failed to soft reset\n"); @@ -515,6 +525,9 @@ disable_txclk: for (i--; i >= 0; i--) clk_disable_unprepare(spdif_priv->txclk[i]); err: + if (!IS_ERR(spdif_priv->spbaclk)) + clk_disable_unprepare(spdif_priv->spbaclk); +err_spbaclk: clk_disable_unprepare(spdif_priv->coreclk); return ret; @@ -548,6 +561,8 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream, spdif_intr_status_clear(spdif_priv); regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, SCR_LOW_POWER); + if (!IS_ERR(spdif_priv->spbaclk)) + clk_disable_unprepare(spdif_priv->spbaclk); clk_disable_unprepare(spdif_priv->coreclk); } } @@ -1006,12 +1021,14 @@ static const struct snd_soc_component_driver fsl_spdif_component = { /* FSL SPDIF REGMAP */ static const struct reg_default fsl_spdif_reg_defaults[] = { - {0x0, 0x00000400}, - {0x4, 0x00000000}, - {0xc, 0x00000000}, - {0x34, 0x00000000}, - {0x38, 0x00000000}, - {0x50, 0x00020f00}, + {REG_SPDIF_SCR, 0x00000400}, + {REG_SPDIF_SRCD, 0x00000000}, + {REG_SPDIF_SIE, 0x00000000}, + {REG_SPDIF_STL, 0x00000000}, + {REG_SPDIF_STR, 0x00000000}, + {REG_SPDIF_STCSCH, 0x00000000}, + {REG_SPDIF_STCSCL, 0x00000000}, + {REG_SPDIF_STC, 0x00020f00}, }; static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg) @@ -1049,8 +1066,6 @@ static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg) case REG_SPDIF_SRCSL: case REG_SPDIF_SRU: case REG_SPDIF_SRQ: - case REG_SPDIF_STL: - case REG_SPDIF_STR: case REG_SPDIF_SRFM: return true; default: @@ -1261,6 +1276,10 @@ static int fsl_spdif_probe(struct platform_device *pdev) return PTR_ERR(spdif_priv->coreclk); } + spdif_priv->spbaclk = devm_clk_get(&pdev->dev, "spba"); + if (IS_ERR(spdif_priv->spbaclk)) + dev_warn(&pdev->dev, "no spba clock in devicetree\n"); + /* Select clock source for rx/tx clock */ spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1"); if (IS_ERR(spdif_priv->rxclk)) { diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 95d2392303eb..e3abad5f980a 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -113,17 +113,17 @@ struct fsl_ssi_rxtx_reg_val { }; static const struct reg_default fsl_ssi_reg_defaults[] = { - {0x10, 0x00000000}, - {0x18, 0x00003003}, - {0x1c, 0x00000200}, - {0x20, 0x00000200}, - {0x24, 0x00040000}, - {0x28, 0x00040000}, - {0x38, 0x00000000}, - {0x48, 0x00000000}, - {0x4c, 0x00000000}, - {0x54, 0x00000000}, - {0x58, 0x00000000}, + {CCSR_SSI_SCR, 0x00000000}, + {CCSR_SSI_SIER, 0x00003003}, + {CCSR_SSI_STCR, 0x00000200}, + {CCSR_SSI_SRCR, 0x00000200}, + {CCSR_SSI_STCCR, 0x00040000}, + {CCSR_SSI_SRCCR, 0x00040000}, + {CCSR_SSI_SACNT, 0x00000000}, + {CCSR_SSI_STMSK, 0x00000000}, + {CCSR_SSI_SRMSK, 0x00000000}, + {CCSR_SSI_SACCEN, 0x00000000}, + {CCSR_SSI_SACCDIS, 0x00000000}, }; static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg) @@ -767,8 +767,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); struct regmap *regs = ssi_private->regs; unsigned int channels = params_channels(hw_params); - unsigned int sample_size = - snd_pcm_format_width(params_format(hw_params)); + unsigned int sample_size = params_width(hw_params); u32 wl = CCSR_SSI_SxCCR_WL(sample_size); int ret; u32 scr_val; diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index 1fc01ed3279d..f3d3d1ffa84e 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -62,6 +62,8 @@ int imx_pcm_dma_init(struct platform_device *pdev, size_t size) config = devm_kzalloc(&pdev->dev, sizeof(struct snd_dmaengine_pcm_config), GFP_KERNEL); + if (!config) + return -ENOMEM; *config = imx_dmaengine_pcm_config; if (size) config->prealloc_buffer_size = size; diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 7abf6a079574..49d7513f429e 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -220,9 +220,9 @@ static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, ret = dma_mmap_writecombine(substream->pcm->card->dev, vma, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); - pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret, + pr_debug("%s: ret: %d %p %pad 0x%08x\n", __func__, ret, runtime->dma_area, - runtime->dma_addr, + &runtime->dma_addr, runtime->dma_bytes); return ret; } diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c index b38b98cae855..201a70d1027a 100644 --- a/sound/soc/fsl/imx-wm8962.c +++ b/sound/soc/fsl/imx-wm8962.c @@ -69,13 +69,16 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { - struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *codec_dai; struct imx_priv *priv = &card_priv; struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card); struct device *dev = &priv->pdev->dev; unsigned int pll_out; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + codec_dai = rtd->codec_dai; if (dapm->dev != codec_dai->dev) return 0; @@ -135,12 +138,15 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card, static int imx_wm8962_late_probe(struct snd_soc_card *card) { - struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *codec_dai; struct imx_priv *priv = &card_priv; struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card); struct device *dev = &priv->pdev->dev; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + codec_dai = rtd->codec_dai; ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK, data->clk_frequency, SND_SOC_CLOCK_IN); if (ret < 0) diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 54c33204541f..1ded8811598e 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -45,7 +45,7 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = - &priv->dai_props[rtd - rtd->card->rtd]; + &priv->dai_props[rtd->num]; int ret; ret = clk_prepare_enable(dai_props->cpu_dai.clk); @@ -64,7 +64,7 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = - &priv->dai_props[rtd - rtd->card->rtd]; + &priv->dai_props[rtd->num]; clk_disable_unprepare(dai_props->cpu_dai.clk); @@ -78,8 +78,7 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *dai_props = - &priv->dai_props[rtd - rtd->card->rtd]; + struct simple_dai_props *dai_props = &priv->dai_props[rtd->num]; unsigned int mclk, mclk_fs = 0; int ret = 0; @@ -174,10 +173,9 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dai *codec = rtd->codec_dai; struct snd_soc_dai *cpu = rtd->cpu_dai; struct simple_dai_props *dai_props; - int num, ret; + int ret; - num = rtd - rtd->card->rtd; - dai_props = &priv->dai_props[num]; + dai_props = &priv->dai_props[rtd->num]; ret = __asoc_simple_card_dai_init(codec, &dai_props->codec_dai); if (ret < 0) return ret; diff --git a/sound/soc/img/Kconfig b/sound/soc/img/Kconfig new file mode 100644 index 000000000000..857a9510ee1c --- /dev/null +++ b/sound/soc/img/Kconfig @@ -0,0 +1,52 @@ +config SND_SOC_IMG + bool "Audio support for Imagination Technologies designs" + help + Audio support for Imagination Technologies audio hardware + +config SND_SOC_IMG_I2S_IN + tristate "Imagination I2S Input Device Driver" + depends on SND_SOC_IMG + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for I2S in driver for + Imagination Technologies I2S in device. + +config SND_SOC_IMG_I2S_OUT + tristate "Imagination I2S Output Device Driver" + depends on SND_SOC_IMG + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for I2S out driver for + Imagination Technologies I2S out device. + +config SND_SOC_IMG_PARALLEL_OUT + tristate "Imagination Parallel Output Device Driver" + depends on SND_SOC_IMG + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for parallel out driver for + Imagination Technologies parallel out device. + +config SND_SOC_IMG_SPDIF_IN + tristate "Imagination SPDIF Input Device Driver" + depends on SND_SOC_IMG + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for SPDIF input driver for + Imagination Technologies SPDIF input device. + +config SND_SOC_IMG_SPDIF_OUT + tristate "Imagination SPDIF Output Device Driver" + depends on SND_SOC_IMG + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for SPDIF out driver for + Imagination Technologies SPDIF out device. + + +config SND_SOC_IMG_PISTACHIO_INTERNAL_DAC + tristate "Support for Pistachio SoC Internal DAC Driver" + depends on SND_SOC_IMG + help + Say Y or M if you want to add support for Pistachio internal DAC + driver for Imagination Technologies Pistachio internal DAC device. diff --git a/sound/soc/img/Makefile b/sound/soc/img/Makefile new file mode 100644 index 000000000000..0508c1ced636 --- /dev/null +++ b/sound/soc/img/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_SND_SOC_IMG_I2S_IN) += img-i2s-in.o +obj-$(CONFIG_SND_SOC_IMG_I2S_OUT) += img-i2s-out.o +obj-$(CONFIG_SND_SOC_IMG_PARALLEL_OUT) += img-parallel-out.o +obj-$(CONFIG_SND_SOC_IMG_SPDIF_IN) += img-spdif-in.o +obj-$(CONFIG_SND_SOC_IMG_SPDIF_OUT) += img-spdif-out.o + +obj-$(CONFIG_SND_SOC_IMG_PISTACHIO_INTERNAL_DAC) += pistachio-internal-dac.o diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c new file mode 100644 index 000000000000..0389203f8560 --- /dev/null +++ b/sound/soc/img/img-i2s-in.c @@ -0,0 +1,516 @@ +/* + * IMG I2S input controller driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley <Damien.Horsley@imgtec.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +#include <sound/core.h> +#include <sound/dmaengine_pcm.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#define IMG_I2S_IN_RX_FIFO 0x0 + +#define IMG_I2S_IN_CTL 0x4 +#define IMG_I2S_IN_CTL_ACTIVE_CHAN_MASK 0xfffffffc +#define IMG_I2S_IN_CTL_ACTIVE_CH_SHIFT 2 +#define IMG_I2S_IN_CTL_16PACK_MASK BIT(1) +#define IMG_I2S_IN_CTL_ME_MASK BIT(0) + +#define IMG_I2S_IN_CH_CTL 0x4 +#define IMG_I2S_IN_CH_CTL_CCDEL_MASK 0x38000 +#define IMG_I2S_IN_CH_CTL_CCDEL_SHIFT 15 +#define IMG_I2S_IN_CH_CTL_FEN_MASK BIT(14) +#define IMG_I2S_IN_CH_CTL_FMODE_MASK BIT(13) +#define IMG_I2S_IN_CH_CTL_16PACK_MASK BIT(12) +#define IMG_I2S_IN_CH_CTL_JUST_MASK BIT(10) +#define IMG_I2S_IN_CH_CTL_PACKH_MASK BIT(9) +#define IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK BIT(8) +#define IMG_I2S_IN_CH_CTL_BLKP_MASK BIT(7) +#define IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK BIT(6) +#define IMG_I2S_IN_CH_CTL_LRD_MASK BIT(3) +#define IMG_I2S_IN_CH_CTL_FW_MASK BIT(2) +#define IMG_I2S_IN_CH_CTL_SW_MASK BIT(1) +#define IMG_I2S_IN_CH_CTL_ME_MASK BIT(0) + +#define IMG_I2S_IN_CH_STRIDE 0x20 + +struct img_i2s_in { + void __iomem *base; + struct clk *clk_sys; + struct snd_dmaengine_dai_dma_data dma_data; + struct device *dev; + unsigned int max_i2s_chan; + void __iomem *channel_base; + unsigned int active_channels; + struct snd_soc_dai_driver dai_driver; +}; + +static inline void img_i2s_in_writel(struct img_i2s_in *i2s, u32 val, u32 reg) +{ + writel(val, i2s->base + reg); +} + +static inline u32 img_i2s_in_readl(struct img_i2s_in *i2s, u32 reg) +{ + return readl(i2s->base + reg); +} + +static inline void img_i2s_in_ch_writel(struct img_i2s_in *i2s, u32 chan, + u32 val, u32 reg) +{ + writel(val, i2s->channel_base + (chan * IMG_I2S_IN_CH_STRIDE) + reg); +} + +static inline u32 img_i2s_in_ch_readl(struct img_i2s_in *i2s, u32 chan, + u32 reg) +{ + return readl(i2s->channel_base + (chan * IMG_I2S_IN_CH_STRIDE) + reg); +} + +static inline void img_i2s_in_ch_disable(struct img_i2s_in *i2s, u32 chan) +{ + u32 reg; + + reg = img_i2s_in_ch_readl(i2s, chan, IMG_I2S_IN_CH_CTL); + reg &= ~IMG_I2S_IN_CH_CTL_ME_MASK; + img_i2s_in_ch_writel(i2s, chan, reg, IMG_I2S_IN_CH_CTL); +} + +static inline void img_i2s_in_ch_enable(struct img_i2s_in *i2s, u32 chan) +{ + u32 reg; + + reg = img_i2s_in_ch_readl(i2s, chan, IMG_I2S_IN_CH_CTL); + reg |= IMG_I2S_IN_CH_CTL_ME_MASK; + img_i2s_in_ch_writel(i2s, chan, reg, IMG_I2S_IN_CH_CTL); +} + +static inline void img_i2s_in_disable(struct img_i2s_in *i2s) +{ + u32 reg; + + reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL); + reg &= ~IMG_I2S_IN_CTL_ME_MASK; + img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL); +} + +static inline void img_i2s_in_enable(struct img_i2s_in *i2s) +{ + u32 reg; + + reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL); + reg |= IMG_I2S_IN_CTL_ME_MASK; + img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL); +} + +static inline void img_i2s_in_flush(struct img_i2s_in *i2s) +{ + int i; + u32 reg; + + for (i = 0; i < i2s->active_channels; i++) { + reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL); + reg |= IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK; + img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL); + reg &= ~IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK; + img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL); + } +} + +static int img_i2s_in_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + img_i2s_in_enable(i2s); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + img_i2s_in_disable(i2s); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int img_i2s_in_check_rate(struct img_i2s_in *i2s, + unsigned int sample_rate, unsigned int frame_size, + unsigned int *bclk_filter_enable, + unsigned int *bclk_filter_value) +{ + unsigned int bclk_freq, cur_freq; + + bclk_freq = sample_rate * frame_size; + + cur_freq = clk_get_rate(i2s->clk_sys); + + if (cur_freq >= bclk_freq * 8) { + *bclk_filter_enable = 1; + *bclk_filter_value = 0; + } else if (cur_freq >= bclk_freq * 7) { + *bclk_filter_enable = 1; + *bclk_filter_value = 1; + } else if (cur_freq >= bclk_freq * 6) { + *bclk_filter_enable = 0; + *bclk_filter_value = 0; + } else { + dev_err(i2s->dev, + "Sys clock rate %u insufficient for sample rate %u\n", + cur_freq, sample_rate); + return -EINVAL; + } + + return 0; +} + +static int img_i2s_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai); + unsigned int rate, channels, i2s_channels, frame_size; + unsigned int bclk_filter_enable, bclk_filter_value; + int i, ret = 0; + u32 reg, control_mask, chan_control_mask; + u32 control_set = 0, chan_control_set = 0; + snd_pcm_format_t format; + + rate = params_rate(params); + format = params_format(params); + channels = params_channels(params); + i2s_channels = channels / 2; + + switch (format) { + case SNDRV_PCM_FORMAT_S32_LE: + frame_size = 64; + chan_control_set |= IMG_I2S_IN_CH_CTL_SW_MASK; + chan_control_set |= IMG_I2S_IN_CH_CTL_FW_MASK; + chan_control_set |= IMG_I2S_IN_CH_CTL_PACKH_MASK; + break; + case SNDRV_PCM_FORMAT_S24_LE: + frame_size = 64; + chan_control_set |= IMG_I2S_IN_CH_CTL_SW_MASK; + chan_control_set |= IMG_I2S_IN_CH_CTL_FW_MASK; + break; + case SNDRV_PCM_FORMAT_S16_LE: + frame_size = 32; + control_set |= IMG_I2S_IN_CTL_16PACK_MASK; + chan_control_set |= IMG_I2S_IN_CH_CTL_16PACK_MASK; + break; + default: + return -EINVAL; + } + + if ((channels < 2) || + (channels > (i2s->max_i2s_chan * 2)) || + (channels % 2)) + return -EINVAL; + + control_set |= ((i2s_channels - 1) << IMG_I2S_IN_CTL_ACTIVE_CH_SHIFT); + + ret = img_i2s_in_check_rate(i2s, rate, frame_size, + &bclk_filter_enable, &bclk_filter_value); + if (ret < 0) + return ret; + + if (bclk_filter_enable) + chan_control_set |= IMG_I2S_IN_CH_CTL_FEN_MASK; + + if (bclk_filter_value) + chan_control_set |= IMG_I2S_IN_CH_CTL_FMODE_MASK; + + control_mask = IMG_I2S_IN_CTL_16PACK_MASK | + IMG_I2S_IN_CTL_ACTIVE_CHAN_MASK; + + chan_control_mask = IMG_I2S_IN_CH_CTL_16PACK_MASK | + IMG_I2S_IN_CH_CTL_FEN_MASK | + IMG_I2S_IN_CH_CTL_FMODE_MASK | + IMG_I2S_IN_CH_CTL_SW_MASK | + IMG_I2S_IN_CH_CTL_FW_MASK | + IMG_I2S_IN_CH_CTL_PACKH_MASK; + + reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL); + reg = (reg & ~control_mask) | control_set; + img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL); + + for (i = 0; i < i2s->active_channels; i++) + img_i2s_in_ch_disable(i2s, i); + + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL); + reg = (reg & ~chan_control_mask) | chan_control_set; + img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL); + } + + i2s->active_channels = i2s_channels; + + img_i2s_in_flush(i2s); + + for (i = 0; i < i2s->active_channels; i++) + img_i2s_in_ch_enable(i2s, i); + + return 0; +} + +static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai); + int i; + u32 chan_control_mask, lrd_set = 0, blkp_set = 0, chan_control_set = 0; + u32 reg; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + lrd_set |= IMG_I2S_IN_CH_CTL_LRD_MASK; + break; + case SND_SOC_DAIFMT_NB_IF: + break; + case SND_SOC_DAIFMT_IB_NF: + lrd_set |= IMG_I2S_IN_CH_CTL_LRD_MASK; + blkp_set |= IMG_I2S_IN_CH_CTL_BLKP_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + blkp_set |= IMG_I2S_IN_CH_CTL_BLKP_MASK; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + chan_control_set |= IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + break; + default: + return -EINVAL; + } + + chan_control_mask = IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK; + + for (i = 0; i < i2s->active_channels; i++) + img_i2s_in_ch_disable(i2s, i); + + /* + * BLKP and LRD must be set during separate register writes + */ + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL); + reg = (reg & ~chan_control_mask) | chan_control_set; + img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL); + reg = (reg & ~IMG_I2S_IN_CH_CTL_BLKP_MASK) | blkp_set; + img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL); + reg = (reg & ~IMG_I2S_IN_CH_CTL_LRD_MASK) | lrd_set; + img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL); + } + + for (i = 0; i < i2s->active_channels; i++) + img_i2s_in_ch_enable(i2s, i); + + return 0; +} + +static const struct snd_soc_dai_ops img_i2s_in_dai_ops = { + .trigger = img_i2s_in_trigger, + .hw_params = img_i2s_in_hw_params, + .set_fmt = img_i2s_in_set_fmt +}; + +static int img_i2s_in_dai_probe(struct snd_soc_dai *dai) +{ + struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, NULL, &i2s->dma_data); + + return 0; +} + +static const struct snd_soc_component_driver img_i2s_in_component = { + .name = "img-i2s-in" +}; + +static int img_i2s_in_dma_prepare_slave_config(struct snd_pcm_substream *st, + struct snd_pcm_hw_params *params, struct dma_slave_config *sc) +{ + unsigned int i2s_channels = params_channels(params) / 2; + struct snd_soc_pcm_runtime *rtd = st->private_data; + struct snd_dmaengine_dai_dma_data *dma_data; + int ret; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st); + + ret = snd_hwparams_to_dma_slave_config(st, params, sc); + if (ret) + return ret; + + sc->src_addr = dma_data->addr; + sc->src_addr_width = dma_data->addr_width; + sc->src_maxburst = 4 * i2s_channels; + + return 0; +} + +static const struct snd_dmaengine_pcm_config img_i2s_in_dma_config = { + .prepare_slave_config = img_i2s_in_dma_prepare_slave_config +}; + +static int img_i2s_in_probe(struct platform_device *pdev) +{ + struct img_i2s_in *i2s; + struct resource *res; + void __iomem *base; + int ret, i; + struct reset_control *rst; + unsigned int max_i2s_chan_pow_2; + struct device *dev = &pdev->dev; + + i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); + if (!i2s) + return -ENOMEM; + + platform_set_drvdata(pdev, i2s); + + i2s->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + i2s->base = base; + + if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels", + &i2s->max_i2s_chan)) { + dev_err(dev, "No img,i2s-channels property\n"); + return -EINVAL; + } + + max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan); + + i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20); + + i2s->clk_sys = devm_clk_get(dev, "sys"); + if (IS_ERR(i2s->clk_sys)) { + if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER) + dev_err(dev, "Failed to acquire clock 'sys'\n"); + return PTR_ERR(i2s->clk_sys); + } + + ret = clk_prepare_enable(i2s->clk_sys); + if (ret) + return ret; + + i2s->active_channels = 1; + i2s->dma_data.addr = res->start + IMG_I2S_IN_RX_FIFO; + i2s->dma_data.addr_width = 4; + + i2s->dai_driver.probe = img_i2s_in_dai_probe; + i2s->dai_driver.capture.channels_min = 2; + i2s->dai_driver.capture.channels_max = i2s->max_i2s_chan * 2; + i2s->dai_driver.capture.rates = SNDRV_PCM_RATE_8000_192000; + i2s->dai_driver.capture.formats = SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE; + i2s->dai_driver.ops = &img_i2s_in_dai_ops; + + rst = devm_reset_control_get(dev, "rst"); + if (IS_ERR(rst)) { + if (PTR_ERR(rst) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_clk_disable; + } + + dev_dbg(dev, "No top level reset found\n"); + + img_i2s_in_disable(i2s); + + for (i = 0; i < i2s->max_i2s_chan; i++) + img_i2s_in_ch_disable(i2s, i); + } else { + reset_control_assert(rst); + reset_control_deassert(rst); + } + + img_i2s_in_writel(i2s, 0, IMG_I2S_IN_CTL); + + for (i = 0; i < i2s->max_i2s_chan; i++) + img_i2s_in_ch_writel(i2s, i, + (4 << IMG_I2S_IN_CH_CTL_CCDEL_SHIFT) | + IMG_I2S_IN_CH_CTL_JUST_MASK | + IMG_I2S_IN_CH_CTL_FW_MASK, IMG_I2S_IN_CH_CTL); + + ret = devm_snd_soc_register_component(dev, &img_i2s_in_component, + &i2s->dai_driver, 1); + if (ret) + goto err_clk_disable; + + ret = devm_snd_dmaengine_pcm_register(dev, &img_i2s_in_dma_config, 0); + if (ret) + goto err_clk_disable; + + return 0; + +err_clk_disable: + clk_disable_unprepare(i2s->clk_sys); + + return ret; +} + +static int img_i2s_in_dev_remove(struct platform_device *pdev) +{ + struct img_i2s_in *i2s = platform_get_drvdata(pdev); + + clk_disable_unprepare(i2s->clk_sys); + + return 0; +} + +static const struct of_device_id img_i2s_in_of_match[] = { + { .compatible = "img,i2s-in" }, + {} +}; +MODULE_DEVICE_TABLE(of, img_i2s_in_of_match); + +static struct platform_driver img_i2s_in_driver = { + .driver = { + .name = "img-i2s-in", + .of_match_table = img_i2s_in_of_match + }, + .probe = img_i2s_in_probe, + .remove = img_i2s_in_dev_remove +}; +module_platform_driver(img_i2s_in_driver); + +MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); +MODULE_DESCRIPTION("IMG I2S Input Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/img/img-i2s-out.c b/sound/soc/img/img-i2s-out.c new file mode 100644 index 000000000000..5f997135a8ae --- /dev/null +++ b/sound/soc/img/img-i2s-out.c @@ -0,0 +1,565 @@ +/* + * IMG I2S output controller driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley <Damien.Horsley@imgtec.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> + +#include <sound/core.h> +#include <sound/dmaengine_pcm.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#define IMG_I2S_OUT_TX_FIFO 0x0 + +#define IMG_I2S_OUT_CTL 0x4 +#define IMG_I2S_OUT_CTL_DATA_EN_MASK BIT(24) +#define IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK 0xffe000 +#define IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT 13 +#define IMG_I2S_OUT_CTL_FRM_SIZE_MASK BIT(8) +#define IMG_I2S_OUT_CTL_MASTER_MASK BIT(6) +#define IMG_I2S_OUT_CTL_CLK_MASK BIT(5) +#define IMG_I2S_OUT_CTL_CLK_EN_MASK BIT(4) +#define IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK BIT(3) +#define IMG_I2S_OUT_CTL_BCLK_POL_MASK BIT(2) +#define IMG_I2S_OUT_CTL_ME_MASK BIT(0) + +#define IMG_I2S_OUT_CH_CTL 0x4 +#define IMG_I2S_OUT_CHAN_CTL_CH_MASK BIT(11) +#define IMG_I2S_OUT_CHAN_CTL_LT_MASK BIT(10) +#define IMG_I2S_OUT_CHAN_CTL_FMT_MASK 0xf0 +#define IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT 4 +#define IMG_I2S_OUT_CHAN_CTL_JUST_MASK BIT(3) +#define IMG_I2S_OUT_CHAN_CTL_CLKT_MASK BIT(1) +#define IMG_I2S_OUT_CHAN_CTL_ME_MASK BIT(0) + +#define IMG_I2S_OUT_CH_STRIDE 0x20 + +struct img_i2s_out { + void __iomem *base; + struct clk *clk_sys; + struct clk *clk_ref; + struct snd_dmaengine_dai_dma_data dma_data; + struct device *dev; + unsigned int max_i2s_chan; + void __iomem *channel_base; + bool force_clk_active; + unsigned int active_channels; + struct reset_control *rst; + struct snd_soc_dai_driver dai_driver; +}; + +static int img_i2s_out_suspend(struct device *dev) +{ + struct img_i2s_out *i2s = dev_get_drvdata(dev); + + if (!i2s->force_clk_active) + clk_disable_unprepare(i2s->clk_ref); + + return 0; +} + +static int img_i2s_out_resume(struct device *dev) +{ + struct img_i2s_out *i2s = dev_get_drvdata(dev); + int ret; + + if (!i2s->force_clk_active) { + ret = clk_prepare_enable(i2s->clk_ref); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + } + + return 0; +} + +static inline void img_i2s_out_writel(struct img_i2s_out *i2s, u32 val, + u32 reg) +{ + writel(val, i2s->base + reg); +} + +static inline u32 img_i2s_out_readl(struct img_i2s_out *i2s, u32 reg) +{ + return readl(i2s->base + reg); +} + +static inline void img_i2s_out_ch_writel(struct img_i2s_out *i2s, + u32 chan, u32 val, u32 reg) +{ + writel(val, i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg); +} + +static inline u32 img_i2s_out_ch_readl(struct img_i2s_out *i2s, u32 chan, + u32 reg) +{ + return readl(i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg); +} + +static inline void img_i2s_out_ch_disable(struct img_i2s_out *i2s, u32 chan) +{ + u32 reg; + + reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL); + reg &= ~IMG_I2S_OUT_CHAN_CTL_ME_MASK; + img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL); +} + +static inline void img_i2s_out_ch_enable(struct img_i2s_out *i2s, u32 chan) +{ + u32 reg; + + reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL); + reg |= IMG_I2S_OUT_CHAN_CTL_ME_MASK; + img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL); +} + +static inline void img_i2s_out_disable(struct img_i2s_out *i2s) +{ + u32 reg; + + reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); + reg &= ~IMG_I2S_OUT_CTL_ME_MASK; + img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); +} + +static inline void img_i2s_out_enable(struct img_i2s_out *i2s) +{ + u32 reg; + + reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); + reg |= IMG_I2S_OUT_CTL_ME_MASK; + img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); +} + +static void img_i2s_out_reset(struct img_i2s_out *i2s) +{ + int i; + u32 core_ctl, chan_ctl; + + core_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL) & + ~IMG_I2S_OUT_CTL_ME_MASK & + ~IMG_I2S_OUT_CTL_DATA_EN_MASK; + + if (!i2s->force_clk_active) + core_ctl &= ~IMG_I2S_OUT_CTL_CLK_EN_MASK; + + chan_ctl = img_i2s_out_ch_readl(i2s, 0, IMG_I2S_OUT_CH_CTL) & + ~IMG_I2S_OUT_CHAN_CTL_ME_MASK; + + reset_control_assert(i2s->rst); + reset_control_deassert(i2s->rst); + + for (i = 0; i < i2s->max_i2s_chan; i++) + img_i2s_out_ch_writel(i2s, i, chan_ctl, IMG_I2S_OUT_CH_CTL); + + for (i = 0; i < i2s->active_channels; i++) + img_i2s_out_ch_enable(i2s, i); + + img_i2s_out_writel(i2s, core_ctl, IMG_I2S_OUT_CTL); + img_i2s_out_enable(i2s); +} + +static int img_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); + u32 reg; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); + if (!i2s->force_clk_active) + reg |= IMG_I2S_OUT_CTL_CLK_EN_MASK; + reg |= IMG_I2S_OUT_CTL_DATA_EN_MASK; + img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + img_i2s_out_reset(i2s); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int img_i2s_out_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); + unsigned int channels, i2s_channels; + long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate; + int i; + u32 reg, control_mask, control_set = 0; + snd_pcm_format_t format; + + rate = params_rate(params); + format = params_format(params); + channels = params_channels(params); + i2s_channels = channels / 2; + + if (format != SNDRV_PCM_FORMAT_S32_LE) + return -EINVAL; + + if ((channels < 2) || + (channels > (i2s->max_i2s_chan * 2)) || + (channels % 2)) + return -EINVAL; + + pre_div_a = clk_round_rate(i2s->clk_ref, rate * 256); + if (pre_div_a < 0) + return pre_div_a; + pre_div_b = clk_round_rate(i2s->clk_ref, rate * 384); + if (pre_div_b < 0) + return pre_div_b; + + diff_a = abs((pre_div_a / 256) - rate); + diff_b = abs((pre_div_b / 384) - rate); + + /* If diffs are equal, use lower clock rate */ + if (diff_a > diff_b) + clk_set_rate(i2s->clk_ref, pre_div_b); + else + clk_set_rate(i2s->clk_ref, pre_div_a); + + /* + * Another driver (eg alsa machine driver) may have rejected the above + * change. Get the current rate and set the register bit according to + * the new minimum diff + */ + clk_rate = clk_get_rate(i2s->clk_ref); + + diff_a = abs((clk_rate / 256) - rate); + diff_b = abs((clk_rate / 384) - rate); + + if (diff_a > diff_b) + control_set |= IMG_I2S_OUT_CTL_CLK_MASK; + + control_set |= ((i2s_channels - 1) << + IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT) & + IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK; + + control_mask = IMG_I2S_OUT_CTL_CLK_MASK | + IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK; + + img_i2s_out_disable(i2s); + + reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); + reg = (reg & ~control_mask) | control_set; + img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); + + for (i = 0; i < i2s_channels; i++) + img_i2s_out_ch_enable(i2s, i); + + for (; i < i2s->max_i2s_chan; i++) + img_i2s_out_ch_disable(i2s, i); + + img_i2s_out_enable(i2s); + + i2s->active_channels = i2s_channels; + + return 0; +} + +static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); + int i; + bool force_clk_active; + u32 chan_control_mask, control_mask, chan_control_set = 0; + u32 reg, control_set = 0; + + force_clk_active = ((fmt & SND_SOC_DAIFMT_CLOCK_MASK) == + SND_SOC_DAIFMT_CONT); + + if (force_clk_active) + control_set |= IMG_I2S_OUT_CTL_CLK_EN_MASK; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + break; + case SND_SOC_DAIFMT_CBS_CFS: + control_set |= IMG_I2S_OUT_CTL_MASTER_MASK; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK; + break; + case SND_SOC_DAIFMT_NB_IF: + control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK; + control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + chan_control_set |= IMG_I2S_OUT_CHAN_CTL_CLKT_MASK; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + control_mask = IMG_I2S_OUT_CTL_CLK_EN_MASK | + IMG_I2S_OUT_CTL_MASTER_MASK | + IMG_I2S_OUT_CTL_BCLK_POL_MASK | + IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK; + + chan_control_mask = IMG_I2S_OUT_CHAN_CTL_CLKT_MASK; + + img_i2s_out_disable(i2s); + + reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); + reg = (reg & ~control_mask) | control_set; + img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); + + for (i = 0; i < i2s->active_channels; i++) + img_i2s_out_ch_disable(i2s, i); + + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL); + reg = (reg & ~chan_control_mask) | chan_control_set; + img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); + } + + for (i = 0; i < i2s->active_channels; i++) + img_i2s_out_ch_enable(i2s, i); + + img_i2s_out_enable(i2s); + + i2s->force_clk_active = force_clk_active; + + return 0; +} + +static const struct snd_soc_dai_ops img_i2s_out_dai_ops = { + .trigger = img_i2s_out_trigger, + .hw_params = img_i2s_out_hw_params, + .set_fmt = img_i2s_out_set_fmt +}; + +static int img_i2s_out_dai_probe(struct snd_soc_dai *dai) +{ + struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &i2s->dma_data, NULL); + + return 0; +} + +static const struct snd_soc_component_driver img_i2s_out_component = { + .name = "img-i2s-out" +}; + +static int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream *st, + struct snd_pcm_hw_params *params, struct dma_slave_config *sc) +{ + unsigned int i2s_channels = params_channels(params) / 2; + struct snd_soc_pcm_runtime *rtd = st->private_data; + struct snd_dmaengine_dai_dma_data *dma_data; + int ret; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st); + + ret = snd_hwparams_to_dma_slave_config(st, params, sc); + if (ret) + return ret; + + sc->dst_addr = dma_data->addr; + sc->dst_addr_width = dma_data->addr_width; + sc->dst_maxburst = 4 * i2s_channels; + + return 0; +} + +static const struct snd_dmaengine_pcm_config img_i2s_out_dma_config = { + .prepare_slave_config = img_i2s_out_dma_prepare_slave_config +}; + +static int img_i2s_out_probe(struct platform_device *pdev) +{ + struct img_i2s_out *i2s; + struct resource *res; + void __iomem *base; + int i, ret; + unsigned int max_i2s_chan_pow_2; + u32 reg; + struct device *dev = &pdev->dev; + + i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); + if (!i2s) + return -ENOMEM; + + platform_set_drvdata(pdev, i2s); + + i2s->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + i2s->base = base; + + if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels", + &i2s->max_i2s_chan)) { + dev_err(&pdev->dev, "No img,i2s-channels property\n"); + return -EINVAL; + } + + max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan); + + i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20); + + i2s->rst = devm_reset_control_get(&pdev->dev, "rst"); + if (IS_ERR(i2s->rst)) { + if (PTR_ERR(i2s->rst) != -EPROBE_DEFER) + dev_err(&pdev->dev, "No top level reset found\n"); + return PTR_ERR(i2s->rst); + } + + i2s->clk_sys = devm_clk_get(&pdev->dev, "sys"); + if (IS_ERR(i2s->clk_sys)) { + if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER) + dev_err(dev, "Failed to acquire clock 'sys'\n"); + return PTR_ERR(i2s->clk_sys); + } + + i2s->clk_ref = devm_clk_get(&pdev->dev, "ref"); + if (IS_ERR(i2s->clk_ref)) { + if (PTR_ERR(i2s->clk_ref) != -EPROBE_DEFER) + dev_err(dev, "Failed to acquire clock 'ref'\n"); + return PTR_ERR(i2s->clk_ref); + } + + ret = clk_prepare_enable(i2s->clk_sys); + if (ret) + return ret; + + reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK; + img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); + + reg = IMG_I2S_OUT_CHAN_CTL_JUST_MASK | + IMG_I2S_OUT_CHAN_CTL_LT_MASK | + IMG_I2S_OUT_CHAN_CTL_CH_MASK | + (8 << IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT); + + for (i = 0; i < i2s->max_i2s_chan; i++) + img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); + + img_i2s_out_reset(i2s); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_i2s_out_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + i2s->active_channels = 1; + i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO; + i2s->dma_data.addr_width = 4; + i2s->dma_data.maxburst = 4; + + i2s->dai_driver.probe = img_i2s_out_dai_probe; + i2s->dai_driver.playback.channels_min = 2; + i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2; + i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000; + i2s->dai_driver.playback.formats = SNDRV_PCM_FMTBIT_S32_LE; + i2s->dai_driver.ops = &img_i2s_out_dai_ops; + + ret = devm_snd_soc_register_component(&pdev->dev, + &img_i2s_out_component, &i2s->dai_driver, 1); + if (ret) + goto err_suspend; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, + &img_i2s_out_dma_config, 0); + if (ret) + goto err_suspend; + + return 0; + +err_suspend: + if (!pm_runtime_status_suspended(&pdev->dev)) + img_i2s_out_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); + clk_disable_unprepare(i2s->clk_sys); + + return ret; +} + +static int img_i2s_out_dev_remove(struct platform_device *pdev) +{ + struct img_i2s_out *i2s = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + img_i2s_out_suspend(&pdev->dev); + + clk_disable_unprepare(i2s->clk_sys); + + return 0; +} + +static const struct of_device_id img_i2s_out_of_match[] = { + { .compatible = "img,i2s-out" }, + {} +}; +MODULE_DEVICE_TABLE(of, img_i2s_out_of_match); + +static const struct dev_pm_ops img_i2s_out_pm_ops = { + SET_RUNTIME_PM_OPS(img_i2s_out_suspend, + img_i2s_out_resume, NULL) +}; + +static struct platform_driver img_i2s_out_driver = { + .driver = { + .name = "img-i2s-out", + .of_match_table = img_i2s_out_of_match, + .pm = &img_i2s_out_pm_ops + }, + .probe = img_i2s_out_probe, + .remove = img_i2s_out_dev_remove +}; +module_platform_driver(img_i2s_out_driver); + +MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); +MODULE_DESCRIPTION("IMG I2S Output Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c new file mode 100644 index 000000000000..c1610a054d65 --- /dev/null +++ b/sound/soc/img/img-parallel-out.c @@ -0,0 +1,327 @@ +/* + * IMG parallel output controller driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley <Damien.Horsley@imgtec.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> + +#include <sound/core.h> +#include <sound/dmaengine_pcm.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#define IMG_PRL_OUT_TX_FIFO 0 + +#define IMG_PRL_OUT_CTL 0x4 +#define IMG_PRL_OUT_CTL_CH_MASK BIT(4) +#define IMG_PRL_OUT_CTL_PACKH_MASK BIT(3) +#define IMG_PRL_OUT_CTL_EDGE_MASK BIT(2) +#define IMG_PRL_OUT_CTL_ME_MASK BIT(1) +#define IMG_PRL_OUT_CTL_SRST_MASK BIT(0) + +struct img_prl_out { + void __iomem *base; + struct clk *clk_sys; + struct clk *clk_ref; + struct snd_dmaengine_dai_dma_data dma_data; + struct device *dev; + struct reset_control *rst; +}; + +static int img_prl_out_suspend(struct device *dev) +{ + struct img_prl_out *prl = dev_get_drvdata(dev); + + clk_disable_unprepare(prl->clk_ref); + + return 0; +} + +static int img_prl_out_resume(struct device *dev) +{ + struct img_prl_out *prl = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(prl->clk_ref); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + + return 0; +} + +static inline void img_prl_out_writel(struct img_prl_out *prl, + u32 val, u32 reg) +{ + writel(val, prl->base + reg); +} + +static inline u32 img_prl_out_readl(struct img_prl_out *prl, u32 reg) +{ + return readl(prl->base + reg); +} + +static void img_prl_out_reset(struct img_prl_out *prl) +{ + u32 ctl; + + ctl = img_prl_out_readl(prl, IMG_PRL_OUT_CTL) & + ~IMG_PRL_OUT_CTL_ME_MASK; + + reset_control_assert(prl->rst); + reset_control_deassert(prl->rst); + + img_prl_out_writel(prl, ctl, IMG_PRL_OUT_CTL); +} + +static int img_prl_out_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai); + u32 reg; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL); + reg |= IMG_PRL_OUT_CTL_ME_MASK; + img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + img_prl_out_reset(prl); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int img_prl_out_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai); + unsigned int rate, channels; + u32 reg, control_set = 0; + snd_pcm_format_t format; + + rate = params_rate(params); + format = params_format(params); + channels = params_channels(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + control_set |= IMG_PRL_OUT_CTL_PACKH_MASK; + break; + case SNDRV_PCM_FORMAT_S24_LE: + break; + default: + return -EINVAL; + } + + if (channels != 2) + return -EINVAL; + + clk_set_rate(prl->clk_ref, rate * 256); + + reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL); + reg = (reg & ~IMG_PRL_OUT_CTL_PACKH_MASK) | control_set; + img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL); + + return 0; +} + +static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai); + u32 reg, control_set = 0; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + control_set |= IMG_PRL_OUT_CTL_EDGE_MASK; + break; + default: + return -EINVAL; + } + + reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL); + reg = (reg & ~IMG_PRL_OUT_CTL_EDGE_MASK) | control_set; + img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL); + + return 0; +} + +static const struct snd_soc_dai_ops img_prl_out_dai_ops = { + .trigger = img_prl_out_trigger, + .hw_params = img_prl_out_hw_params, + .set_fmt = img_prl_out_set_fmt +}; + +static int img_prl_out_dai_probe(struct snd_soc_dai *dai) +{ + struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &prl->dma_data, NULL); + + return 0; +} + +static struct snd_soc_dai_driver img_prl_out_dai = { + .probe = img_prl_out_dai_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE + }, + .ops = &img_prl_out_dai_ops +}; + +static const struct snd_soc_component_driver img_prl_out_component = { + .name = "img-prl-out" +}; + +static int img_prl_out_probe(struct platform_device *pdev) +{ + struct img_prl_out *prl; + struct resource *res; + void __iomem *base; + int ret; + struct device *dev = &pdev->dev; + + prl = devm_kzalloc(&pdev->dev, sizeof(*prl), GFP_KERNEL); + if (!prl) + return -ENOMEM; + + platform_set_drvdata(pdev, prl); + + prl->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + prl->base = base; + + prl->rst = devm_reset_control_get(&pdev->dev, "rst"); + if (IS_ERR(prl->rst)) { + if (PTR_ERR(prl->rst) != -EPROBE_DEFER) + dev_err(&pdev->dev, "No top level reset found\n"); + return PTR_ERR(prl->rst); + } + + prl->clk_sys = devm_clk_get(&pdev->dev, "sys"); + if (IS_ERR(prl->clk_sys)) { + if (PTR_ERR(prl->clk_sys) != -EPROBE_DEFER) + dev_err(dev, "Failed to acquire clock 'sys'\n"); + return PTR_ERR(prl->clk_sys); + } + + prl->clk_ref = devm_clk_get(&pdev->dev, "ref"); + if (IS_ERR(prl->clk_ref)) { + if (PTR_ERR(prl->clk_ref) != -EPROBE_DEFER) + dev_err(dev, "Failed to acquire clock 'ref'\n"); + return PTR_ERR(prl->clk_ref); + } + + ret = clk_prepare_enable(prl->clk_sys); + if (ret) + return ret; + + img_prl_out_writel(prl, IMG_PRL_OUT_CTL_EDGE_MASK, IMG_PRL_OUT_CTL); + img_prl_out_reset(prl); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_prl_out_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + prl->dma_data.addr = res->start + IMG_PRL_OUT_TX_FIFO; + prl->dma_data.addr_width = 4; + prl->dma_data.maxburst = 4; + + ret = devm_snd_soc_register_component(&pdev->dev, + &img_prl_out_component, + &img_prl_out_dai, 1); + if (ret) + goto err_suspend; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) + goto err_suspend; + + return 0; + +err_suspend: + if (!pm_runtime_status_suspended(&pdev->dev)) + img_prl_out_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); + clk_disable_unprepare(prl->clk_sys); + + return ret; +} + +static int img_prl_out_dev_remove(struct platform_device *pdev) +{ + struct img_prl_out *prl = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + img_prl_out_suspend(&pdev->dev); + + clk_disable_unprepare(prl->clk_sys); + + return 0; +} + +static const struct of_device_id img_prl_out_of_match[] = { + { .compatible = "img,parallel-out" }, + {} +}; +MODULE_DEVICE_TABLE(of, img_prl_out_of_match); + +static const struct dev_pm_ops img_prl_out_pm_ops = { + SET_RUNTIME_PM_OPS(img_prl_out_suspend, + img_prl_out_resume, NULL) +}; + +static struct platform_driver img_prl_out_driver = { + .driver = { + .name = "img-parallel-out", + .of_match_table = img_prl_out_of_match, + .pm = &img_prl_out_pm_ops + }, + .probe = img_prl_out_probe, + .remove = img_prl_out_dev_remove +}; +module_platform_driver(img_prl_out_driver); + +MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); +MODULE_DESCRIPTION("IMG Parallel Output Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/img/img-spdif-in.c b/sound/soc/img/img-spdif-in.c new file mode 100644 index 000000000000..4d9953d318af --- /dev/null +++ b/sound/soc/img/img-spdif-in.c @@ -0,0 +1,806 @@ +/* + * IMG SPDIF input controller driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley <Damien.Horsley@imgtec.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +#include <sound/core.h> +#include <sound/dmaengine_pcm.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#define IMG_SPDIF_IN_RX_FIFO_OFFSET 0 + +#define IMG_SPDIF_IN_CTL 0x4 +#define IMG_SPDIF_IN_CTL_LOCKLO_MASK 0xff +#define IMG_SPDIF_IN_CTL_LOCKLO_SHIFT 0 +#define IMG_SPDIF_IN_CTL_LOCKHI_MASK 0xff00 +#define IMG_SPDIF_IN_CTL_LOCKHI_SHIFT 8 +#define IMG_SPDIF_IN_CTL_TRK_MASK 0xff0000 +#define IMG_SPDIF_IN_CTL_TRK_SHIFT 16 +#define IMG_SPDIF_IN_CTL_SRD_MASK 0x70000000 +#define IMG_SPDIF_IN_CTL_SRD_SHIFT 28 +#define IMG_SPDIF_IN_CTL_SRT_MASK BIT(31) + +#define IMG_SPDIF_IN_STATUS 0x8 +#define IMG_SPDIF_IN_STATUS_SAM_MASK 0x7000 +#define IMG_SPDIF_IN_STATUS_SAM_SHIFT 12 +#define IMG_SPDIF_IN_STATUS_LOCK_MASK BIT(15) +#define IMG_SPDIF_IN_STATUS_LOCK_SHIFT 15 + +#define IMG_SPDIF_IN_CLKGEN 0x1c +#define IMG_SPDIF_IN_CLKGEN_NOM_MASK 0x3ff +#define IMG_SPDIF_IN_CLKGEN_NOM_SHIFT 0 +#define IMG_SPDIF_IN_CLKGEN_HLD_MASK 0x3ff0000 +#define IMG_SPDIF_IN_CLKGEN_HLD_SHIFT 16 + +#define IMG_SPDIF_IN_CSL 0x20 + +#define IMG_SPDIF_IN_CSH 0x24 +#define IMG_SPDIF_IN_CSH_MASK 0xff +#define IMG_SPDIF_IN_CSH_SHIFT 0 + +#define IMG_SPDIF_IN_SOFT_RESET 0x28 +#define IMG_SPDIF_IN_SOFT_RESET_MASK BIT(0) + +#define IMG_SPDIF_IN_ACLKGEN_START 0x2c +#define IMG_SPDIF_IN_ACLKGEN_NOM_MASK 0x3ff +#define IMG_SPDIF_IN_ACLKGEN_NOM_SHIFT 0 +#define IMG_SPDIF_IN_ACLKGEN_HLD_MASK 0xffc00 +#define IMG_SPDIF_IN_ACLKGEN_HLD_SHIFT 10 +#define IMG_SPDIF_IN_ACLKGEN_TRK_MASK 0xff00000 +#define IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT 20 + +#define IMG_SPDIF_IN_NUM_ACLKGEN 4 + +struct img_spdif_in { + spinlock_t lock; + void __iomem *base; + struct clk *clk_sys; + struct snd_dmaengine_dai_dma_data dma_data; + struct device *dev; + unsigned int trk; + bool multi_freq; + int lock_acquire; + int lock_release; + unsigned int single_freq; + unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN]; + bool active; + + /* Write-only registers */ + unsigned int aclkgen_regs[IMG_SPDIF_IN_NUM_ACLKGEN]; +}; + +static inline void img_spdif_in_writel(struct img_spdif_in *spdif, + u32 val, u32 reg) +{ + writel(val, spdif->base + reg); +} + +static inline u32 img_spdif_in_readl(struct img_spdif_in *spdif, u32 reg) +{ + return readl(spdif->base + reg); +} + +static inline void img_spdif_in_aclkgen_writel(struct img_spdif_in *spdif, + u32 index) +{ + img_spdif_in_writel(spdif, spdif->aclkgen_regs[index], + IMG_SPDIF_IN_ACLKGEN_START + (index * 0x4)); +} + +static int img_spdif_in_check_max_rate(struct img_spdif_in *spdif, + unsigned int sample_rate, unsigned long *actual_freq) +{ + unsigned long min_freq, freq_t; + + /* Clock rate must be at least 24x the bit rate */ + min_freq = sample_rate * 2 * 32 * 24; + + freq_t = clk_get_rate(spdif->clk_sys); + + if (freq_t < min_freq) + return -EINVAL; + + *actual_freq = freq_t; + + return 0; +} + +static int img_spdif_in_do_clkgen_calc(unsigned int rate, unsigned int *pnom, + unsigned int *phld, unsigned long clk_rate) +{ + unsigned int ori, nom, hld; + + /* + * Calculate oversampling ratio, nominal phase increment and hold + * increment for the given rate / frequency + */ + + if (!rate) + return -EINVAL; + + ori = clk_rate / (rate * 64); + + if (!ori) + return -EINVAL; + + nom = (4096 / ori) + 1; + do + hld = 4096 - (--nom * (ori - 1)); + while (hld < 120); + + *pnom = nom; + *phld = hld; + + return 0; +} + +static int img_spdif_in_do_clkgen_single(struct img_spdif_in *spdif, + unsigned int rate) +{ + unsigned int nom, hld; + unsigned long flags, clk_rate; + int ret = 0; + u32 reg; + + ret = img_spdif_in_check_max_rate(spdif, rate, &clk_rate); + if (ret) + return ret; + + ret = img_spdif_in_do_clkgen_calc(rate, &nom, &hld, clk_rate); + if (ret) + return ret; + + reg = (nom << IMG_SPDIF_IN_CLKGEN_NOM_SHIFT) & + IMG_SPDIF_IN_CLKGEN_NOM_MASK; + reg |= (hld << IMG_SPDIF_IN_CLKGEN_HLD_SHIFT) & + IMG_SPDIF_IN_CLKGEN_HLD_MASK; + + spin_lock_irqsave(&spdif->lock, flags); + + if (spdif->active) { + spin_unlock_irqrestore(&spdif->lock, flags); + return -EBUSY; + } + + img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CLKGEN); + + spdif->single_freq = rate; + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static int img_spdif_in_do_clkgen_multi(struct img_spdif_in *spdif, + unsigned int multi_freqs[]) +{ + unsigned int nom, hld, rate, max_rate = 0; + unsigned long flags, clk_rate; + int i, ret = 0; + u32 reg, trk_reg, temp_regs[IMG_SPDIF_IN_NUM_ACLKGEN]; + + for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) + if (multi_freqs[i] > max_rate) + max_rate = multi_freqs[i]; + + ret = img_spdif_in_check_max_rate(spdif, max_rate, &clk_rate); + if (ret) + return ret; + + for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) { + rate = multi_freqs[i]; + + ret = img_spdif_in_do_clkgen_calc(rate, &nom, &hld, clk_rate); + if (ret) + return ret; + + reg = (nom << IMG_SPDIF_IN_ACLKGEN_NOM_SHIFT) & + IMG_SPDIF_IN_ACLKGEN_NOM_MASK; + reg |= (hld << IMG_SPDIF_IN_ACLKGEN_HLD_SHIFT) & + IMG_SPDIF_IN_ACLKGEN_HLD_MASK; + temp_regs[i] = reg; + } + + spin_lock_irqsave(&spdif->lock, flags); + + if (spdif->active) { + spin_unlock_irqrestore(&spdif->lock, flags); + return -EBUSY; + } + + trk_reg = spdif->trk << IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT; + + for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) { + spdif->aclkgen_regs[i] = temp_regs[i] | trk_reg; + img_spdif_in_aclkgen_writel(spdif, i); + } + + spdif->multi_freq = true; + spdif->multi_freqs[0] = multi_freqs[0]; + spdif->multi_freqs[1] = multi_freqs[1]; + spdif->multi_freqs[2] = multi_freqs[2]; + spdif->multi_freqs[3] = multi_freqs[3]; + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static int img_spdif_in_iec958_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + + return 0; +} + +static int img_spdif_in_get_status_mask(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + ucontrol->value.iec958.status[4] = 0xff; + + return 0; +} + +static int img_spdif_in_get_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + u32 reg; + + reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CSL); + ucontrol->value.iec958.status[0] = reg & 0xff; + ucontrol->value.iec958.status[1] = (reg >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (reg >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (reg >> 24) & 0xff; + reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CSH); + ucontrol->value.iec958.status[4] = (reg & IMG_SPDIF_IN_CSH_MASK) + >> IMG_SPDIF_IN_CSH_SHIFT; + + return 0; +} + +static int img_spdif_in_info_multi_freq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = IMG_SPDIF_IN_NUM_ACLKGEN; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = LONG_MAX; + + return 0; +} + +static int img_spdif_in_get_multi_freq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long flags; + + spin_lock_irqsave(&spdif->lock, flags); + if (spdif->multi_freq) { + ucontrol->value.integer.value[0] = spdif->multi_freqs[0]; + ucontrol->value.integer.value[1] = spdif->multi_freqs[1]; + ucontrol->value.integer.value[2] = spdif->multi_freqs[2]; + ucontrol->value.integer.value[3] = spdif->multi_freqs[3]; + } else { + ucontrol->value.integer.value[0] = 0; + ucontrol->value.integer.value[1] = 0; + ucontrol->value.integer.value[2] = 0; + ucontrol->value.integer.value[3] = 0; + } + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static int img_spdif_in_set_multi_freq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN]; + bool multi_freq; + unsigned long flags; + + if ((ucontrol->value.integer.value[0] == 0) && + (ucontrol->value.integer.value[1] == 0) && + (ucontrol->value.integer.value[2] == 0) && + (ucontrol->value.integer.value[3] == 0)) { + multi_freq = false; + } else { + multi_freqs[0] = ucontrol->value.integer.value[0]; + multi_freqs[1] = ucontrol->value.integer.value[1]; + multi_freqs[2] = ucontrol->value.integer.value[2]; + multi_freqs[3] = ucontrol->value.integer.value[3]; + multi_freq = true; + } + + if (multi_freq) + return img_spdif_in_do_clkgen_multi(spdif, multi_freqs); + + spin_lock_irqsave(&spdif->lock, flags); + + if (spdif->active) { + spin_unlock_irqrestore(&spdif->lock, flags); + return -EBUSY; + } + + spdif->multi_freq = false; + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static int img_spdif_in_info_lock_freq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = LONG_MAX; + + return 0; +} + +static int img_spdif_in_get_lock_freq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uc) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + u32 reg; + int i; + unsigned long flags; + + spin_lock_irqsave(&spdif->lock, flags); + + reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_STATUS); + if (reg & IMG_SPDIF_IN_STATUS_LOCK_MASK) { + if (spdif->multi_freq) { + i = ((reg & IMG_SPDIF_IN_STATUS_SAM_MASK) >> + IMG_SPDIF_IN_STATUS_SAM_SHIFT) - 1; + uc->value.integer.value[0] = spdif->multi_freqs[i]; + } else { + uc->value.integer.value[0] = spdif->single_freq; + } + } else { + uc->value.integer.value[0] = 0; + } + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static int img_spdif_in_info_trk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + + return 0; +} + +static int img_spdif_in_get_trk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + + ucontrol->value.integer.value[0] = spdif->trk; + + return 0; +} + +static int img_spdif_in_set_trk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long flags; + int i; + u32 reg; + + spin_lock_irqsave(&spdif->lock, flags); + + if (spdif->active) { + spin_unlock_irqrestore(&spdif->lock, flags); + return -EBUSY; + } + + spdif->trk = ucontrol->value.integer.value[0]; + + reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); + reg &= ~IMG_SPDIF_IN_CTL_TRK_MASK; + reg |= spdif->trk << IMG_SPDIF_IN_CTL_TRK_SHIFT; + img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); + + for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) { + spdif->aclkgen_regs[i] = (spdif->aclkgen_regs[i] & + ~IMG_SPDIF_IN_ACLKGEN_TRK_MASK) | + (spdif->trk << IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT); + + img_spdif_in_aclkgen_writel(spdif, i); + } + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static int img_spdif_in_info_lock(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = -128; + uinfo->value.integer.max = 127; + + return 0; +} + +static int img_spdif_in_get_lock_acquire(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + + ucontrol->value.integer.value[0] = spdif->lock_acquire; + + return 0; +} + +static int img_spdif_in_set_lock_acquire(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&spdif->lock, flags); + + if (spdif->active) { + spin_unlock_irqrestore(&spdif->lock, flags); + return -EBUSY; + } + + spdif->lock_acquire = ucontrol->value.integer.value[0]; + + reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); + reg &= ~IMG_SPDIF_IN_CTL_LOCKHI_MASK; + reg |= (spdif->lock_acquire << IMG_SPDIF_IN_CTL_LOCKHI_SHIFT) & + IMG_SPDIF_IN_CTL_LOCKHI_MASK; + img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static int img_spdif_in_get_lock_release(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + + ucontrol->value.integer.value[0] = spdif->lock_release; + + return 0; +} + +static int img_spdif_in_set_lock_release(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&spdif->lock, flags); + + if (spdif->active) { + spin_unlock_irqrestore(&spdif->lock, flags); + return -EBUSY; + } + + spdif->lock_release = ucontrol->value.integer.value[0]; + + reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); + reg &= ~IMG_SPDIF_IN_CTL_LOCKLO_MASK; + reg |= (spdif->lock_release << IMG_SPDIF_IN_CTL_LOCKLO_SHIFT) & + IMG_SPDIF_IN_CTL_LOCKLO_MASK; + img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static struct snd_kcontrol_new img_spdif_in_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK), + .info = img_spdif_in_iec958_info, + .get = img_spdif_in_get_status_mask + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), + .info = img_spdif_in_iec958_info, + .get = img_spdif_in_get_status + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SPDIF In Multi Frequency Acquire", + .info = img_spdif_in_info_multi_freq, + .get = img_spdif_in_get_multi_freq, + .put = img_spdif_in_set_multi_freq + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SPDIF In Lock Frequency", + .info = img_spdif_in_info_lock_freq, + .get = img_spdif_in_get_lock_freq + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SPDIF In Lock TRK", + .info = img_spdif_in_info_trk, + .get = img_spdif_in_get_trk, + .put = img_spdif_in_set_trk + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SPDIF In Lock Acquire Threshold", + .info = img_spdif_in_info_lock, + .get = img_spdif_in_get_lock_acquire, + .put = img_spdif_in_set_lock_acquire + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SPDIF In Lock Release Threshold", + .info = img_spdif_in_info_lock, + .get = img_spdif_in_get_lock_release, + .put = img_spdif_in_set_lock_release + } +}; + +static int img_spdif_in_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + unsigned long flags; + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai); + int ret = 0; + u32 reg; + + spin_lock_irqsave(&spdif->lock, flags); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); + if (spdif->multi_freq) + reg &= ~IMG_SPDIF_IN_CTL_SRD_MASK; + else + reg |= (1UL << IMG_SPDIF_IN_CTL_SRD_SHIFT); + reg |= IMG_SPDIF_IN_CTL_SRT_MASK; + img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); + spdif->active = true; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); + reg &= ~IMG_SPDIF_IN_CTL_SRT_MASK; + img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); + spdif->active = false; + break; + default: + ret = -EINVAL; + } + + spin_unlock_irqrestore(&spdif->lock, flags); + + return ret; +} + +static int img_spdif_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai); + unsigned int rate, channels; + snd_pcm_format_t format; + + rate = params_rate(params); + channels = params_channels(params); + format = params_format(params); + + if (format != SNDRV_PCM_FORMAT_S32_LE) + return -EINVAL; + + if (channels != 2) + return -EINVAL; + + return img_spdif_in_do_clkgen_single(spdif, rate); +} + +static const struct snd_soc_dai_ops img_spdif_in_dai_ops = { + .trigger = img_spdif_in_trigger, + .hw_params = img_spdif_in_hw_params +}; + +static int img_spdif_in_dai_probe(struct snd_soc_dai *dai) +{ + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, NULL, &spdif->dma_data); + + snd_soc_add_dai_controls(dai, img_spdif_in_controls, + ARRAY_SIZE(img_spdif_in_controls)); + + return 0; +} + +static struct snd_soc_dai_driver img_spdif_in_dai = { + .probe = img_spdif_in_dai_probe, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S32_LE + }, + .ops = &img_spdif_in_dai_ops +}; + +static const struct snd_soc_component_driver img_spdif_in_component = { + .name = "img-spdif-in" +}; + +static int img_spdif_in_probe(struct platform_device *pdev) +{ + struct img_spdif_in *spdif; + struct resource *res; + void __iomem *base; + int ret; + struct reset_control *rst; + u32 reg; + struct device *dev = &pdev->dev; + + spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL); + if (!spdif) + return -ENOMEM; + + platform_set_drvdata(pdev, spdif); + + spdif->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + spdif->base = base; + + spdif->clk_sys = devm_clk_get(dev, "sys"); + if (IS_ERR(spdif->clk_sys)) { + if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER) + dev_err(dev, "Failed to acquire clock 'sys'\n"); + return PTR_ERR(spdif->clk_sys); + } + + ret = clk_prepare_enable(spdif->clk_sys); + if (ret) + return ret; + + rst = devm_reset_control_get(&pdev->dev, "rst"); + if (IS_ERR(rst)) { + if (PTR_ERR(rst) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_clk_disable; + } + dev_dbg(dev, "No top level reset found\n"); + img_spdif_in_writel(spdif, IMG_SPDIF_IN_SOFT_RESET_MASK, + IMG_SPDIF_IN_SOFT_RESET); + img_spdif_in_writel(spdif, 0, IMG_SPDIF_IN_SOFT_RESET); + } else { + reset_control_assert(rst); + reset_control_deassert(rst); + } + + spin_lock_init(&spdif->lock); + + spdif->dma_data.addr = res->start + IMG_SPDIF_IN_RX_FIFO_OFFSET; + spdif->dma_data.addr_width = 4; + spdif->dma_data.maxburst = 4; + spdif->trk = 0x80; + spdif->lock_acquire = 4; + spdif->lock_release = -128; + + reg = (spdif->lock_acquire << IMG_SPDIF_IN_CTL_LOCKHI_SHIFT) & + IMG_SPDIF_IN_CTL_LOCKHI_MASK; + reg |= (spdif->lock_release << IMG_SPDIF_IN_CTL_LOCKLO_SHIFT) & + IMG_SPDIF_IN_CTL_LOCKLO_MASK; + reg |= (spdif->trk << IMG_SPDIF_IN_CTL_TRK_SHIFT) & + IMG_SPDIF_IN_CTL_TRK_MASK; + img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); + + ret = devm_snd_soc_register_component(&pdev->dev, + &img_spdif_in_component, &img_spdif_in_dai, 1); + if (ret) + goto err_clk_disable; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) + goto err_clk_disable; + + return 0; + +err_clk_disable: + clk_disable_unprepare(spdif->clk_sys); + + return ret; +} + +static int img_spdif_in_dev_remove(struct platform_device *pdev) +{ + struct img_spdif_in *spdif = platform_get_drvdata(pdev); + + clk_disable_unprepare(spdif->clk_sys); + + return 0; +} + +static const struct of_device_id img_spdif_in_of_match[] = { + { .compatible = "img,spdif-in" }, + {} +}; +MODULE_DEVICE_TABLE(of, img_spdif_in_of_match); + +static struct platform_driver img_spdif_in_driver = { + .driver = { + .name = "img-spdif-in", + .of_match_table = img_spdif_in_of_match + }, + .probe = img_spdif_in_probe, + .remove = img_spdif_in_dev_remove +}; +module_platform_driver(img_spdif_in_driver); + +MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); +MODULE_DESCRIPTION("IMG SPDIF Input driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/img/img-spdif-out.c b/sound/soc/img/img-spdif-out.c new file mode 100644 index 000000000000..08f93a5dadfe --- /dev/null +++ b/sound/soc/img/img-spdif-out.c @@ -0,0 +1,441 @@ +/* + * IMG SPDIF output controller driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley <Damien.Horsley@imgtec.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> + +#include <sound/core.h> +#include <sound/dmaengine_pcm.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#define IMG_SPDIF_OUT_TX_FIFO 0x0 + +#define IMG_SPDIF_OUT_CTL 0x4 +#define IMG_SPDIF_OUT_CTL_FS_MASK BIT(4) +#define IMG_SPDIF_OUT_CTL_CLK_MASK BIT(2) +#define IMG_SPDIF_OUT_CTL_SRT_MASK BIT(0) + +#define IMG_SPDIF_OUT_CSL 0x14 + +#define IMG_SPDIF_OUT_CSH_UV 0x18 +#define IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT 0 +#define IMG_SPDIF_OUT_CSH_UV_CSH_MASK 0xff + +struct img_spdif_out { + spinlock_t lock; + void __iomem *base; + struct clk *clk_sys; + struct clk *clk_ref; + struct snd_dmaengine_dai_dma_data dma_data; + struct device *dev; + struct reset_control *rst; +}; + +static int img_spdif_out_suspend(struct device *dev) +{ + struct img_spdif_out *spdif = dev_get_drvdata(dev); + + clk_disable_unprepare(spdif->clk_ref); + + return 0; +} + +static int img_spdif_out_resume(struct device *dev) +{ + struct img_spdif_out *spdif = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(spdif->clk_ref); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + + return 0; +} + +static inline void img_spdif_out_writel(struct img_spdif_out *spdif, u32 val, + u32 reg) +{ + writel(val, spdif->base + reg); +} + +static inline u32 img_spdif_out_readl(struct img_spdif_out *spdif, u32 reg) +{ + return readl(spdif->base + reg); +} + +static void img_spdif_out_reset(struct img_spdif_out *spdif) +{ + u32 ctl, status_low, status_high; + + ctl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL) & + ~IMG_SPDIF_OUT_CTL_SRT_MASK; + status_low = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL); + status_high = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV); + + reset_control_assert(spdif->rst); + reset_control_deassert(spdif->rst); + + img_spdif_out_writel(spdif, ctl, IMG_SPDIF_OUT_CTL); + img_spdif_out_writel(spdif, status_low, IMG_SPDIF_OUT_CSL); + img_spdif_out_writel(spdif, status_high, IMG_SPDIF_OUT_CSH_UV); +} + +static int img_spdif_out_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + + return 0; +} + +static int img_spdif_out_get_status_mask(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + ucontrol->value.iec958.status[4] = 0xff; + + return 0; +} + +static int img_spdif_out_get_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(cpu_dai); + u32 reg; + unsigned long flags; + + spin_lock_irqsave(&spdif->lock, flags); + + reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL); + ucontrol->value.iec958.status[0] = reg & 0xff; + ucontrol->value.iec958.status[1] = (reg >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (reg >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (reg >> 24) & 0xff; + + reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV); + ucontrol->value.iec958.status[4] = + (reg & IMG_SPDIF_OUT_CSH_UV_CSH_MASK) >> + IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT; + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static int img_spdif_out_set_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(cpu_dai); + u32 reg; + unsigned long flags; + + reg = ((u32)ucontrol->value.iec958.status[3] << 24); + reg |= ((u32)ucontrol->value.iec958.status[2] << 16); + reg |= ((u32)ucontrol->value.iec958.status[1] << 8); + reg |= (u32)ucontrol->value.iec958.status[0]; + + spin_lock_irqsave(&spdif->lock, flags); + + img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CSL); + + reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV); + reg &= ~IMG_SPDIF_OUT_CSH_UV_CSH_MASK; + reg |= (u32)ucontrol->value.iec958.status[4] << + IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT; + img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CSH_UV); + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static struct snd_kcontrol_new img_spdif_out_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), + .info = img_spdif_out_info, + .get = img_spdif_out_get_status_mask + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = img_spdif_out_info, + .get = img_spdif_out_get_status, + .put = img_spdif_out_set_status + } +}; + +static int img_spdif_out_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai); + u32 reg; + unsigned long flags; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL); + reg |= IMG_SPDIF_OUT_CTL_SRT_MASK; + img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CTL); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + spin_lock_irqsave(&spdif->lock, flags); + img_spdif_out_reset(spdif); + spin_unlock_irqrestore(&spdif->lock, flags); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int img_spdif_out_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai); + unsigned int channels; + long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate; + u32 reg; + snd_pcm_format_t format; + + rate = params_rate(params); + format = params_format(params); + channels = params_channels(params); + + dev_dbg(spdif->dev, "hw_params rate %ld channels %u format %u\n", + rate, channels, format); + + if (format != SNDRV_PCM_FORMAT_S32_LE) + return -EINVAL; + + if (channels != 2) + return -EINVAL; + + pre_div_a = clk_round_rate(spdif->clk_ref, rate * 256); + if (pre_div_a < 0) + return pre_div_a; + pre_div_b = clk_round_rate(spdif->clk_ref, rate * 384); + if (pre_div_b < 0) + return pre_div_b; + + diff_a = abs((pre_div_a / 256) - rate); + diff_b = abs((pre_div_b / 384) - rate); + + /* If diffs are equal, use lower clock rate */ + if (diff_a > diff_b) + clk_set_rate(spdif->clk_ref, pre_div_b); + else + clk_set_rate(spdif->clk_ref, pre_div_a); + + /* + * Another driver (eg machine driver) may have rejected the above + * change. Get the current rate and set the register bit according to + * the new min diff + */ + clk_rate = clk_get_rate(spdif->clk_ref); + + diff_a = abs((clk_rate / 256) - rate); + diff_b = abs((clk_rate / 384) - rate); + + reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL); + if (diff_a <= diff_b) + reg &= ~IMG_SPDIF_OUT_CTL_CLK_MASK; + else + reg |= IMG_SPDIF_OUT_CTL_CLK_MASK; + img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CTL); + + return 0; +} + +static const struct snd_soc_dai_ops img_spdif_out_dai_ops = { + .trigger = img_spdif_out_trigger, + .hw_params = img_spdif_out_hw_params +}; + +static int img_spdif_out_dai_probe(struct snd_soc_dai *dai) +{ + struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &spdif->dma_data, NULL); + + snd_soc_add_dai_controls(dai, img_spdif_out_controls, + ARRAY_SIZE(img_spdif_out_controls)); + + return 0; +} + +static struct snd_soc_dai_driver img_spdif_out_dai = { + .probe = img_spdif_out_dai_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S32_LE + }, + .ops = &img_spdif_out_dai_ops +}; + +static const struct snd_soc_component_driver img_spdif_out_component = { + .name = "img-spdif-out" +}; + +static int img_spdif_out_probe(struct platform_device *pdev) +{ + struct img_spdif_out *spdif; + struct resource *res; + void __iomem *base; + int ret; + struct device *dev = &pdev->dev; + + spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL); + if (!spdif) + return -ENOMEM; + + platform_set_drvdata(pdev, spdif); + + spdif->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + spdif->base = base; + + spdif->rst = devm_reset_control_get(&pdev->dev, "rst"); + if (IS_ERR(spdif->rst)) { + if (PTR_ERR(spdif->rst) != -EPROBE_DEFER) + dev_err(&pdev->dev, "No top level reset found\n"); + return PTR_ERR(spdif->rst); + } + + spdif->clk_sys = devm_clk_get(&pdev->dev, "sys"); + if (IS_ERR(spdif->clk_sys)) { + if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER) + dev_err(dev, "Failed to acquire clock 'sys'\n"); + return PTR_ERR(spdif->clk_sys); + } + + spdif->clk_ref = devm_clk_get(&pdev->dev, "ref"); + if (IS_ERR(spdif->clk_ref)) { + if (PTR_ERR(spdif->clk_ref) != -EPROBE_DEFER) + dev_err(dev, "Failed to acquire clock 'ref'\n"); + return PTR_ERR(spdif->clk_ref); + } + + ret = clk_prepare_enable(spdif->clk_sys); + if (ret) + return ret; + + img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK, + IMG_SPDIF_OUT_CTL); + + img_spdif_out_reset(spdif); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_spdif_out_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + spin_lock_init(&spdif->lock); + + spdif->dma_data.addr = res->start + IMG_SPDIF_OUT_TX_FIFO; + spdif->dma_data.addr_width = 4; + spdif->dma_data.maxburst = 4; + + ret = devm_snd_soc_register_component(&pdev->dev, + &img_spdif_out_component, + &img_spdif_out_dai, 1); + if (ret) + goto err_suspend; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) + goto err_suspend; + + dev_dbg(&pdev->dev, "Probe successful\n"); + + return 0; + +err_suspend: + if (!pm_runtime_status_suspended(&pdev->dev)) + img_spdif_out_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); + clk_disable_unprepare(spdif->clk_sys); + + return ret; +} + +static int img_spdif_out_dev_remove(struct platform_device *pdev) +{ + struct img_spdif_out *spdif = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + img_spdif_out_suspend(&pdev->dev); + + clk_disable_unprepare(spdif->clk_sys); + + return 0; +} + +static const struct of_device_id img_spdif_out_of_match[] = { + { .compatible = "img,spdif-out" }, + {} +}; +MODULE_DEVICE_TABLE(of, img_spdif_out_of_match); + +static const struct dev_pm_ops img_spdif_out_pm_ops = { + SET_RUNTIME_PM_OPS(img_spdif_out_suspend, + img_spdif_out_resume, NULL) +}; + +static struct platform_driver img_spdif_out_driver = { + .driver = { + .name = "img-spdif-out", + .of_match_table = img_spdif_out_of_match, + .pm = &img_spdif_out_pm_ops + }, + .probe = img_spdif_out_probe, + .remove = img_spdif_out_dev_remove +}; +module_platform_driver(img_spdif_out_driver); + +MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); +MODULE_DESCRIPTION("IMG SPDIF Output driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/img/pistachio-internal-dac.c b/sound/soc/img/pistachio-internal-dac.c new file mode 100644 index 000000000000..162a0fd68c7b --- /dev/null +++ b/sound/soc/img/pistachio-internal-dac.c @@ -0,0 +1,287 @@ +/* + * Pistachio internal dac driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley <Damien.Horsley@imgtec.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#define PISTACHIO_INTERNAL_DAC_CTRL 0x40 +#define PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK 0x2 +#define PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK 0x1 + +#define PISTACHIO_INTERNAL_DAC_SRST 0x44 +#define PISTACHIO_INTERNAL_DAC_SRST_MASK 0x1 + +#define PISTACHIO_INTERNAL_DAC_GTI_CTRL 0x48 +#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_SHIFT 0 +#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_MASK 0xFFF +#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK 0x1000 +#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_SHIFT 13 +#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_MASK 0x1FE000 + +#define PISTACHIO_INTERNAL_DAC_PWR 0x1 +#define PISTACHIO_INTERNAL_DAC_PWR_MASK 0x1 + +#define PISTACHIO_INTERNAL_DAC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* codec private data */ +struct pistachio_internal_dac { + struct regmap *regmap; + struct regulator *supply; + bool mute; +}; + +static const struct snd_kcontrol_new pistachio_internal_dac_snd_controls[] = { + SOC_SINGLE("Playback Switch", PISTACHIO_INTERNAL_DAC_CTRL, 2, 1, 1) +}; + +static const struct snd_soc_dapm_widget pistachio_internal_dac_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("AOUTL"), + SND_SOC_DAPM_OUTPUT("AOUTR"), +}; + +static const struct snd_soc_dapm_route pistachio_internal_dac_routes[] = { + { "AOUTL", NULL, "DAC" }, + { "AOUTR", NULL, "DAC" }, +}; + +static void pistachio_internal_dac_reg_writel(struct regmap *top_regs, + u32 val, u32 reg) +{ + regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL, + PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_MASK, + reg << PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_SHIFT); + + regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL, + PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_MASK, + val << PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_SHIFT); + + regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL, + PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK, + PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK); + + regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL, + PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK, 0); +} + +static void pistachio_internal_dac_pwr_off(struct pistachio_internal_dac *dac) +{ + regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL, + PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK, + PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK); + + pistachio_internal_dac_reg_writel(dac->regmap, 0, + PISTACHIO_INTERNAL_DAC_PWR); +} + +static void pistachio_internal_dac_pwr_on(struct pistachio_internal_dac *dac) +{ + regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_SRST, + PISTACHIO_INTERNAL_DAC_SRST_MASK, + PISTACHIO_INTERNAL_DAC_SRST_MASK); + + regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_SRST, + PISTACHIO_INTERNAL_DAC_SRST_MASK, 0); + + pistachio_internal_dac_reg_writel(dac->regmap, + PISTACHIO_INTERNAL_DAC_PWR_MASK, + PISTACHIO_INTERNAL_DAC_PWR); + + regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL, + PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK, 0); +} + +static struct snd_soc_dai_driver pistachio_internal_dac_dais[] = { + { + .name = "pistachio_internal_dac", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = PISTACHIO_INTERNAL_DAC_FORMATS, + } + }, +}; + +static int pistachio_internal_dac_codec_probe(struct snd_soc_codec *codec) +{ + struct pistachio_internal_dac *dac = snd_soc_codec_get_drvdata(codec); + + snd_soc_codec_init_regmap(codec, dac->regmap); + + return 0; +} + +static const struct snd_soc_codec_driver pistachio_internal_dac_driver = { + .probe = pistachio_internal_dac_codec_probe, + .idle_bias_off = true, + .controls = pistachio_internal_dac_snd_controls, + .num_controls = ARRAY_SIZE(pistachio_internal_dac_snd_controls), + .dapm_widgets = pistachio_internal_dac_widgets, + .num_dapm_widgets = ARRAY_SIZE(pistachio_internal_dac_widgets), + .dapm_routes = pistachio_internal_dac_routes, + .num_dapm_routes = ARRAY_SIZE(pistachio_internal_dac_routes), +}; + +static int pistachio_internal_dac_probe(struct platform_device *pdev) +{ + struct pistachio_internal_dac *dac; + int ret, voltage; + struct device *dev = &pdev->dev; + u32 reg; + + dac = devm_kzalloc(dev, sizeof(*dac), GFP_KERNEL); + + if (!dac) + return -ENOMEM; + + platform_set_drvdata(pdev, dac); + + dac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "img,cr-top"); + if (IS_ERR(dac->regmap)) + return PTR_ERR(dac->regmap); + + dac->supply = devm_regulator_get(dev, "VDD"); + if (IS_ERR(dac->supply)) { + ret = PTR_ERR(dac->supply); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to acquire supply 'VDD-supply': %d\n", ret); + return ret; + } + + ret = regulator_enable(dac->supply); + if (ret) { + dev_err(dev, "failed to enable supply: %d\n", ret); + return ret; + } + + voltage = regulator_get_voltage(dac->supply); + + switch (voltage) { + case 1800000: + reg = 0; + break; + case 3300000: + reg = PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK; + break; + default: + dev_err(dev, "invalid voltage: %d\n", voltage); + ret = -EINVAL; + goto err_regulator; + } + + regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL, + PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK, reg); + + pistachio_internal_dac_pwr_off(dac); + pistachio_internal_dac_pwr_on(dac); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + ret = snd_soc_register_codec(dev, &pistachio_internal_dac_driver, + pistachio_internal_dac_dais, + ARRAY_SIZE(pistachio_internal_dac_dais)); + if (ret) { + dev_err(dev, "failed to register codec: %d\n", ret); + goto err_pwr; + } + + return 0; + +err_pwr: + pm_runtime_disable(&pdev->dev); + pistachio_internal_dac_pwr_off(dac); +err_regulator: + regulator_disable(dac->supply); + + return ret; +} + +static int pistachio_internal_dac_remove(struct platform_device *pdev) +{ + struct pistachio_internal_dac *dac = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_codec(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pistachio_internal_dac_pwr_off(dac); + regulator_disable(dac->supply); + + return 0; +} + +#ifdef CONFIG_PM +static int pistachio_internal_dac_rt_resume(struct device *dev) +{ + struct pistachio_internal_dac *dac = dev_get_drvdata(dev); + int ret; + + ret = regulator_enable(dac->supply); + if (ret) { + dev_err(dev, "failed to enable supply: %d\n", ret); + return ret; + } + + pistachio_internal_dac_pwr_on(dac); + + return 0; +} + +static int pistachio_internal_dac_rt_suspend(struct device *dev) +{ + struct pistachio_internal_dac *dac = dev_get_drvdata(dev); + + pistachio_internal_dac_pwr_off(dac); + + regulator_disable(dac->supply); + + return 0; +} +#endif + +static const struct dev_pm_ops pistachio_internal_dac_pm_ops = { + SET_RUNTIME_PM_OPS(pistachio_internal_dac_rt_suspend, + pistachio_internal_dac_rt_resume, NULL) +}; + +static const struct of_device_id pistachio_internal_dac_of_match[] = { + { .compatible = "img,pistachio-internal-dac" }, + {} +}; +MODULE_DEVICE_TABLE(of, pistachio_internal_dac_of_match); + +static struct platform_driver pistachio_internal_dac_plat_driver = { + .driver = { + .name = "img-pistachio-internal-dac", + .of_match_table = pistachio_internal_dac_of_match, + .pm = &pistachio_internal_dac_pm_ops + }, + .probe = pistachio_internal_dac_probe, + .remove = pistachio_internal_dac_remove +}; +module_platform_driver(pistachio_internal_dac_plat_driver); + +MODULE_DESCRIPTION("Pistachio Internal DAC driver"); +MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 7b778ab85f8b..337e178c1acb 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -24,6 +24,7 @@ config SND_SST_IPC_PCI config SND_SST_IPC_ACPI tristate select SND_SST_IPC + select SND_SOC_INTEL_SST depends on ACPI config SND_SOC_INTEL_SST @@ -43,7 +44,7 @@ config SND_SOC_INTEL_BAYTRAIL config SND_SOC_INTEL_HASWELL_MACH tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint" depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM - depends on DW_DMAC_CORE + depends on DW_DMAC_CORE=y select SND_SOC_INTEL_SST select SND_SOC_INTEL_HASWELL select SND_SOC_RT5640 @@ -56,18 +57,19 @@ config SND_SOC_INTEL_HASWELL_MACH config SND_SOC_INTEL_BYT_RT5640_MACH tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec" depends on X86_INTEL_LPSS && I2C - depends on DW_DMAC_CORE + depends on DW_DMAC_CORE=y && (SND_SOC_INTEL_BYTCR_RT5640_MACH = n) select SND_SOC_INTEL_SST select SND_SOC_INTEL_BAYTRAIL select SND_SOC_RT5640 help This adds audio driver for Intel Baytrail platform based boards - with the RT5640 audio codec. + with the RT5640 audio codec. This driver is deprecated, use + SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality config SND_SOC_INTEL_BYT_MAX98090_MACH tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec" depends on X86_INTEL_LPSS && I2C - depends on DW_DMAC_CORE + depends on DW_DMAC_CORE=y select SND_SOC_INTEL_SST select SND_SOC_INTEL_BAYTRAIL select SND_SOC_MAX98090 @@ -79,7 +81,7 @@ config SND_SOC_INTEL_BROADWELL_MACH tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint" depends on X86_INTEL_LPSS && I2C && DW_DMAC && \ I2C_DESIGNWARE_PLATFORM - depends on DW_DMAC_CORE + depends on DW_DMAC_CORE=y select SND_SOC_INTEL_SST select SND_SOC_INTEL_HASWELL select SND_SOC_RT286 @@ -90,14 +92,14 @@ config SND_SOC_INTEL_BROADWELL_MACH If unsure select "N". config SND_SOC_INTEL_BYTCR_RT5640_MACH - tristate "ASoC Audio DSP Support for MID BYT Platform" + tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5640 codec" depends on X86 && I2C select SND_SOC_RT5640 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI help - This adds support for ASoC machine driver for Intel(R) MID Baytrail platform - used as alsa device in audio substem in Intel(R) MID devices + This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR + platforms with RT5640 audio codec. Say Y if you have such a device If unsure select "N". @@ -144,7 +146,7 @@ config SND_SOC_INTEL_SKYLAKE config SND_SOC_INTEL_SKL_RT286_MACH tristate "ASoC Audio driver for SKL with RT286 I2S mode" - depends on X86 && ACPI + depends on X86 && ACPI && I2C select SND_SOC_INTEL_SST select SND_SOC_INTEL_SKYLAKE select SND_SOC_RT286 @@ -154,3 +156,31 @@ config SND_SOC_INTEL_SKL_RT286_MACH with RT286 I2S audio codec. Say Y if you have such a device If unsure select "N". + +config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH + tristate "ASoC Audio driver for SKL with NAU88L25 and SSM4567 in I2S Mode" + depends on X86_INTEL_LPSS && I2C + select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SKYLAKE + select SND_SOC_NAU8825 + select SND_SOC_SSM4567 + select SND_SOC_DMIC + help + This adds support for ASoC Onboard Codec I2S machine driver. This will + create an alsa sound card for NAU88L25 + SSM4567. + Say Y if you have such a device + If unsure select "N". + +config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH + tristate "ASoC Audio driver for SKL with NAU88L25 and MAX98357A in I2S Mode" + depends on X86_INTEL_LPSS && I2C + select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SKYLAKE + select SND_SOC_NAU8825 + select SND_SOC_MAX98357A + select SND_SOC_DMIC + help + This adds support for ASoC Onboard Codec I2S machine driver. This will + create an alsa sound card for NAU88L25 + MAX98357A. + Say Y if you have such a device + If unsure select "N". diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index d55388e082e1..b97e6adcf1b2 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -443,7 +443,7 @@ static int sst_gain_get(struct snd_kcontrol *kcontrol, break; case SST_GAIN_MUTE: - ucontrol->value.integer.value[0] = gv->mute ? 1 : 0; + ucontrol->value.integer.value[0] = gv->mute ? 0 : 1; break; case SST_GAIN_RAMP_DURATION: @@ -479,7 +479,7 @@ static int sst_gain_put(struct snd_kcontrol *kcontrol, break; case SST_GAIN_MUTE: - gv->mute = !!ucontrol->value.integer.value[0]; + gv->mute = !ucontrol->value.integer.value[0]; dev_dbg(cmpnt->dev, "%s: Mute %d\n", mc->pname, gv->mute); break; @@ -1109,6 +1109,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"media0_in", NULL, "Compress Playback"}, {"media1_in", NULL, "Headset Playback"}, {"media2_in", NULL, "pcm0_out"}, + {"media3_in", NULL, "Deepbuffer Playback"}, {"media0_out mix 0", "media0_in Switch", "media0_in"}, {"media0_out mix 0", "media1_in Switch", "media1_in"}, diff --git a/sound/soc/intel/atom/sst-atom-controls.h b/sound/soc/intel/atom/sst-atom-controls.h index 93de8045d4e1..e0113112f668 100644 --- a/sound/soc/intel/atom/sst-atom-controls.h +++ b/sound/soc/intel/atom/sst-atom-controls.h @@ -28,6 +28,7 @@ enum { MERR_DPCM_AUDIO = 0, + MERR_DPCM_DEEP_BUFFER, MERR_DPCM_COMPR, }; diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 0487cfaac538..55c33dc76ce4 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -98,6 +98,7 @@ static struct sst_dev_stream_map dpcm_strm_map[] = { {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA1_IN, SST_TASK_ID_MEDIA, 0}, {MERR_DPCM_COMPR, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA0_IN, SST_TASK_ID_MEDIA, 0}, {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0}, + {MERR_DPCM_DEEP_BUFFER, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA3_IN, SST_TASK_ID_MEDIA, 0}, }; static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream) @@ -500,14 +501,25 @@ static struct snd_soc_dai_driver sst_platform_dai[] = { .channels_min = SST_STEREO, .channels_max = SST_STEREO, .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, .capture = { .stream_name = "Headset Capture", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "deepbuffer-cpu-dai", + .ops = &sst_media_dai_ops, + .playback = { + .stream_name = "Deepbuffer Playback", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, }, { @@ -516,10 +528,6 @@ static struct snd_soc_dai_driver sst_platform_dai[] = { .ops = &sst_compr_dai_ops, .playback = { .stream_name = "Compress Playback", - .channels_min = SST_STEREO, - .channels_max = SST_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, }, }, /* BE CPU Dais */ @@ -760,15 +768,15 @@ static int sst_platform_remove(struct platform_device *pdev) static int sst_soc_prepare(struct device *dev) { struct sst_data *drv = dev_get_drvdata(dev); - int i; + struct snd_soc_pcm_runtime *rtd; /* suspend all pcms first */ snd_soc_suspend(drv->soc_card->dev); snd_soc_poweroff(drv->soc_card->dev); /* set the SSPs to idle */ - for (i = 0; i < drv->soc_card->num_rtd; i++) { - struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai; + list_for_each_entry(rtd, &drv->soc_card->rtd_list, list) { + struct snd_soc_dai *dai = rtd->cpu_dai; if (dai->active) { send_ssp_cmd(dai, dai->name, 0); @@ -782,11 +790,11 @@ static int sst_soc_prepare(struct device *dev) static void sst_soc_complete(struct device *dev) { struct sst_data *drv = dev_get_drvdata(dev); - int i; + struct snd_soc_pcm_runtime *rtd; /* restart SSPs */ - for (i = 0; i < drv->soc_card->num_rtd; i++) { - struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai; + list_for_each_entry(rtd, &drv->soc_card->rtd_list, list) { + struct snd_soc_dai *dai = rtd->cpu_dai; if (dai->active) { sst_handle_vb_timer(dai, true); diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index bb19b5801466..f424460b917e 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -40,18 +40,9 @@ #include <acpi/acpi_bus.h> #include "../sst-mfld-platform.h" #include "../../common/sst-dsp.h" +#include "../../common/sst-acpi.h" #include "sst.h" -struct sst_machines { - char *codec_id; - char board[32]; - char machine[32]; - void (*machine_quirk)(void); - char firmware[FW_NAME_SIZE]; - struct sst_platform_info *pdata; - -}; - /* LPE viewpoint addresses */ #define SST_BYT_IRAM_PHY_START 0xff2c0000 #define SST_BYT_IRAM_PHY_END 0xff2d4000 @@ -223,37 +214,16 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) return 0; } -static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, - void *context, void **ret) -{ - *(bool *)context = true; - return AE_OK; -} - -static struct sst_machines *sst_acpi_find_machine( - struct sst_machines *machines) -{ - struct sst_machines *mach; - bool found = false; - - for (mach = machines; mach->codec_id; mach++) - if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id, - sst_acpi_mach_match, - &found, NULL)) && found) - return mach; - - return NULL; -} - static int sst_acpi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; int ret = 0; struct intel_sst_drv *ctx; const struct acpi_device_id *id; - struct sst_machines *mach; + struct sst_acpi_mach *mach; struct platform_device *mdev; struct platform_device *plat_dev; + struct sst_platform_info *pdata; unsigned int dev_id; id = acpi_match_device(dev->driver->acpi_match_table, dev); @@ -261,12 +231,13 @@ static int sst_acpi_probe(struct platform_device *pdev) return -ENODEV; dev_dbg(dev, "for %s", id->id); - mach = (struct sst_machines *)id->driver_data; + mach = (struct sst_acpi_mach *)id->driver_data; mach = sst_acpi_find_machine(mach); if (mach == NULL) { dev_err(dev, "No matching machine driver found\n"); return -ENODEV; } + pdata = mach->pdata; ret = kstrtouint(id->id, 16, &dev_id); if (ret < 0) { @@ -276,16 +247,16 @@ static int sst_acpi_probe(struct platform_device *pdev) dev_dbg(dev, "ACPI device id: %x\n", dev_id); - plat_dev = platform_device_register_data(dev, mach->pdata->platform, -1, NULL, 0); + plat_dev = platform_device_register_data(dev, pdata->platform, -1, NULL, 0); if (IS_ERR(plat_dev)) { - dev_err(dev, "Failed to create machine device: %s\n", mach->pdata->platform); + dev_err(dev, "Failed to create machine device: %s\n", pdata->platform); return PTR_ERR(plat_dev); } /* Create platform device for sst machine driver */ - mdev = platform_device_register_data(dev, mach->machine, -1, NULL, 0); + mdev = platform_device_register_data(dev, mach->drv_name, -1, NULL, 0); if (IS_ERR(mdev)) { - dev_err(dev, "Failed to create machine device: %s\n", mach->machine); + dev_err(dev, "Failed to create machine device: %s\n", mach->drv_name); return PTR_ERR(mdev); } @@ -294,8 +265,8 @@ static int sst_acpi_probe(struct platform_device *pdev) return ret; /* Fill sst platform data */ - ctx->pdata = mach->pdata; - strcpy(ctx->firmware_name, mach->firmware); + ctx->pdata = pdata; + strcpy(ctx->firmware_name, mach->fw_filename); ret = sst_platform_get_resources(ctx); if (ret) @@ -342,22 +313,22 @@ static int sst_acpi_remove(struct platform_device *pdev) return 0; } -static struct sst_machines sst_acpi_bytcr[] = { - {"10EC5640", "T100", "bytt100_rt5640", NULL, "intel/fw_sst_0f28.bin", +static struct sst_acpi_mach sst_acpi_bytcr[] = { + {"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL, &byt_rvp_platform_data }, {}, }; /* Cherryview-based platforms: CherryTrail and Braswell */ -static struct sst_machines sst_acpi_chv[] = { - {"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "intel/fw_sst_22a8.bin", +static struct sst_acpi_mach sst_acpi_chv[] = { + {"10EC5670", "cht-bsw-rt5672", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, + &chv_platform_data }, + {"10EC5645", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, &chv_platform_data }, - {"10EC5645", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin", + {"10EC5650", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, &chv_platform_data }, - {"10EC5650", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin", + {"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, &chv_platform_data }, - {"193C9890", "cht-bsw", "cht-bsw-max98090", NULL, - "intel/fw_sst_22a8.bin", &chv_platform_data }, {}, }; diff --git a/sound/soc/intel/atom/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c index a74c64c7053c..4ccc80e5e8cc 100644 --- a/sound/soc/intel/atom/sst/sst_stream.c +++ b/sound/soc/intel/atom/sst/sst_stream.c @@ -108,7 +108,7 @@ int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params) str_id, pipe_id); ret = sst_prepare_and_post_msg(sst_drv_ctx, task_id, IPC_CMD, IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param), - &alloc_param, data, true, true, false, true); + &alloc_param, &data, true, true, false, true); if (ret < 0) { dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret); diff --git a/sound/soc/intel/baytrail/sst-baytrail-pcm.c b/sound/soc/intel/baytrail/sst-baytrail-pcm.c index 79547bec558b..4765ad474544 100644 --- a/sound/soc/intel/baytrail/sst-baytrail-pcm.c +++ b/sound/soc/intel/baytrail/sst-baytrail-pcm.c @@ -377,6 +377,8 @@ static int sst_byt_pcm_probe(struct snd_soc_platform *platform) priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data), GFP_KERNEL); + if (!priv_data) + return -ENOMEM; priv_data->byt = plat_data->dsp; snd_soc_platform_set_drvdata(platform, priv_data); diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 371c4565cad8..2485ea9434ad 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -7,6 +7,8 @@ snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o snd-soc-skl_rt286-objs := skl_rt286.o +snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o +snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -17,3 +19,5 @@ obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o +obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o +obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 7a5c9a36c1db..a81389d10e17 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -20,51 +20,75 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/acpi.h> #include <linux/device.h> +#include <linux/dmi.h> #include <linux/slab.h> -#include <linux/input.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/jack.h> #include "../../codecs/rt5640.h" #include "../atom/sst-atom-controls.h" -static const struct snd_soc_dapm_widget byt_dapm_widgets[] = { +static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Int Mic", NULL), - SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_MIC("Internal Mic", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), }; -static const struct snd_soc_dapm_route byt_audio_map[] = { - {"IN2P", NULL, "Headset Mic"}, - {"IN2N", NULL, "Headset Mic"}, - {"Headset Mic", NULL, "MICBIAS1"}, - {"IN1P", NULL, "MICBIAS1"}, - {"LDO2", NULL, "Int Mic"}, - {"Headphone", NULL, "HPOL"}, - {"Headphone", NULL, "HPOR"}, - {"Ext Spk", NULL, "SPOLP"}, - {"Ext Spk", NULL, "SPOLN"}, - {"Ext Spk", NULL, "SPORP"}, - {"Ext Spk", NULL, "SPORN"}, - +static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = { {"AIF1 Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx"}, {"codec_in1", NULL, "ssp2 Rx"}, {"ssp2 Rx", NULL, "AIF1 Capture"}, + + {"Headset Mic", NULL, "MICBIAS1"}, + {"IN2P", NULL, "Headset Mic"}, + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Speaker", NULL, "SPOLP"}, + {"Speaker", NULL, "SPOLN"}, + {"Speaker", NULL, "SPORP"}, + {"Speaker", NULL, "SPORN"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = { + {"DMIC1", NULL, "Internal Mic"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic2_map[] = { + {"DMIC2", NULL, "Internal Mic"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = { + {"Internal Mic", NULL, "MICBIAS1"}, + {"IN1P", NULL, "Internal Mic"}, }; -static const struct snd_kcontrol_new byt_mc_controls[] = { +enum { + BYT_RT5640_DMIC1_MAP, + BYT_RT5640_DMIC2_MAP, + BYT_RT5640_IN1_MAP, +}; + +#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff) +#define BYT_RT5640_DMIC_EN BIT(16) + +static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP | + BYT_RT5640_DMIC_EN; + +static const struct snd_kcontrol_new byt_rt5640_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headset Mic"), - SOC_DAPM_PIN_SWITCH("Int Mic"), - SOC_DAPM_PIN_SWITCH("Ext Spk"), + SOC_DAPM_PIN_SWITCH("Internal Mic"), + SOC_DAPM_PIN_SWITCH("Speaker"), }; -static int byt_aif1_hw_params(struct snd_pcm_substream *substream, +static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -92,7 +116,82 @@ static int byt_aif1_hw_params(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_pcm_stream byt_dai_params = { +static int byt_rt5640_quirk_cb(const struct dmi_system_id *id) +{ + byt_rt5640_quirk = (unsigned long)id->driver_data; + return 1; +} + +static const struct dmi_system_id byt_rt5640_quirk_table[] = { + { + .callback = byt_rt5640_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"), + }, + .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP, + }, + { + .callback = byt_rt5640_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "DellInc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), + }, + .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP | + BYT_RT5640_DMIC_EN), + }, + {} +}; + +static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_card *card = runtime->card; + const struct snd_soc_dapm_route *custom_map; + int num_routes; + + card->dapm.idle_bias_off = true; + + ret = snd_soc_add_card_controls(card, byt_rt5640_controls, + ARRAY_SIZE(byt_rt5640_controls)); + if (ret) { + dev_err(card->dev, "unable to add card controls\n"); + return ret; + } + + dmi_check_system(byt_rt5640_quirk_table); + switch (BYT_RT5640_MAP(byt_rt5640_quirk)) { + case BYT_RT5640_IN1_MAP: + custom_map = byt_rt5640_intmic_in1_map; + num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map); + break; + case BYT_RT5640_DMIC2_MAP: + custom_map = byt_rt5640_intmic_dmic2_map; + num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map); + break; + default: + custom_map = byt_rt5640_intmic_dmic1_map; + num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map); + } + + ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes); + if (ret) + return ret; + + if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) { + ret = rt5640_dmic_enable(codec, 0, 0); + if (ret) + return ret; + } + + snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone"); + snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); + + return ret; +} + +static const struct snd_soc_pcm_stream byt_rt5640_dai_params = { .formats = SNDRV_PCM_FMTBIT_S24_LE, .rate_min = 48000, .rate_max = 48000, @@ -100,13 +199,14 @@ static const struct snd_soc_pcm_stream byt_dai_params = { .channels_max = 2, }; -static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd, +static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + int ret; /* The DSP will covert the FE rate to 48k, stereo, 24bits */ rate->min = rate->max = 48000; @@ -114,24 +214,46 @@ static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP2 to 24-bit */ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + + /* + * Default mode for SSP configuration is TDM 4 slot, override config + * with explicit setting to I2S 2ch 24-bit. The word length is set with + * dai_set_tdm_slot() since there is no other API exposed + */ + ret = snd_soc_dai_set_fmt(rtd->cpu_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBS_CFS + ); + if (ret < 0) { + dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24); + if (ret < 0) { + dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); + return ret; + } + return 0; } -static int byt_aif1_startup(struct snd_pcm_substream *substream) +static int byt_rt5640_aif1_startup(struct snd_pcm_substream *substream) { return snd_pcm_hw_constraint_single(substream->runtime, SNDRV_PCM_HW_PARAM_RATE, 48000); } -static struct snd_soc_ops byt_aif1_ops = { - .startup = byt_aif1_startup, +static struct snd_soc_ops byt_rt5640_aif1_ops = { + .startup = byt_rt5640_aif1_startup, }; -static struct snd_soc_ops byt_be_ssp2_ops = { - .hw_params = byt_aif1_hw_params, +static struct snd_soc_ops byt_rt5640_be_ssp2_ops = { + .hw_params = byt_rt5640_aif1_hw_params, }; -static struct snd_soc_dai_link byt_dailink[] = { +static struct snd_soc_dai_link byt_rt5640_dais[] = { [MERR_DPCM_AUDIO] = { .name = "Baytrail Audio Port", .stream_name = "Baytrail Audio", @@ -143,7 +265,20 @@ static struct snd_soc_dai_link byt_dailink[] = { .dynamic = 1, .dpcm_playback = 1, .dpcm_capture = 1, - .ops = &byt_aif1_ops, + .ops = &byt_rt5640_aif1_ops, + }, + [MERR_DPCM_DEEP_BUFFER] = { + .name = "Deep-Buffer Audio Port", + .stream_name = "Deep-Buffer Audio", + .cpu_dai_name = "deepbuffer-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-mfld-platform", + .ignore_suspend = 1, + .nonatomic = true, + .dynamic = 1, + .dpcm_playback = 1, + .ops = &byt_rt5640_aif1_ops, }, [MERR_DPCM_COMPR] = { .name = "Baytrail Compressed Port", @@ -164,55 +299,57 @@ static struct snd_soc_dai_link byt_dailink[] = { .codec_name = "i2c-10EC5640:00", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, - .be_hw_params_fixup = byt_codec_fixup, + .be_hw_params_fixup = byt_rt5640_codec_fixup, .ignore_suspend = 1, .dpcm_playback = 1, .dpcm_capture = 1, - .ops = &byt_be_ssp2_ops, + .init = byt_rt5640_init, + .ops = &byt_rt5640_be_ssp2_ops, }, }; /* SoC card */ -static struct snd_soc_card snd_soc_card_byt = { - .name = "baytrailcraudio", +static struct snd_soc_card byt_rt5640_card = { + .name = "bytcr-rt5640", .owner = THIS_MODULE, - .dai_link = byt_dailink, - .num_links = ARRAY_SIZE(byt_dailink), - .dapm_widgets = byt_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(byt_dapm_widgets), - .dapm_routes = byt_audio_map, - .num_dapm_routes = ARRAY_SIZE(byt_audio_map), - .controls = byt_mc_controls, - .num_controls = ARRAY_SIZE(byt_mc_controls), + .dai_link = byt_rt5640_dais, + .num_links = ARRAY_SIZE(byt_rt5640_dais), + .dapm_widgets = byt_rt5640_widgets, + .num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets), + .dapm_routes = byt_rt5640_audio_map, + .num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map), + .fully_routed = true, }; -static int snd_byt_mc_probe(struct platform_device *pdev) +static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) { int ret_val = 0; /* register the soc card */ - snd_soc_card_byt.dev = &pdev->dev; + byt_rt5640_card.dev = &pdev->dev; + + ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card); - ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_byt); if (ret_val) { - dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", ret_val); + dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", + ret_val); return ret_val; } - platform_set_drvdata(pdev, &snd_soc_card_byt); + platform_set_drvdata(pdev, &byt_rt5640_card); return ret_val; } -static struct platform_driver snd_byt_mc_driver = { +static struct platform_driver snd_byt_rt5640_mc_driver = { .driver = { - .name = "bytt100_rt5640", + .name = "bytcr_rt5640", .pm = &snd_soc_pm_ops, }, - .probe = snd_byt_mc_probe, + .probe = snd_byt_rt5640_mc_probe, }; -module_platform_driver(snd_byt_mc_driver); +module_platform_driver(snd_byt_rt5640_mc_driver); MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver"); MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:bytt100_rt5640"); +MODULE_ALIAS("platform:bytcr_rt5640"); diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index 4e2fcf188dd1..90588d6e64fc 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -41,12 +41,9 @@ struct cht_mc_private { static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) { - int i; + struct snd_soc_pcm_runtime *rtd; - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_pcm_runtime *rtd; - - rtd = card->rtd + i; + list_for_each_entry(rtd, &card->rtd_list, list) { if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI, strlen(CHT_CODEC_DAI))) return rtd->codec_dai; @@ -235,6 +232,18 @@ static struct snd_soc_dai_link cht_dailink[] = { .dpcm_capture = 1, .ops = &cht_aif1_ops, }, + [MERR_DPCM_DEEP_BUFFER] = { + .name = "Deep-Buffer Audio Port", + .stream_name = "Deep-Buffer Audio", + .cpu_dai_name = "deepbuffer-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-mfld-platform", + .nonatomic = true, + .dynamic = 1, + .dpcm_playback = 1, + .ops = &cht_aif1_ops, + }, [MERR_DPCM_COMPR] = { .name = "Compressed Port", .stream_name = "Compress", diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 38d65a3529c4..2d3afddb0a2e 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -47,12 +47,9 @@ struct cht_mc_private { static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) { - int i; - - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_pcm_runtime *rtd; + struct snd_soc_pcm_runtime *rtd; - rtd = card->rtd + i; + list_for_each_entry(rtd, &card->rtd_list, list) { if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI, strlen(CHT_CODEC_DAI))) return rtd->codec_dai; @@ -263,6 +260,18 @@ static struct snd_soc_dai_link cht_dailink[] = { .dpcm_capture = 1, .ops = &cht_aif1_ops, }, + [MERR_DPCM_DEEP_BUFFER] = { + .name = "Deep-Buffer Audio Port", + .stream_name = "Deep-Buffer Audio", + .cpu_dai_name = "deepbuffer-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-mfld-platform", + .nonatomic = true, + .dynamic = 1, + .dpcm_playback = 1, + .ops = &cht_aif1_ops, + }, [MERR_DPCM_COMPR] = { .name = "Compressed Port", .stream_name = "Compress", diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index 5621ccd92992..2e5347f8f96c 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -46,12 +46,9 @@ static struct snd_soc_jack_pin cht_bsw_headset_pins[] = { static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) { - int i; + struct snd_soc_pcm_runtime *rtd; - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_pcm_runtime *rtd; - - rtd = card->rtd + i; + list_for_each_entry(rtd, &card->rtd_list, list) { if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI, strlen(CHT_CODEC_DAI))) return rtd->codec_dai; @@ -251,6 +248,18 @@ static struct snd_soc_dai_link cht_dailink[] = { .dpcm_capture = 1, .ops = &cht_aif1_ops, }, + [MERR_DPCM_DEEP_BUFFER] = { + .name = "Deep-Buffer Audio Port", + .stream_name = "Deep-Buffer Audio", + .cpu_dai_name = "deepbuffer-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-mfld-platform", + .nonatomic = true, + .dynamic = 1, + .dpcm_playback = 1, + .ops = &cht_aif1_ops, + }, [MERR_DPCM_COMPR] = { .name = "Compressed Port", .stream_name = "Compress", diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c new file mode 100644 index 000000000000..ab7da9c304b2 --- /dev/null +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -0,0 +1,485 @@ +/* + * Intel Skylake I2S Machine Driver with MAXIM98357A + * and NAU88L25 + * + * Copyright (C) 2015, Intel Corporation. All rights reserved. + * + * 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/platform_device.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "../../codecs/nau8825.h" + +#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi" +#define SKL_MAXIM_CODEC_DAI "HiFi" + +static struct snd_soc_jack skylake_headset; +static struct snd_soc_card skylake_audio_card; + +static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + + if (!strncmp(rtd->codec_dai->name, SKL_NUVOTON_CODEC_DAI, + strlen(SKL_NUVOTON_CODEC_DAI))) + return rtd->codec_dai; + } + + return NULL; +} + +static int platform_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct snd_soc_dai *codec_dai; + int ret; + + codec_dai = skl_get_codec_dai(card); + if (!codec_dai) { + dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n"); + return -EIO; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + ret = snd_soc_dai_set_sysclk(codec_dai, + NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "set sysclk err = %d\n", ret); + return -EIO; + } + } else { + ret = snd_soc_dai_set_sysclk(codec_dai, + NAU8825_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "set sysclk err = %d\n", ret); + return -EIO; + } + } + + return ret; +} + +static const struct snd_kcontrol_new skylake_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Spk"), +}; + +static const struct snd_soc_dapm_widget skylake_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Spk", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SINK("WoV Sink"), + SND_SOC_DAPM_SPK("DP", NULL), + SND_SOC_DAPM_SPK("HDMI", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route skylake_map[] = { + /* HP jack connectors - unknown if we have jack detection */ + { "Headphone Jack", NULL, "HPOL" }, + { "Headphone Jack", NULL, "HPOR" }, + + /* speaker */ + { "Spk", NULL, "Speaker" }, + + /* other jacks */ + { "MIC", NULL, "Headset Mic" }, + { "DMic", NULL, "SoC DMIC" }, + + {"WoV Sink", NULL, "hwd_in sink"}, + {"HDMI", NULL, "hif5 Output"}, + {"DP", NULL, "hif6 Output"}, + + /* CODEC BE connections */ + { "HiFi Playback", NULL, "ssp0 Tx" }, + { "ssp0 Tx", NULL, "codec0_out" }, + + { "Playback", NULL, "ssp1 Tx" }, + { "ssp1 Tx", NULL, "codec1_out" }, + + { "codec0_in", NULL, "ssp1 Rx" }, + { "ssp1 Rx", NULL, "Capture" }, + + /* DMIC */ + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + { "DMIC01 Rx", NULL, "DMIC AIF" }, + { "hifi1", NULL, "iDisp Tx"}, + { "iDisp Tx", NULL, "iDisp_out"}, + { "Headphone Jack", NULL, "Platform Clock" }, + { "Headset Mic", NULL, "Platform Clock" }, +}; + +static int skylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The ADSP will covert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP0 to 24 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + struct snd_soc_codec *codec = rtd->codec; + + /* + * Headset buttons map to the google Reference headset. + * These can be configured by userspace. + */ + ret = snd_soc_card_jack_new(&skylake_audio_card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset, + NULL, 0); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret); + return ret; + } + + nau8825_enable_jack_detect(codec, &skylake_headset); + + snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); + snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink"); + + return ret; +} + +static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_component *component = rtd->cpu_dai->component; + + dapm = snd_soc_component_get_dapm(component); + snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); + + return 0; +} + +static unsigned int rates[] = { + 48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static unsigned int channels[] = { + 2, +}; + +static struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static int skl_fe_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + /* + * On this platform for PCM device we support, + * 48Khz + * stereo + * 16 bit audio + */ + + runtime->hw.channels_max = 2; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16); + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); + + return 0; +} + +static const struct snd_soc_ops skylake_nau8825_fe_ops = { + .startup = skl_fe_startup, +}; + +static int skylake_nau8825_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, + NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN); + + if (ret < 0) + dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret); + + return ret; +} + +static struct snd_soc_ops skylake_nau8825_ops = { + .hw_params = skylake_nau8825_hw_params, +}; + +static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + if (params_channels(params) == 2) + channels->min = channels->max = 2; + else + channels->min = channels->max = 4; + + return 0; +} + +static unsigned int channels_dmic[] = { + 2, 4, +}; + +static struct snd_pcm_hw_constraint_list constraints_dmic_channels = { + .count = ARRAY_SIZE(channels_dmic), + .list = channels_dmic, + .mask = 0, +}; + +static int skylake_dmic_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.channels_max = 4; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_dmic_channels); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); +} + +static struct snd_soc_ops skylake_dmic_ops = { + .startup = skylake_dmic_startup, +}; + +static unsigned int rates_16000[] = { + 16000, +}; + +static struct snd_pcm_hw_constraint_list constraints_16000 = { + .count = ARRAY_SIZE(rates_16000), + .list = rates_16000, +}; + +static int skylake_refcap_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_16000); +} + +static struct snd_soc_ops skylaye_refcap_ops = { + .startup = skylake_refcap_startup, +}; + +/* skylake digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link skylake_dais[] = { + /* Front End DAI links */ + { + .name = "Skl Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .init = skylake_nau8825_fe_init, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .ops = &skylake_nau8825_fe_ops, + }, + { + .name = "Skl Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + .ops = &skylake_nau8825_fe_ops, + }, + { + .name = "Skl Audio Reference cap", + .stream_name = "Wake on Voice", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &skylaye_refcap_ops, + }, + { + .name = "Skl Audio DMIC cap", + .stream_name = "dmiccap", + .cpu_dai_name = "DMIC Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &skylake_dmic_ops, + }, + { + .name = "Skl HDMI Port", + .stream_name = "Hdmi", + .cpu_dai_name = "HDMI Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .be_id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codec_name = "MX98357A:00", + .codec_dai_name = SKL_MAXIM_CODEC_DAI, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = skylake_ssp_fixup, + .dpcm_playback = 1, + }, + { + /* SSP1 - Codec */ + .name = "SSP1-Codec", + .be_id = 0, + .cpu_dai_name = "SSP1 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codec_name = "i2c-10508825:00", + .codec_dai_name = SKL_NUVOTON_CODEC_DAI, + .init = skylake_nau8825_codec_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = skylake_ssp_fixup, + .ops = &skylake_nau8825_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .be_id = 1, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:1f.3", + .be_hw_params_fixup = skylake_dmic_fixup, + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + .name = "iDisp", + .be_id = 3, + .cpu_dai_name = "iDisp Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +/* skylake audio machine driver for SPT + NAU88L25 */ +static struct snd_soc_card skylake_audio_card = { + .name = "sklnau8825max", + .owner = THIS_MODULE, + .dai_link = skylake_dais, + .num_links = ARRAY_SIZE(skylake_dais), + .controls = skylake_controls, + .num_controls = ARRAY_SIZE(skylake_controls), + .dapm_widgets = skylake_widgets, + .num_dapm_widgets = ARRAY_SIZE(skylake_widgets), + .dapm_routes = skylake_map, + .num_dapm_routes = ARRAY_SIZE(skylake_map), + .fully_routed = true, +}; + +static int skylake_audio_probe(struct platform_device *pdev) +{ + skylake_audio_card.dev = &pdev->dev; + + return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card); +} + +static struct platform_driver skylake_audio = { + .probe = skylake_audio_probe, + .driver = { + .name = "skl_nau88l25_max98357a_i2s", + .pm = &snd_soc_pm_ops, + }, +}; + +module_platform_driver(skylake_audio) + +/* Module information */ +MODULE_DESCRIPTION("Audio Machine driver-NAU88L25 & MAX98357A in I2S mode"); +MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:skl_nau88l25_max98357a_i2s"); diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c new file mode 100644 index 000000000000..c071812f31e5 --- /dev/null +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -0,0 +1,536 @@ +/* + * Intel Skylake I2S Machine Driver for NAU88L25+SSM4567 + * + * Copyright (C) 2015, Intel Corporation. All rights reserved. + * + * Modified from: + * Intel Skylake I2S Machine Driver for NAU88L25 and SSM4567 + * + * Copyright (C) 2015, Intel Corporation. All rights reserved. + * + * 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/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <sound/pcm_params.h> +#include "../../codecs/nau8825.h" + +#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi" +#define SKL_SSM_CODEC_DAI "ssm4567-hifi" + +static struct snd_soc_jack skylake_headset; +static struct snd_soc_card skylake_audio_card; + +static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + + if (!strncmp(rtd->codec_dai->name, SKL_NUVOTON_CODEC_DAI, + strlen(SKL_NUVOTON_CODEC_DAI))) + return rtd->codec_dai; + } + + return NULL; +} + +static const struct snd_kcontrol_new skylake_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Left Speaker"), + SOC_DAPM_PIN_SWITCH("Right Speaker"), +}; + +static int platform_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct snd_soc_dai *codec_dai; + int ret; + + codec_dai = skl_get_codec_dai(card); + if (!codec_dai) { + dev_err(card->dev, "Codec dai not found\n"); + return -EIO; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + ret = snd_soc_dai_set_sysclk(codec_dai, + NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "set sysclk err = %d\n", ret); + return -EIO; + } + } else { + ret = snd_soc_dai_set_sysclk(codec_dai, + NAU8825_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "set sysclk err = %d\n", ret); + return -EIO; + } + } + return ret; +} + +static const struct snd_soc_dapm_widget skylake_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Left Speaker", NULL), + SND_SOC_DAPM_SPK("Right Speaker", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SINK("WoV Sink"), + SND_SOC_DAPM_SPK("DP", NULL), + SND_SOC_DAPM_SPK("HDMI", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route skylake_map[] = { + /* HP jack connectors - unknown if we have jack detection */ + {"Headphone Jack", NULL, "HPOL"}, + {"Headphone Jack", NULL, "HPOR"}, + + /* speaker */ + {"Left Speaker", NULL, "Left OUT"}, + {"Right Speaker", NULL, "Right OUT"}, + + /* other jacks */ + {"MIC", NULL, "Headset Mic"}, + {"DMic", NULL, "SoC DMIC"}, + + {"WoV Sink", NULL, "hwd_in sink"}, + + {"HDMI", NULL, "hif5 Output"}, + {"DP", NULL, "hif6 Output"}, + /* CODEC BE connections */ + { "Left Playback", NULL, "ssp0 Tx"}, + { "Right Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec0_out"}, + + { "Playback", NULL, "ssp1 Tx"}, + { "ssp1 Tx", NULL, "codec1_out"}, + + { "codec0_in", NULL, "ssp1 Rx" }, + { "ssp1 Rx", NULL, "Capture" }, + + /* DMIC */ + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + { "DMIC01 Rx", NULL, "DMIC AIF" }, + { "hifi1", NULL, "iDisp Tx"}, + { "iDisp Tx", NULL, "iDisp_out"}, + { "Headphone Jack", NULL, "Platform Clock" }, + { "Headset Mic", NULL, "Platform Clock" }, +}; + +static struct snd_soc_codec_conf ssm4567_codec_conf[] = { + { + .dev_name = "i2c-INT343B:00", + .name_prefix = "Left", + }, + { + .dev_name = "i2c-INT343B:01", + .name_prefix = "Right", + }, +}; + +static struct snd_soc_dai_link_component ssm4567_codec_components[] = { + { /* Left */ + .name = "i2c-INT343B:00", + .dai_name = SKL_SSM_CODEC_DAI, + }, + { /* Right */ + .name = "i2c-INT343B:01", + .dai_name = SKL_SSM_CODEC_DAI, + }, +}; + +static int skylake_ssm4567_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + + /* Slot 1 for left */ + ret = snd_soc_dai_set_tdm_slot(rtd->codec_dais[0], 0x01, 0x01, 2, 48); + if (ret < 0) + return ret; + + /* Slot 2 for right */ + ret = snd_soc_dai_set_tdm_slot(rtd->codec_dais[1], 0x02, 0x02, 2, 48); + if (ret < 0) + return ret; + + return ret; +} + +static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + struct snd_soc_codec *codec = rtd->codec; + + /* + * 4 buttons here map to the google Reference headset + * The use of these buttons can be decided by the user space. + */ + ret = snd_soc_card_jack_new(&skylake_audio_card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset, + NULL, 0); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret); + return ret; + } + + nau8825_enable_jack_detect(codec, &skylake_headset); + + snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); + snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink"); + + return ret; +} + +static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_component *component = rtd->cpu_dai->component; + + dapm = snd_soc_component_get_dapm(component); + snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); + + return 0; +} + +static unsigned int rates[] = { + 48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static unsigned int channels[] = { + 2, +}; + +static struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static int skl_fe_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + /* + * on this platform for PCM device we support, + * 48Khz + * stereo + * 16 bit audio + */ + + runtime->hw.channels_max = 2; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16); + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); + + return 0; +} + +static const struct snd_soc_ops skylake_nau8825_fe_ops = { + .startup = skl_fe_startup, +}; + +static int skylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The ADSP will covert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP0 to 24 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + return 0; +} + +static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + if (params_channels(params) == 2) + channels->min = channels->max = 2; + else + channels->min = channels->max = 4; + + return 0; +} + +static int skylake_nau8825_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, + NAU8825_CLK_MCLK, 24000000, SND_SOC_CLOCK_IN); + + if (ret < 0) + dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret); + + return ret; +} + +static struct snd_soc_ops skylake_nau8825_ops = { + .hw_params = skylake_nau8825_hw_params, +}; + +static unsigned int channels_dmic[] = { + 2, 4, +}; + +static struct snd_pcm_hw_constraint_list constraints_dmic_channels = { + .count = ARRAY_SIZE(channels_dmic), + .list = channels_dmic, + .mask = 0, +}; + +static int skylake_dmic_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.channels_max = 4; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_dmic_channels); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); +} + +static struct snd_soc_ops skylake_dmic_ops = { + .startup = skylake_dmic_startup, +}; + +static unsigned int rates_16000[] = { + 16000, +}; + +static struct snd_pcm_hw_constraint_list constraints_16000 = { + .count = ARRAY_SIZE(rates_16000), + .list = rates_16000, +}; + +static int skylake_refcap_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_16000); +} + +static struct snd_soc_ops skylaye_refcap_ops = { + .startup = skylake_refcap_startup, +}; + +/* skylake digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link skylake_dais[] = { + /* Front End DAI links */ + { + .name = "Skl Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .init = skylake_nau8825_fe_init, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .ops = &skylake_nau8825_fe_ops, + }, + { + .name = "Skl Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + .ops = &skylake_nau8825_fe_ops, + }, + { + .name = "Skl Audio Reference cap", + .stream_name = "Wake on Voice", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &skylaye_refcap_ops, + }, + { + .name = "Skl Audio DMIC cap", + .stream_name = "dmiccap", + .cpu_dai_name = "DMIC Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &skylake_dmic_ops, + }, + { + .name = "Skl HDMI Port", + .stream_name = "Hdmi", + .cpu_dai_name = "HDMI Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .be_id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codecs = ssm4567_codec_components, + .num_codecs = ARRAY_SIZE(ssm4567_codec_components), + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .init = skylake_ssm4567_codec_init, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = skylake_ssp_fixup, + .dpcm_playback = 1, + }, + { + /* SSP1 - Codec */ + .name = "SSP1-Codec", + .be_id = 0, + .cpu_dai_name = "SSP1 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codec_name = "i2c-10508825:00", + .codec_dai_name = SKL_NUVOTON_CODEC_DAI, + .init = skylake_nau8825_codec_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = skylake_ssp_fixup, + .ops = &skylake_nau8825_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .be_id = 1, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:1f.3", + .ignore_suspend = 1, + .be_hw_params_fixup = skylake_dmic_fixup, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + .name = "iDisp", + .be_id = 3, + .cpu_dai_name = "iDisp Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +/* skylake audio machine driver for SPT + NAU88L25 */ +static struct snd_soc_card skylake_audio_card = { + .name = "sklnau8825adi", + .owner = THIS_MODULE, + .dai_link = skylake_dais, + .num_links = ARRAY_SIZE(skylake_dais), + .controls = skylake_controls, + .num_controls = ARRAY_SIZE(skylake_controls), + .dapm_widgets = skylake_widgets, + .num_dapm_widgets = ARRAY_SIZE(skylake_widgets), + .dapm_routes = skylake_map, + .num_dapm_routes = ARRAY_SIZE(skylake_map), + .codec_conf = ssm4567_codec_conf, + .num_configs = ARRAY_SIZE(ssm4567_codec_conf), + .fully_routed = true, +}; + +static int skylake_audio_probe(struct platform_device *pdev) +{ + skylake_audio_card.dev = &pdev->dev; + + return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card); +} + +static struct platform_driver skylake_audio = { + .probe = skylake_audio_probe, + .driver = { + .name = "skl_nau88l25_ssm4567_i2s", + .pm = &snd_soc_pm_ops, + }, +}; + +module_platform_driver(skylake_audio) + +/* Module information */ +MODULE_AUTHOR("Conrad Cooke <conrad.cooke@intel.com>"); +MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>"); +MODULE_AUTHOR("Naveen M <naveen.m@intel.com>"); +MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>"); +MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>"); +MODULE_DESCRIPTION("Intel Audio Machine driver for SKL with NAU88L25 and SSM4567 in I2S Mode"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:skl_nau88l25_ssm4567_i2s"); diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index a73a431bd8b7..7396ddb427d8 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -52,6 +52,7 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = { SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_MIC("DMIC2", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SINK("WoV Sink"), }; static const struct snd_soc_dapm_route skylake_rt286_map[] = { @@ -67,7 +68,9 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = { /* digital mics */ {"DMIC1 Pin", NULL, "DMIC2"}, - {"DMIC AIF", NULL, "SoC DMIC"}, + {"DMic", NULL, "SoC DMIC"}, + + {"WoV Sink", NULL, "hwd_in sink"}, /* CODEC BE connections */ { "AIF1 Playback", NULL, "ssp0 Tx"}, @@ -79,13 +82,24 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = { { "ssp0 Rx", NULL, "AIF1 Capture" }, { "dmic01_hifi", NULL, "DMIC01 Rx" }, - { "DMIC01 Rx", NULL, "Capture" }, + { "DMIC01 Rx", NULL, "DMIC AIF" }, { "hif1", NULL, "iDisp Tx"}, { "iDisp Tx", NULL, "iDisp_out"}, }; +static int skylake_rt286_fe_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_component *component = rtd->cpu_dai->component; + + dapm = snd_soc_component_get_dapm(component); + snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); + + return 0; +} + static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; @@ -101,9 +115,59 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) rt286_mic_detect(codec, &skylake_headset); + snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); + snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink"); + return 0; } +static unsigned int rates[] = { + 48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static unsigned int channels[] = { + 2, +}; + +static struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static int skl_fe_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + /* + * on this platform for PCM device we support, + * 48Khz + * stereo + * 16 bit audio + */ + + runtime->hw.channels_max = 2; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16); + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); + + return 0; +} + +static const struct snd_soc_ops skylake_rt286_fe_ops = { + .startup = skl_fe_startup, +}; static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) @@ -112,12 +176,15 @@ static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); /* The output is 48KHz, stereo, 16bits */ rate->min = rate->max = 48000; channels->min = channels->max = 2; - params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); + /* set SSP0 to 24 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); return 0; } @@ -140,6 +207,42 @@ static struct snd_soc_ops skylake_rt286_ops = { .hw_params = skylake_rt286_hw_params, }; +static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = 4; + + return 0; +} + +static unsigned int channels_dmic[] = { + 2, 4, +}; + +static struct snd_pcm_hw_constraint_list constraints_dmic_channels = { + .count = ARRAY_SIZE(channels_dmic), + .list = channels_dmic, + .mask = 0, +}; + +static int skylake_dmic_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.channels_max = 4; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_dmic_channels); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); +} + +static struct snd_soc_ops skylake_dmic_ops = { + .startup = skylake_dmic_startup, +}; + /* skylake digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link skylake_rt286_dais[] = { /* Front End DAI links */ @@ -152,11 +255,13 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .dynamic = 1, .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", + .init = skylake_rt286_fe_init, .trigger = { SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST }, .dpcm_playback = 1, + .ops = &skylake_rt286_fe_ops, }, { .name = "Skl Audio Capture Port", @@ -172,6 +277,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { SND_SOC_DPCM_TRIGGER_POST }, .dpcm_capture = 1, + .ops = &skylake_rt286_fe_ops, }, { .name = "Skl Audio Reference cap", @@ -186,6 +292,19 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .nonatomic = 1, .dynamic = 1, }, + { + .name = "Skl Audio DMIC cap", + .stream_name = "dmiccap", + .cpu_dai_name = "DMIC Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &skylake_dmic_ops, + }, /* Back End DAI links */ { @@ -201,7 +320,6 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, - .ignore_suspend = 1, .ignore_pmdown_time = 1, .be_hw_params_fixup = skylake_ssp0_fixup, .ops = &skylake_rt286_ops, @@ -215,6 +333,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", .platform_name = "0000:00:1f.3", + .be_hw_params_fixup = skylake_dmic_fixup, .ignore_suspend = 1, .dpcm_capture = 1, .no_pcm = 1, @@ -247,6 +366,7 @@ static struct platform_driver skylake_audio = { .probe = skylake_audio_probe, .driver = { .name = "skl_alc286s_i2s", + .pm = &snd_soc_pm_ops, }, }; diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index d9105584c51f..3b9332e7a094 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -1,11 +1,8 @@ snd-soc-sst-dsp-objs := sst-dsp.o -snd-soc-sst-acpi-objs := sst-acpi.o +snd-soc-sst-acpi-objs := sst-acpi.o sst-match-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o -ifneq ($(CONFIG_DW_DMAC_CORE),) -snd-soc-sst-dsp-objs += sst-firmware.o -endif +snd-soc-sst-dsp-$(CONFIG_DW_DMAC_CORE) += sst-firmware.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o - diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c index 67b6d3d52f57..7a85c576dad3 100644 --- a/sound/soc/intel/common/sst-acpi.c +++ b/sound/soc/intel/common/sst-acpi.c @@ -21,21 +21,12 @@ #include <linux/platform_device.h> #include "sst-dsp.h" +#include "sst-acpi.h" #define SST_LPT_DSP_DMA_ADDR_OFFSET 0x0F0000 #define SST_WPT_DSP_DMA_ADDR_OFFSET 0x0FE000 #define SST_LPT_DSP_DMA_SIZE (1024 - 1) -/* Descriptor for SST ASoC machine driver */ -struct sst_acpi_mach { - /* ACPI ID for the matching machine driver. Audio codec for instance */ - const u8 id[ACPI_ID_LEN]; - /* machine driver name */ - const char *drv_name; - /* firmware file name */ - const char *fw_filename; -}; - /* Descriptor for setting up SST platform data */ struct sst_acpi_desc { const char *drv_name; @@ -88,28 +79,6 @@ static void sst_acpi_fw_cb(const struct firmware *fw, void *context) return; } -static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, - void *context, void **ret) -{ - *(bool *)context = true; - return AE_OK; -} - -static struct sst_acpi_mach *sst_acpi_find_machine( - struct sst_acpi_mach *machines) -{ - struct sst_acpi_mach *mach; - bool found = false; - - for (mach = machines; mach->id[0]; mach++) - if (ACPI_SUCCESS(acpi_get_devices(mach->id, - sst_acpi_mach_match, - &found, NULL)) && found) - return mach; - - return NULL; -} - static int sst_acpi_probe(struct platform_device *pdev) { const struct acpi_device_id *id; @@ -211,7 +180,7 @@ static int sst_acpi_remove(struct platform_device *pdev) } static struct sst_acpi_mach haswell_machines[] = { - { "INT33CA", "haswell-audio", "intel/IntcSST1.bin" }, + { "INT33CA", "haswell-audio", "intel/IntcSST1.bin", NULL, NULL, NULL }, {} }; @@ -229,7 +198,7 @@ static struct sst_acpi_desc sst_acpi_haswell_desc = { }; static struct sst_acpi_mach broadwell_machines[] = { - { "INT343A", "broadwell-audio", "intel/IntcSST2.bin" }, + { "INT343A", "broadwell-audio", "intel/IntcSST2.bin", NULL, NULL, NULL }, {} }; @@ -247,8 +216,8 @@ static struct sst_acpi_desc sst_acpi_broadwell_desc = { }; static struct sst_acpi_mach baytrail_machines[] = { - { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master" }, - { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master" }, + { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL }, + { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL }, {} }; diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h new file mode 100644 index 000000000000..3ee3b7ab5d03 --- /dev/null +++ b/sound/soc/intel/common/sst-acpi.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013-15, Intel Corporation. All rights reserved. + * + * 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/acpi.h> + +/* acpi match */ +struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines); + +/* Descriptor for SST ASoC machine driver */ +struct sst_acpi_mach { + /* ACPI ID for the matching machine driver. Audio codec for instance */ + const u8 id[ACPI_ID_LEN]; + /* machine driver name */ + const char *drv_name; + /* firmware file name */ + const char *fw_filename; + + /* board name */ + const char *board; + void (*machine_quirk)(void); + void *pdata; +}; diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 2151652d37b7..81aa1ed64201 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -243,7 +243,7 @@ struct sst_mem_block { u32 size; /* block size */ u32 index; /* block index 0..N */ enum sst_mem_type type; /* block memory type IRAM/DRAM */ - struct sst_block_ops *ops; /* block operations, if any */ + const struct sst_block_ops *ops;/* block operations, if any */ /* block status */ u32 bytes_used; /* bytes in use by modules */ @@ -308,6 +308,8 @@ struct sst_dsp { /* SKL data */ + const char *fw_name; + /* To allocate CL dma buffers */ struct skl_dsp_loader_ops dsp_ops; struct skl_dsp_fw_ops fw_ops; @@ -376,8 +378,8 @@ void sst_block_free_scratch(struct sst_dsp *dsp); /* Register the DSPs memory blocks - would be nice to read from ACPI */ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset, - u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index, - void *private); + u32 size, enum sst_mem_type type, const struct sst_block_ops *ops, + u32 index, void *private); void sst_mem_block_unregister_all(struct sst_dsp *dsp); /* Create/Free DMA resources */ diff --git a/sound/soc/intel/common/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c index c9452e02e0dd..b5bbdf4fe93a 100644 --- a/sound/soc/intel/common/sst-dsp.c +++ b/sound/soc/intel/common/sst-dsp.c @@ -420,7 +420,7 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes) } EXPORT_SYMBOL_GPL(sst_dsp_inbox_read); -#if IS_ENABLED(CONFIG_DW_DMAC_CORE) +#ifdef CONFIG_DW_DMAC_CORE struct sst_dsp *sst_dsp_new(struct device *dev, struct sst_dsp_device *sst_dev, struct sst_pdata *pdata) { diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index 859f0de00339..0b84c719ec48 100644 --- a/sound/soc/intel/common/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -216,7 +216,7 @@ struct sst_pdata { void *dsp; }; -#if IS_ENABLED(CONFIG_DW_DMAC_CORE) +#ifdef CONFIG_DW_DMAC_CORE /* Initialization */ struct sst_dsp *sst_dsp_new(struct device *dev, struct sst_dsp_device *sst_dev, struct sst_pdata *pdata); diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c index 1636a1eeb002..ef4881e7753a 100644 --- a/sound/soc/intel/common/sst-firmware.c +++ b/sound/soc/intel/common/sst-firmware.c @@ -51,8 +51,22 @@ struct sst_dma { static inline void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes) { + u32 tmp = 0; + int i, m, n; + const u8 *src_byte = src; + + m = bytes / 4; + n = bytes % 4; + /* __iowrite32_copy use 32bit size values so divide by 4 */ - __iowrite32_copy((void *)dest, src, bytes/4); + __iowrite32_copy((void *)dest, src, m); + + if (n) { + for (i = 0; i < n; i++) + tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); + __iowrite32_copy((void *)(dest + m * 4), &tmp, 1); + } + } static void sst_dma_transfer_complete(void *arg) @@ -1014,8 +1028,8 @@ EXPORT_SYMBOL_GPL(sst_module_runtime_restore); /* register a DSP memory block for use with FW based modules */ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset, - u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index, - void *private) + u32 size, enum sst_mem_type type, const struct sst_block_ops *ops, + u32 index, void *private) { struct sst_mem_block *block; diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/intel/common/sst-match-acpi.c new file mode 100644 index 000000000000..dd077e116d25 --- /dev/null +++ b/sound/soc/intel/common/sst-match-acpi.c @@ -0,0 +1,43 @@ +/* + * sst_match_apci.c - SST (LPE) match for ACPI enumeration. + * + * Copyright (c) 2013-15, Intel Corporation. + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/acpi.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "sst-acpi.h" + +static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, + void *context, void **ret) +{ + *(bool *)context = true; + return AE_OK; +} + +struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines) +{ + struct sst_acpi_mach *mach; + bool found = false; + + for (mach = machines; mach->id[0]; mach++) + if (ACPI_SUCCESS(acpi_get_devices(mach->id, + sst_acpi_mach_match, + &found, NULL)) && found) + return mach; + + return NULL; +} +EXPORT_SYMBOL_GPL(sst_acpi_find_machine); diff --git a/sound/soc/intel/haswell/sst-haswell-dsp.c b/sound/soc/intel/haswell/sst-haswell-dsp.c index 7f94920c8a4d..b2bec36d074c 100644 --- a/sound/soc/intel/haswell/sst-haswell-dsp.c +++ b/sound/soc/intel/haswell/sst-haswell-dsp.c @@ -607,7 +607,7 @@ static int hsw_block_disable(struct sst_mem_block *block) return 0; } -static struct sst_block_ops sst_hsw_ops = { +static const struct sst_block_ops sst_hsw_ops = { .enable = hsw_block_enable, .disable = hsw_block_disable, }; diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c index b27f25f70730..ac60f1301e21 100644 --- a/sound/soc/intel/haswell/sst-haswell-ipc.c +++ b/sound/soc/intel/haswell/sst-haswell-ipc.c @@ -778,7 +778,6 @@ static irqreturn_t hsw_irq_thread(int irq, void *context) struct sst_hsw *hsw = sst_dsp_get_thread_context(sst); struct sst_generic_ipc *ipc = &hsw->ipc; u32 ipcx, ipcd; - int handled; unsigned long flags; spin_lock_irqsave(&sst->spinlock, flags); @@ -790,34 +789,30 @@ static irqreturn_t hsw_irq_thread(int irq, void *context) if (ipcx & SST_IPCX_DONE) { /* Handle Immediate reply from DSP Core */ - handled = hsw_process_reply(hsw, ipcx); + hsw_process_reply(hsw, ipcx); - if (handled > 0) { - /* clear DONE bit - tell DSP we have completed */ - sst_dsp_shim_update_bits_unlocked(sst, SST_IPCX, - SST_IPCX_DONE, 0); + /* clear DONE bit - tell DSP we have completed */ + sst_dsp_shim_update_bits_unlocked(sst, SST_IPCX, + SST_IPCX_DONE, 0); - /* unmask Done interrupt */ - sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, - SST_IMRX_DONE, 0); - } + /* unmask Done interrupt */ + sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, + SST_IMRX_DONE, 0); } /* new message from DSP */ if (ipcd & SST_IPCD_BUSY) { /* Handle Notification and Delayed reply from DSP Core */ - handled = hsw_process_notification(hsw); + hsw_process_notification(hsw); /* clear BUSY bit and set DONE bit - accept new messages */ - if (handled > 0) { - sst_dsp_shim_update_bits_unlocked(sst, SST_IPCD, - SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE); + sst_dsp_shim_update_bits_unlocked(sst, SST_IPCD, + SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE); - /* unmask busy interrupt */ - sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, - SST_IMRX_BUSY, 0); - } + /* unmask busy interrupt */ + sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, + SST_IMRX_BUSY, 0); } spin_unlock_irqrestore(&sst->spinlock, flags); diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 50a109503a3f..de6dac496a0d 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -96,7 +96,7 @@ int skl_init_dsp(struct skl *skl) } ret = skl_sst_dsp_init(bus->dev, mmio_base, irq, - loader_ops, &skl->skl_sst); + skl->fw_name, loader_ops, &skl->skl_sst); if (ret < 0) return ret; @@ -182,94 +182,6 @@ enum skl_bitdepth skl_get_bit_depth(int params) } } -static u32 skl_create_channel_map(enum skl_ch_cfg ch_cfg) -{ - u32 config; - - switch (ch_cfg) { - case SKL_CH_CFG_MONO: - config = (0xFFFFFFF0 | SKL_CHANNEL_LEFT); - break; - - case SKL_CH_CFG_STEREO: - config = (0xFFFFFF00 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_RIGHT << 4)); - break; - - case SKL_CH_CFG_2_1: - config = (0xFFFFF000 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_RIGHT << 4) - | (SKL_CHANNEL_LFE << 8)); - break; - - case SKL_CH_CFG_3_0: - config = (0xFFFFF000 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_CENTER << 4) - | (SKL_CHANNEL_RIGHT << 8)); - break; - - case SKL_CH_CFG_3_1: - config = (0xFFFF0000 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_CENTER << 4) - | (SKL_CHANNEL_RIGHT << 8) - | (SKL_CHANNEL_LFE << 12)); - break; - - case SKL_CH_CFG_QUATRO: - config = (0xFFFF0000 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_RIGHT << 4) - | (SKL_CHANNEL_LEFT_SURROUND << 8) - | (SKL_CHANNEL_RIGHT_SURROUND << 12)); - break; - - case SKL_CH_CFG_4_0: - config = (0xFFFF0000 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_CENTER << 4) - | (SKL_CHANNEL_RIGHT << 8) - | (SKL_CHANNEL_CENTER_SURROUND << 12)); - break; - - case SKL_CH_CFG_5_0: - config = (0xFFF00000 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_CENTER << 4) - | (SKL_CHANNEL_RIGHT << 8) - | (SKL_CHANNEL_LEFT_SURROUND << 12) - | (SKL_CHANNEL_RIGHT_SURROUND << 16)); - break; - - case SKL_CH_CFG_5_1: - config = (0xFF000000 | SKL_CHANNEL_CENTER - | (SKL_CHANNEL_LEFT << 4) - | (SKL_CHANNEL_RIGHT << 8) - | (SKL_CHANNEL_LEFT_SURROUND << 12) - | (SKL_CHANNEL_RIGHT_SURROUND << 16) - | (SKL_CHANNEL_LFE << 20)); - break; - - case SKL_CH_CFG_DUAL_MONO: - config = (0xFFFFFF00 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_LEFT << 4)); - break; - - case SKL_CH_CFG_I2S_DUAL_STEREO_0: - config = (0xFFFFFF00 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_RIGHT << 4)); - break; - - case SKL_CH_CFG_I2S_DUAL_STEREO_1: - config = (0xFFFF00FF | (SKL_CHANNEL_LEFT << 8) - | (SKL_CHANNEL_RIGHT << 12)); - break; - - default: - config = 0xFFFFFFFF; - break; - - } - - return config; -} - /* * Each module in DSP expects a base module configuration, which consists of * PCM format information, which we calculate in driver and resource values @@ -280,7 +192,7 @@ static void skl_set_base_module_format(struct skl_sst *ctx, struct skl_module_cfg *mconfig, struct skl_base_cfg *base_cfg) { - struct skl_module_fmt *format = &mconfig->in_fmt; + struct skl_module_fmt *format = &mconfig->in_fmt[0]; base_cfg->audio_fmt.number_of_channels = (u8)format->channels; @@ -293,14 +205,14 @@ static void skl_set_base_module_format(struct skl_sst *ctx, format->bit_depth, format->valid_bit_depth, format->ch_cfg); - base_cfg->audio_fmt.channel_map = skl_create_channel_map( - base_cfg->audio_fmt.ch_cfg); + base_cfg->audio_fmt.channel_map = format->ch_map; - base_cfg->audio_fmt.interleaving = SKL_INTERLEAVING_PER_CHANNEL; + base_cfg->audio_fmt.interleaving = format->interleaving_style; base_cfg->cps = mconfig->mcps; base_cfg->ibs = mconfig->ibs; base_cfg->obs = mconfig->obs; + base_cfg->is_pages = mconfig->mem_pages; } /* @@ -399,7 +311,7 @@ static void skl_setup_out_format(struct skl_sst *ctx, struct skl_module_cfg *mconfig, struct skl_audio_data_format *out_fmt) { - struct skl_module_fmt *format = &mconfig->out_fmt; + struct skl_module_fmt *format = &mconfig->out_fmt[0]; out_fmt->number_of_channels = (u8)format->channels; out_fmt->s_freq = format->s_freq; @@ -407,8 +319,9 @@ static void skl_setup_out_format(struct skl_sst *ctx, out_fmt->valid_bit_depth = format->valid_bit_depth; out_fmt->ch_cfg = format->ch_cfg; - out_fmt->channel_map = skl_create_channel_map(out_fmt->ch_cfg); - out_fmt->interleaving = SKL_INTERLEAVING_PER_CHANNEL; + out_fmt->channel_map = format->ch_map; + out_fmt->interleaving = format->interleaving_style; + out_fmt->sample_type = format->sample_type; dev_dbg(ctx->dev, "copier out format chan=%d fre=%d bitdepth=%d\n", out_fmt->number_of_channels, format->s_freq, format->bit_depth); @@ -423,7 +336,7 @@ static void skl_set_src_format(struct skl_sst *ctx, struct skl_module_cfg *mconfig, struct skl_src_module_cfg *src_mconfig) { - struct skl_module_fmt *fmt = &mconfig->out_fmt; + struct skl_module_fmt *fmt = &mconfig->out_fmt[0]; skl_set_base_module_format(ctx, mconfig, (struct skl_base_cfg *)src_mconfig); @@ -440,7 +353,7 @@ static void skl_set_updown_mixer_format(struct skl_sst *ctx, struct skl_module_cfg *mconfig, struct skl_up_down_mixer_cfg *mixer_mconfig) { - struct skl_module_fmt *fmt = &mconfig->out_fmt; + struct skl_module_fmt *fmt = &mconfig->out_fmt[0]; int i = 0; skl_set_base_module_format(ctx, mconfig, @@ -475,6 +388,47 @@ static void skl_set_copier_format(struct skl_sst *ctx, skl_setup_cpr_gateway_cfg(ctx, mconfig, cpr_mconfig); } +/* + * Algo module are DSP pre processing modules. Algo module take base module + * configuration and params + */ + +static void skl_set_algo_format(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, + struct skl_algo_cfg *algo_mcfg) +{ + struct skl_base_cfg *base_cfg = (struct skl_base_cfg *)algo_mcfg; + + skl_set_base_module_format(ctx, mconfig, base_cfg); + + if (mconfig->formats_config.caps_size == 0) + return; + + memcpy(algo_mcfg->params, + mconfig->formats_config.caps, + mconfig->formats_config.caps_size); + +} + +/* + * Mic select module allows selecting one or many input channels, thus + * acting as a demux. + * + * Mic select module take base module configuration and out-format + * configuration + */ +static void skl_set_base_outfmt_format(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, + struct skl_base_outfmt_cfg *base_outfmt_mcfg) +{ + struct skl_audio_data_format *out_fmt = &base_outfmt_mcfg->out_fmt; + struct skl_base_cfg *base_cfg = + (struct skl_base_cfg *)base_outfmt_mcfg; + + skl_set_base_module_format(ctx, mconfig, base_cfg); + skl_setup_out_format(ctx, mconfig, out_fmt); +} + static u16 skl_get_module_param_size(struct skl_sst *ctx, struct skl_module_cfg *mconfig) { @@ -492,6 +446,14 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, case SKL_MODULE_TYPE_UPDWMIX: return sizeof(struct skl_up_down_mixer_cfg); + case SKL_MODULE_TYPE_ALGO: + param_size = sizeof(struct skl_base_cfg); + param_size += mconfig->formats_config.caps_size; + return param_size; + + case SKL_MODULE_TYPE_BASE_OUTFMT: + return sizeof(struct skl_base_outfmt_cfg); + default: /* * return only base cfg when no specific module type is @@ -538,6 +500,14 @@ static int skl_set_module_format(struct skl_sst *ctx, skl_set_updown_mixer_format(ctx, module_config, *param_data); break; + case SKL_MODULE_TYPE_ALGO: + skl_set_algo_format(ctx, module_config, *param_data); + break; + + case SKL_MODULE_TYPE_BASE_OUTFMT: + skl_set_base_outfmt_format(ctx, module_config, *param_data); + break; + default: skl_set_base_module_format(ctx, module_config, *param_data); break; @@ -571,10 +541,10 @@ static int skl_get_queue_index(struct skl_module_pin *mpin, * In static, the pin_index is fixed based on module_id and instance id */ static int skl_alloc_queue(struct skl_module_pin *mpin, - struct skl_module_inst_id id, int max) + struct skl_module_cfg *tgt_cfg, int max) { int i; - + struct skl_module_inst_id id = tgt_cfg->id; /* * if pin in dynamic, find first free pin * otherwise find match module and instance id pin as topology will @@ -583,16 +553,23 @@ static int skl_alloc_queue(struct skl_module_pin *mpin, */ for (i = 0; i < max; i++) { if (mpin[i].is_dynamic) { - if (!mpin[i].in_use) { + if (!mpin[i].in_use && + mpin[i].pin_state == SKL_PIN_UNBIND) { + mpin[i].in_use = true; mpin[i].id.module_id = id.module_id; mpin[i].id.instance_id = id.instance_id; + mpin[i].tgt_mcfg = tgt_cfg; return i; } } else { if (mpin[i].id.module_id == id.module_id && - mpin[i].id.instance_id == id.instance_id) + mpin[i].id.instance_id == id.instance_id && + mpin[i].pin_state == SKL_PIN_UNBIND) { + + mpin[i].tgt_mcfg = tgt_cfg; return i; + } } } @@ -606,6 +583,28 @@ static void skl_free_queue(struct skl_module_pin *mpin, int q_index) mpin[q_index].id.module_id = 0; mpin[q_index].id.instance_id = 0; } + mpin[q_index].pin_state = SKL_PIN_UNBIND; + mpin[q_index].tgt_mcfg = NULL; +} + +/* Module state will be set to unint, if all the out pin state is UNBIND */ + +static void skl_clear_module_state(struct skl_module_pin *mpin, int max, + struct skl_module_cfg *mcfg) +{ + int i; + bool found = false; + + for (i = 0; i < max; i++) { + if (mpin[i].pin_state == SKL_PIN_UNBIND) + continue; + found = true; + break; + } + + if (!found) + mcfg->m_state = SKL_MODULE_UNINIT; + return; } /* @@ -615,7 +614,7 @@ static void skl_free_queue(struct skl_module_pin *mpin, int q_index) * invoke the DSP by sending IPC INIT_INSTANCE using ipc helper */ int skl_init_module(struct skl_sst *ctx, - struct skl_module_cfg *mconfig, char *param) + struct skl_module_cfg *mconfig) { u16 module_config_size = 0; void *param_data = NULL; @@ -682,37 +681,30 @@ int skl_unbind_modules(struct skl_sst *ctx, struct skl_module_inst_id dst_id = dst_mcfg->id; int in_max = dst_mcfg->max_in_queue; int out_max = src_mcfg->max_out_queue; - int src_index, dst_index; + int src_index, dst_index, src_pin_state, dst_pin_state; skl_dump_bind_info(ctx, src_mcfg, dst_mcfg); - if (src_mcfg->m_state != SKL_MODULE_BIND_DONE) - return 0; - - /* - * if intra module unbind, check if both modules are BIND, - * then send unbind - */ - if ((src_mcfg->pipe->ppl_id != dst_mcfg->pipe->ppl_id) && - dst_mcfg->m_state != SKL_MODULE_BIND_DONE) - return 0; - else if (src_mcfg->m_state < SKL_MODULE_INIT_DONE && - dst_mcfg->m_state < SKL_MODULE_INIT_DONE) - return 0; - /* get src queue index */ src_index = skl_get_queue_index(src_mcfg->m_out_pin, dst_id, out_max); if (src_index < 0) return -EINVAL; - msg.src_queue = src_mcfg->m_out_pin[src_index].pin_index; + msg.src_queue = src_index; /* get dst queue index */ dst_index = skl_get_queue_index(dst_mcfg->m_in_pin, src_id, in_max); if (dst_index < 0) return -EINVAL; - msg.dst_queue = dst_mcfg->m_in_pin[dst_index].pin_index; + msg.dst_queue = dst_index; + + src_pin_state = src_mcfg->m_out_pin[src_index].pin_state; + dst_pin_state = dst_mcfg->m_in_pin[dst_index].pin_state; + + if (src_pin_state != SKL_PIN_BIND_DONE || + dst_pin_state != SKL_PIN_BIND_DONE) + return 0; msg.module_id = src_mcfg->id.module_id; msg.instance_id = src_mcfg->id.instance_id; @@ -722,10 +714,15 @@ int skl_unbind_modules(struct skl_sst *ctx, ret = skl_ipc_bind_unbind(&ctx->ipc, &msg); if (!ret) { - src_mcfg->m_state = SKL_MODULE_UNINIT; /* free queue only if unbind is success */ skl_free_queue(src_mcfg->m_out_pin, src_index); skl_free_queue(dst_mcfg->m_in_pin, dst_index); + + /* + * check only if src module bind state, bind is + * always from src -> sink + */ + skl_clear_module_state(src_mcfg->m_out_pin, out_max, src_mcfg); } return ret; @@ -744,8 +741,6 @@ int skl_bind_modules(struct skl_sst *ctx, { int ret; struct skl_ipc_bind_unbind_msg msg; - struct skl_module_inst_id src_id = src_mcfg->id; - struct skl_module_inst_id dst_id = dst_mcfg->id; int in_max = dst_mcfg->max_in_queue; int out_max = src_mcfg->max_out_queue; int src_index, dst_index; @@ -756,18 +751,18 @@ int skl_bind_modules(struct skl_sst *ctx, dst_mcfg->m_state < SKL_MODULE_INIT_DONE) return 0; - src_index = skl_alloc_queue(src_mcfg->m_out_pin, dst_id, out_max); + src_index = skl_alloc_queue(src_mcfg->m_out_pin, dst_mcfg, out_max); if (src_index < 0) return -EINVAL; - msg.src_queue = src_mcfg->m_out_pin[src_index].pin_index; - dst_index = skl_alloc_queue(dst_mcfg->m_in_pin, src_id, in_max); + msg.src_queue = src_index; + dst_index = skl_alloc_queue(dst_mcfg->m_in_pin, src_mcfg, in_max); if (dst_index < 0) { skl_free_queue(src_mcfg->m_out_pin, src_index); return -EINVAL; } - msg.dst_queue = dst_mcfg->m_in_pin[dst_index].pin_index; + msg.dst_queue = dst_index; dev_dbg(ctx->dev, "src queue = %d dst queue =%d\n", msg.src_queue, msg.dst_queue); @@ -782,6 +777,8 @@ int skl_bind_modules(struct skl_sst *ctx, if (!ret) { src_mcfg->m_state = SKL_MODULE_BIND_DONE; + src_mcfg->m_out_pin[src_index].pin_state = SKL_PIN_BIND_DONE; + dst_mcfg->m_in_pin[dst_index].pin_state = SKL_PIN_BIND_DONE; } else { /* error case , if IPC fails, clear the queue index */ skl_free_queue(src_mcfg->m_out_pin, src_index); @@ -852,6 +849,8 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id); if (ret < 0) dev_err(ctx->dev, "Failed to delete pipeline\n"); + + pipe->state = SKL_PIPE_INVALID; } return ret; @@ -916,3 +915,30 @@ int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) return 0; } + +/* Algo parameter set helper function */ +int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size, + u32 param_id, struct skl_module_cfg *mcfg) +{ + struct skl_ipc_large_config_msg msg; + + msg.module_id = mcfg->id.module_id; + msg.instance_id = mcfg->id.instance_id; + msg.param_data_size = size; + msg.large_param_id = param_id; + + return skl_ipc_set_large_config(&ctx->ipc, &msg, params); +} + +int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, + u32 param_id, struct skl_module_cfg *mcfg) +{ + struct skl_ipc_large_config_msg msg; + + msg.module_id = mcfg->id.module_id; + msg.instance_id = mcfg->id.instance_id; + msg.param_data_size = size; + msg.large_param_id = param_id; + + return skl_ipc_get_large_config(&ctx->ipc, &msg, params); +} diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index b0c7bd113aac..6e4b21cdb1bd 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -55,7 +55,7 @@ void skl_nhlt_free(void *addr) static struct nhlt_specific_cfg *skl_get_specific_cfg( struct device *dev, struct nhlt_fmt *fmt, - u8 no_ch, u32 rate, u16 bps) + u8 no_ch, u32 rate, u16 bps, u8 linktype) { struct nhlt_specific_cfg *sp_config; struct wav_fmt *wfmt; @@ -68,11 +68,17 @@ static struct nhlt_specific_cfg *skl_get_specific_cfg( wfmt = &fmt_config->fmt_ext.fmt; dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", wfmt->channels, wfmt->bits_per_sample, wfmt->samples_per_sec); - if (wfmt->channels == no_ch && wfmt->samples_per_sec == rate && - wfmt->bits_per_sample == bps) { + if (wfmt->channels == no_ch && wfmt->bits_per_sample == bps) { + /* + * if link type is dmic ignore rate check as the blob is + * generic for all rates + */ sp_config = &fmt_config->config; + if (linktype == NHLT_LINK_DMIC) + return sp_config; - return sp_config; + if (wfmt->samples_per_sec == rate) + return sp_config; } fmt_config = (struct nhlt_fmt_cfg *)(fmt_config->config.caps + @@ -115,7 +121,7 @@ struct nhlt_specific_cfg struct device *dev = bus->dev; struct nhlt_specific_cfg *sp_config; struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; - u16 bps = num_ch * s_fmt; + u16 bps = (s_fmt == 16) ? 16 : 32; u8 j; dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps); @@ -128,7 +134,8 @@ struct nhlt_specific_cfg if (skl_check_ep_match(dev, epnt, instance, link_type, dirn)) { fmt = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size); - sp_config = skl_get_specific_cfg(dev, fmt, num_ch, s_rate, bps); + sp_config = skl_get_specific_cfg(dev, fmt, num_ch, + s_rate, bps, link_type); if (sp_config) return sp_config; } diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index a2f94ce1679d..b89ae6f7c096 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -28,6 +28,7 @@ #define HDA_MONO 1 #define HDA_STEREO 2 +#define HDA_QUAD 4 static struct snd_pcm_hardware azx_pcm_hw = { .info = (SNDRV_PCM_INFO_MMAP | @@ -39,12 +40,15 @@ static struct snd_pcm_hardware azx_pcm_hw = { SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */ SNDRV_PCM_INFO_HAS_LINK_ATIME | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_8000, + .rate_min = 8000, .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, + .channels_min = 1, + .channels_max = HDA_QUAD, .buffer_bytes_max = AZX_MAX_BUF_SIZE, .period_bytes_min = 128, .period_bytes_max = AZX_MAX_BUF_SIZE / 2, @@ -105,6 +109,31 @@ static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *e return HDAC_EXT_STREAM_TYPE_COUPLED; } +/* + * check if the stream opened is marked as ignore_suspend by machine, if so + * then enable suspend_active refcount + * + * The count supend_active does not need lock as it is used in open/close + * and suspend context + */ +static void skl_set_suspend_active(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, bool enable) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct snd_soc_dapm_widget *w; + struct skl *skl = ebus_to_skl(ebus); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + w = dai->playback_widget; + else + w = dai->capture_widget; + + if (w->ignore_suspend && enable) + skl->supend_active++; + else if (w->ignore_suspend && !enable) + skl->supend_active--; +} + static int skl_pcm_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -112,12 +141,8 @@ static int skl_pcm_open(struct snd_pcm_substream *substream, struct hdac_ext_stream *stream; struct snd_pcm_runtime *runtime = substream->runtime; struct skl_dma_params *dma_params; - int ret; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); - ret = pm_runtime_get_sync(dai->dev); - if (ret < 0) - return ret; stream = snd_hdac_ext_stream_assign(ebus, substream, skl_get_host_stream_type(ebus)); @@ -146,6 +171,7 @@ static int skl_pcm_open(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "stream tag set in dma params=%d\n", dma_params->stream_tag); + skl_set_suspend_active(substream, dai, true); snd_pcm_set_sync(substream); return 0; @@ -185,10 +211,6 @@ static int skl_pcm_prepare(struct snd_pcm_substream *substream, int err; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); - if (hdac_stream(stream)->prepared) { - dev_dbg(dai->dev, "already stream is prepared - returning\n"); - return 0; - } format_val = skl_get_format(substream, dai); dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d\n", @@ -261,9 +283,8 @@ static void skl_pcm_close(struct snd_pcm_substream *substream, * dma_params */ snd_soc_dai_set_dma_data(dai, substream, NULL); + skl_set_suspend_active(substream, dai, false); - pm_runtime_mark_last_busy(dai->dev); - pm_runtime_put_autosuspend(dai->dev); kfree(dma_params); } @@ -291,7 +312,53 @@ static int skl_be_hw_params(struct snd_pcm_substream *substream, p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); p_params.stream = substream->stream; - skl_tplg_be_update_params(dai, &p_params); + + return skl_tplg_be_update_params(dai, &p_params); +} + +static int skl_decoupled_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_ext_stream *stream; + int start; + unsigned long cookie; + struct hdac_stream *hstr; + + stream = get_hdac_ext_stream(substream); + hstr = hdac_stream(stream); + + if (!hstr->prepared) + return -EPIPE; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + start = 1; + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + start = 0; + break; + + default: + return -EINVAL; + } + + spin_lock_irqsave(&bus->reg_lock, cookie); + + if (start) { + snd_hdac_stream_start(hdac_stream(stream), true); + snd_hdac_stream_timecounter_init(hstr, 0); + } else { + snd_hdac_stream_stop(hdac_stream(stream)); + } + + spin_unlock_irqrestore(&bus->reg_lock, cookie); return 0; } @@ -302,23 +369,54 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct skl *skl = get_skl_ctx(dai->dev); struct skl_sst *ctx = skl->skl_sst; struct skl_module_cfg *mconfig; + struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + int ret; mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); if (!mconfig) return -EIO; switch (cmd) { - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: + skl_pcm_prepare(substream, dai); + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* + * Start HOST DMA and Start FE Pipe.This is to make sure that + * there are no underrun/overrun in the case when the FE + * pipeline is started but there is a delay in starting the + * DMA channel on the host. + */ + snd_hdac_ext_stream_decouple(ebus, stream, true); + ret = skl_decoupled_trigger(substream, cmd); + if (ret < 0) + return ret; return skl_run_pipe(ctx, mconfig->pipe); + break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: - return skl_stop_pipe(ctx, mconfig->pipe); + case SNDRV_PCM_TRIGGER_STOP: + /* + * Stop FE Pipe first and stop DMA. This is to make sure that + * there are no underrun/overrun in the case if there is a delay + * between the two operations. + */ + ret = skl_stop_pipe(ctx, mconfig->pipe); + if (ret < 0) + return ret; + + ret = skl_decoupled_trigger(substream, cmd); + if (cmd == SNDRV_PCM_TRIGGER_SUSPEND) + snd_hdac_ext_stream_decouple(ebus, stream, false); + break; default: - return 0; + return -EINVAL; } + + return 0; } static int skl_link_hw_params(struct snd_pcm_substream *substream, @@ -352,9 +450,7 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, p_params.stream = substream->stream; p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1; - skl_tplg_be_update_params(dai, &p_params); - - return 0; + return skl_tplg_be_update_params(dai, &p_params); } static int skl_link_pcm_prepare(struct snd_pcm_substream *substream, @@ -443,19 +539,6 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream, return 0; } -static int skl_be_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - return pm_runtime_get_sync(dai->dev); -} - -static void skl_be_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - pm_runtime_mark_last_busy(dai->dev); - pm_runtime_put_autosuspend(dai->dev); -} - static struct snd_soc_dai_ops skl_pcm_dai_ops = { .startup = skl_pcm_open, .shutdown = skl_pcm_close, @@ -466,24 +549,18 @@ static struct snd_soc_dai_ops skl_pcm_dai_ops = { }; static struct snd_soc_dai_ops skl_dmic_dai_ops = { - .startup = skl_be_startup, .hw_params = skl_be_hw_params, - .shutdown = skl_be_shutdown, }; static struct snd_soc_dai_ops skl_be_ssp_dai_ops = { - .startup = skl_be_startup, .hw_params = skl_be_hw_params, - .shutdown = skl_be_shutdown, }; static struct snd_soc_dai_ops skl_link_dai_ops = { - .startup = skl_be_startup, .prepare = skl_link_pcm_prepare, .hw_params = skl_link_hw_params, .hw_free = skl_link_hw_free, .trigger = skl_link_pcm_trigger, - .shutdown = skl_be_shutdown, }; static struct snd_soc_dai_driver skl_platform_dai[] = { @@ -511,7 +588,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .capture = { .stream_name = "Reference Capture", .channels_min = HDA_MONO, - .channels_max = HDA_STEREO, + .channels_max = HDA_QUAD, .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, @@ -538,6 +615,18 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, }, +{ + .name = "DMIC Pin", + .ops = &skl_pcm_dai_ops, + .capture = { + .stream_name = "DMIC Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_QUAD, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, + /* BE CPU Dais */ { .name = "SSP0 Pin", @@ -558,6 +647,24 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, { + .name = "SSP1 Pin", + .ops = &skl_be_ssp_dai_ops, + .playback = { + .stream_name = "ssp1 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp1 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ .name = "iDisp Pin", .ops = &skl_link_dai_ops, .playback = { @@ -573,8 +680,8 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .ops = &skl_dmic_dai_ops, .capture = { .stream_name = "DMIC01 Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, + .channels_min = HDA_MONO, + .channels_max = HDA_QUAD, .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, @@ -688,66 +795,15 @@ static int skl_coupled_trigger(struct snd_pcm_substream *substream, return 0; } -static int skl_decoupled_trigger(struct snd_pcm_substream *substream, - int cmd) -{ - struct hdac_ext_bus *ebus = get_bus_ctx(substream); - struct hdac_bus *bus = ebus_to_hbus(ebus); - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct hdac_ext_stream *stream; - int start; - unsigned long cookie; - struct hdac_stream *hstr; - - dev_dbg(bus->dev, "In %s cmd=%d streamname=%s\n", __func__, cmd, cpu_dai->name); - - stream = get_hdac_ext_stream(substream); - hstr = hdac_stream(stream); - - if (!hstr->prepared) - return -EPIPE; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_RESUME: - start = 1; - break; - - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_STOP: - start = 0; - break; - - default: - return -EINVAL; - } - - spin_lock_irqsave(&bus->reg_lock, cookie); - - if (start) - snd_hdac_stream_start(hdac_stream(stream), true); - else - snd_hdac_stream_stop(hdac_stream(stream)); - - if (start) - snd_hdac_stream_timecounter_init(hstr, 0); - - spin_unlock_irqrestore(&bus->reg_lock, cookie); - - return 0; -} static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct hdac_ext_bus *ebus = get_bus_ctx(substream); - if (ebus->ppcap) - return skl_decoupled_trigger(substream, cmd); - else + if (!ebus->ppcap) return skl_coupled_trigger(substream, cmd); + + return 0; } /* calculate runtime delay from LPIB */ @@ -789,7 +845,7 @@ static unsigned int skl_get_position(struct hdac_ext_stream *hstream, { struct hdac_stream *hstr = hdac_stream(hstream); struct snd_pcm_substream *substream = hstr->substream; - struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_ext_bus *ebus; unsigned int pos; int delay; @@ -800,6 +856,7 @@ static unsigned int skl_get_position(struct hdac_ext_stream *hstream, pos = 0; if (substream->runtime) { + ebus = get_bus_ctx(substream); delay = skl_get_delay_from_lpib(ebus, hstream, pos) + codec_delay; substream->runtime->delay += delay; @@ -941,7 +998,6 @@ int skl_platform_register(struct device *dev) struct skl *skl = ebus_to_skl(ebus); INIT_LIST_HEAD(&skl->ppl_list); - INIT_LIST_HEAD(&skl->dapm_path_list); ret = snd_soc_register_platform(dev, &skl_platform_drv); if (ret) { diff --git a/sound/soc/intel/skylake/skl-sst-cldma.c b/sound/soc/intel/skylake/skl-sst-cldma.c index 44748ba98da2..da2329d17f4d 100644 --- a/sound/soc/intel/skylake/skl-sst-cldma.c +++ b/sound/soc/intel/skylake/skl-sst-cldma.c @@ -18,6 +18,7 @@ #include <linux/device.h> #include <linux/mm.h> #include <linux/kthread.h> +#include <linux/delay.h> #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" @@ -33,6 +34,53 @@ void skl_cldma_int_disable(struct sst_dsp *ctx) SKL_ADSP_REG_ADSPIC, SKL_ADSPIC_CL_DMA, 0); } +static void skl_cldma_stream_run(struct sst_dsp *ctx, bool enable) +{ + unsigned char val; + int timeout; + + sst_dsp_shim_update_bits_unlocked(ctx, + SKL_ADSP_REG_CL_SD_CTL, + CL_SD_CTL_RUN_MASK, CL_SD_CTL_RUN(enable)); + + udelay(3); + timeout = 300; + do { + /* waiting for hardware to report that the stream Run bit set */ + val = sst_dsp_shim_read(ctx, SKL_ADSP_REG_CL_SD_CTL) & + CL_SD_CTL_RUN_MASK; + if (enable && val) + break; + else if (!enable && !val) + break; + udelay(3); + } while (--timeout); + + if (timeout == 0) + dev_err(ctx->dev, "Failed to set Run bit=%d enable=%d\n", val, enable); +} + +static void skl_cldma_stream_clear(struct sst_dsp *ctx) +{ + /* make sure Run bit is cleared before setting stream register */ + skl_cldma_stream_run(ctx, 0); + + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL, + CL_SD_CTL_IOCE_MASK, CL_SD_CTL_IOCE(0)); + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL, + CL_SD_CTL_FEIE_MASK, CL_SD_CTL_FEIE(0)); + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL, + CL_SD_CTL_DEIE_MASK, CL_SD_CTL_DEIE(0)); + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL, + CL_SD_CTL_STRM_MASK, CL_SD_CTL_STRM(0)); + + sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPL, CL_SD_BDLPLBA(0)); + sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPU, 0); + + sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_CBL, 0); + sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_LVI, 0); +} + /* Code loader helper APIs */ static void skl_cldma_setup_bdle(struct sst_dsp *ctx, struct snd_dma_buffer *dmab_data, @@ -68,6 +116,7 @@ static void skl_cldma_setup_controller(struct sst_dsp *ctx, struct snd_dma_buffer *dmab_bdl, unsigned int max_size, u32 count) { + skl_cldma_stream_clear(ctx); sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPL, CL_SD_BDLPLBA(dmab_bdl->addr)); sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPU, @@ -107,36 +156,13 @@ static void skl_cldma_cleanup_spb(struct sst_dsp *ctx) sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_CL_SPBFIFO_SPIB, 0); } -static void skl_cldma_trigger(struct sst_dsp *ctx, bool enable) -{ - if (enable) - sst_dsp_shim_update_bits_unlocked(ctx, - SKL_ADSP_REG_CL_SD_CTL, - CL_SD_CTL_RUN_MASK, CL_SD_CTL_RUN(1)); - else - sst_dsp_shim_update_bits_unlocked(ctx, - SKL_ADSP_REG_CL_SD_CTL, - CL_SD_CTL_RUN_MASK, CL_SD_CTL_RUN(0)); -} - static void skl_cldma_cleanup(struct sst_dsp *ctx) { skl_cldma_cleanup_spb(ctx); + skl_cldma_stream_clear(ctx); - sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL, - CL_SD_CTL_IOCE_MASK, CL_SD_CTL_IOCE(0)); - sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL, - CL_SD_CTL_FEIE_MASK, CL_SD_CTL_FEIE(0)); - sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL, - CL_SD_CTL_DEIE_MASK, CL_SD_CTL_DEIE(0)); - sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_CL_SD_CTL, - CL_SD_CTL_STRM_MASK, CL_SD_CTL_STRM(0)); - - sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPL, CL_SD_BDLPLBA(0)); - sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_BDLPU, 0); - - sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_CBL, 0); - sst_dsp_shim_write(ctx, SKL_ADSP_REG_CL_SD_LVI, 0); + ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data); + ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_bdl); } static int skl_cldma_wait_interruptible(struct sst_dsp *ctx) @@ -164,7 +190,7 @@ cleanup: static void skl_cldma_stop(struct sst_dsp *ctx) { - ctx->cl_dev.ops.cl_trigger(ctx, false); + skl_cldma_stream_run(ctx, false); } static void skl_cldma_fill_buffer(struct sst_dsp *ctx, unsigned int size, @@ -175,6 +201,21 @@ static void skl_cldma_fill_buffer(struct sst_dsp *ctx, unsigned int size, ctx->cl_dev.dma_buffer_offset, trigger); dev_dbg(ctx->dev, "spib position: %d\n", ctx->cl_dev.curr_spib_pos); + /* + * Check if the size exceeds buffer boundary. If it exceeds + * max_buffer size, then copy till buffer size and then copy + * remaining buffer from the start of ring buffer. + */ + if (ctx->cl_dev.dma_buffer_offset + size > ctx->cl_dev.bufsize) { + unsigned int size_b = ctx->cl_dev.bufsize - + ctx->cl_dev.dma_buffer_offset; + memcpy(ctx->cl_dev.dmab_data.area + ctx->cl_dev.dma_buffer_offset, + curr_pos, size_b); + size -= size_b; + curr_pos += size_b; + ctx->cl_dev.dma_buffer_offset = 0; + } + memcpy(ctx->cl_dev.dmab_data.area + ctx->cl_dev.dma_buffer_offset, curr_pos, size); @@ -291,7 +332,7 @@ int skl_cldma_prepare(struct sst_dsp *ctx) ctx->cl_dev.ops.cl_setup_controller = skl_cldma_setup_controller; ctx->cl_dev.ops.cl_setup_spb = skl_cldma_setup_spb; ctx->cl_dev.ops.cl_cleanup_spb = skl_cldma_cleanup_spb; - ctx->cl_dev.ops.cl_trigger = skl_cldma_trigger; + ctx->cl_dev.ops.cl_trigger = skl_cldma_stream_run; ctx->cl_dev.ops.cl_cleanup_controller = skl_cldma_cleanup; ctx->cl_dev.ops.cl_copy_to_dmabuf = skl_cldma_copy_to_buf; ctx->cl_dev.ops.cl_stop_dma = skl_cldma_stop; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 6bfcef449bdc..cbb40751c37e 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -58,9 +58,9 @@ struct sst_dsp_device; #define SKL_ADSP_MMIO_LEN 0x10000 -#define SKL_ADSP_W0_STAT_SZ 0x800 +#define SKL_ADSP_W0_STAT_SZ 0x1000 -#define SKL_ADSP_W0_UP_SZ 0x800 +#define SKL_ADSP_W0_UP_SZ 0x1000 #define SKL_ADSP_W1_SZ 0x1000 @@ -114,6 +114,9 @@ struct skl_dsp_fw_ops { int (*set_state_D0)(struct sst_dsp *ctx); int (*set_state_D3)(struct sst_dsp *ctx); unsigned int (*get_fw_errcode)(struct sst_dsp *ctx); + int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, char *mod_name); + int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id); + }; struct skl_dsp_loader_ops { @@ -123,6 +126,17 @@ struct skl_dsp_loader_ops { struct snd_dma_buffer *dmab); }; +struct skl_load_module_info { + u16 mod_id; + const struct firmware *fw; +}; + +struct skl_module_table { + struct skl_load_module_info *mod_info; + unsigned int usage_cnt; + struct list_head list; +}; + void skl_cldma_process_intr(struct sst_dsp *ctx); void skl_cldma_int_disable(struct sst_dsp *ctx); int skl_cldma_prepare(struct sst_dsp *ctx); @@ -139,7 +153,8 @@ void skl_dsp_free(struct sst_dsp *dsp); int skl_dsp_boot(struct sst_dsp *ctx); int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp); + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + struct skl_sst **dsp); void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); #endif /*__SKL_SST_DSP_H__*/ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 3345ea0d4414..62e665a3b8f7 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -130,6 +130,11 @@ #define IPC_SRC_QUEUE_MASK 0x7 #define IPC_SRC_QUEUE(x) (((x) & IPC_SRC_QUEUE_MASK) \ << IPC_SRC_QUEUE_SHIFT) +/* Load Module count */ +#define IPC_LOAD_MODULE_SHIFT 0 +#define IPC_LOAD_MODULE_MASK 0xFF +#define IPC_LOAD_MODULE_CNT(x) (((x) & IPC_LOAD_MODULE_MASK) \ + << IPC_LOAD_MODULE_SHIFT) /* Save pipeline messgae extension register */ #define IPC_DMA_ID_SHIFT 0 @@ -344,6 +349,8 @@ static void skl_ipc_process_reply(struct sst_generic_ipc *ipc, switch (reply) { case IPC_GLB_REPLY_SUCCESS: dev_info(ipc->dev, "ipc FW reply %x: success\n", header.primary); + /* copy the rx data from the mailbox */ + sst_dsp_inbox_read(ipc->dsp, msg->rx_data, msg->rx_size); break; case IPC_GLB_REPLY_OUT_OF_MEMORY: @@ -650,7 +657,7 @@ int skl_ipc_set_dx(struct sst_generic_ipc *ipc, u8 instance_id, dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, header.primary, header.extension); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, - dx, sizeof(dx), NULL, 0); + dx, sizeof(*dx), NULL, 0); if (ret < 0) { dev_err(ipc->dev, "ipc: set dx failed, err %d\n", ret); return ret; @@ -728,6 +735,54 @@ int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc, } EXPORT_SYMBOL_GPL(skl_ipc_bind_unbind); +/* + * In order to load a module we need to send IPC to initiate that. DMA will + * performed to load the module memory. The FW supports multiple module load + * at single shot, so we can send IPC with N modules represented by + * module_cnt + */ +int skl_ipc_load_modules(struct sst_generic_ipc *ipc, + u8 module_cnt, void *data) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret; + + header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_GLB_LOAD_MULTIPLE_MODS); + header.primary |= IPC_LOAD_MODULE_CNT(module_cnt); + + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data, + (sizeof(u16) * module_cnt), NULL, 0); + if (ret < 0) + dev_err(ipc->dev, "ipc: load modules failed :%d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(skl_ipc_load_modules); + +int skl_ipc_unload_modules(struct sst_generic_ipc *ipc, u8 module_cnt, + void *data) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret; + + header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_GLB_UNLOAD_MULTIPLE_MODS); + header.primary |= IPC_LOAD_MODULE_CNT(module_cnt); + + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data, + (sizeof(u16) * module_cnt), NULL, 0); + if (ret < 0) + dev_err(ipc->dev, "ipc: unload modules failed :%d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(skl_ipc_unload_modules); + int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, struct skl_ipc_large_config_msg *msg, u32 *param) { @@ -781,3 +836,54 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, return ret; } EXPORT_SYMBOL_GPL(skl_ipc_set_large_config); + +int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, + struct skl_ipc_large_config_msg *msg, u32 *param) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret = 0; + size_t sz_remaining, rx_size, data_offset; + + header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_MOD_LARGE_CONFIG_GET); + header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id); + header.primary |= IPC_MOD_ID(msg->module_id); + + header.extension = IPC_DATA_OFFSET_SZ(msg->param_data_size); + header.extension |= IPC_LARGE_PARAM_ID(msg->large_param_id); + header.extension |= IPC_FINAL_BLOCK(1); + header.extension |= IPC_INITIAL_BLOCK(1); + + sz_remaining = msg->param_data_size; + data_offset = 0; + + while (sz_remaining != 0) { + rx_size = sz_remaining > SKL_ADSP_W1_SZ + ? SKL_ADSP_W1_SZ : sz_remaining; + if (rx_size == sz_remaining) + header.extension |= IPC_FINAL_BLOCK(1); + + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, + ((char *)param) + data_offset, + msg->param_data_size); + if (ret < 0) { + dev_err(ipc->dev, + "ipc: get large config fail, err: %d\n", ret); + return ret; + } + sz_remaining -= rx_size; + data_offset = msg->param_data_size - sz_remaining; + + /* clear the fields */ + header.extension &= IPC_INITIAL_BLOCK_CLEAR; + header.extension &= IPC_DATA_OFFSET_SZ_CLEAR; + /* fill the fields */ + header.extension |= IPC_INITIAL_BLOCK(1); + header.extension |= IPC_DATA_OFFSET_SZ(data_offset); + } + + return ret; +} +EXPORT_SYMBOL_GPL(skl_ipc_get_large_config); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index f1a154e45dc3..1bbcdb471cf2 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -108,12 +108,21 @@ int skl_ipc_init_instance(struct sst_generic_ipc *sst_ipc, int skl_ipc_bind_unbind(struct sst_generic_ipc *sst_ipc, struct skl_ipc_bind_unbind_msg *msg); +int skl_ipc_load_modules(struct sst_generic_ipc *ipc, + u8 module_cnt, void *data); + +int skl_ipc_unload_modules(struct sst_generic_ipc *ipc, + u8 module_cnt, void *data); + int skl_ipc_set_dx(struct sst_generic_ipc *ipc, u8 instance_id, u16 module_id, struct skl_ipc_dxstate_info *dx); int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, struct skl_ipc_large_config_msg *msg, u32 *param); +int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, + struct skl_ipc_large_config_msg *msg, u32 *param); + void skl_ipc_int_enable(struct sst_dsp *dsp); void skl_ipc_op_int_enable(struct sst_dsp *ctx); void skl_ipc_op_int_disable(struct sst_dsp *ctx); diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index 3b83dc99f1d4..e26f4746afb7 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/err.h> #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" #include "../common/sst-ipc.h" @@ -37,6 +38,8 @@ #define SKL_INSTANCE_ID 0 #define SKL_BASE_FW_MODULE_ID 0 +#define SKL_NUM_MODULES 1 + static bool skl_check_fw_status(struct sst_dsp *ctx, u32 status) { u32 cur_sts; @@ -77,7 +80,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) init_waitqueue_head(&skl->boot_wait); if (ctx->fw == NULL) { - ret = request_firmware(&ctx->fw, "dsp_fw_release.bin", ctx->dev); + ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev); if (ret < 0) { dev_err(ctx->dev, "Request firmware failed %d\n", ret); skl_dsp_disable_core(ctx); @@ -115,27 +118,28 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) dev_err(ctx->dev, "Timeout waiting for ROM init done, reg:0x%x\n", reg); ret = -EIO; - goto skl_load_base_firmware_failed; + goto transfer_firmware_failed; } ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size); if (ret < 0) { dev_err(ctx->dev, "Transfer firmware failed%d\n", ret); - goto skl_load_base_firmware_failed; + goto transfer_firmware_failed; } else { ret = wait_event_timeout(skl->boot_wait, skl->boot_complete, msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); if (ret == 0) { dev_err(ctx->dev, "DSP boot failed, FW Ready timed-out\n"); ret = -EIO; - goto skl_load_base_firmware_failed; + goto transfer_firmware_failed; } dev_dbg(ctx->dev, "Download firmware successful%d\n", ret); skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); } return 0; - +transfer_firmware_failed: + ctx->cl_dev.ops.cl_cleanup_controller(ctx); skl_load_base_firmware_failed: skl_dsp_disable_core(ctx); release_firmware(ctx->fw); @@ -175,10 +179,15 @@ static int skl_set_dsp_D3(struct sst_dsp *ctx) dx.core_mask = SKL_DSP_CORE0_MASK; dx.dx_mask = SKL_IPC_D3_MASK; ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, SKL_BASE_FW_MODULE_ID, &dx); - if (ret < 0) { - dev_err(ctx->dev, "Failed to set DSP to D3 state\n"); - return ret; - } + if (ret < 0) + dev_err(ctx->dev, + "D3 request to FW failed, continuing reset: %d", ret); + + /* disable Interrupt */ + ctx->cl_dev.ops.cl_cleanup_controller(ctx); + skl_cldma_int_disable(ctx); + skl_ipc_op_int_disable(ctx); + skl_ipc_int_disable(ctx); ret = skl_dsp_disable_core(ctx); if (ret < 0) { @@ -187,12 +196,6 @@ static int skl_set_dsp_D3(struct sst_dsp *ctx) } skl_dsp_set_state_locked(ctx, SKL_DSP_RESET); - /* disable Interrupt */ - ctx->cl_dev.ops.cl_cleanup_controller(ctx); - skl_cldma_int_disable(ctx); - skl_ipc_op_int_disable(ctx); - skl_ipc_int_disable(ctx); - return ret; } @@ -201,11 +204,182 @@ static unsigned int skl_get_errorcode(struct sst_dsp *ctx) return sst_dsp_shim_read(ctx, SKL_ADSP_ERROR_CODE); } +/* + * since get/set_module are called from DAPM context, + * we don't need lock for usage count + */ +static int skl_get_module(struct sst_dsp *ctx, u16 mod_id) +{ + struct skl_module_table *module; + + list_for_each_entry(module, &ctx->module_list, list) { + if (module->mod_info->mod_id == mod_id) + return ++module->usage_cnt; + } + + return -EINVAL; +} + +static int skl_put_module(struct sst_dsp *ctx, u16 mod_id) +{ + struct skl_module_table *module; + + list_for_each_entry(module, &ctx->module_list, list) { + if (module->mod_info->mod_id == mod_id) + return --module->usage_cnt; + } + + return -EINVAL; +} + +static struct skl_module_table *skl_fill_module_table(struct sst_dsp *ctx, + char *mod_name, int mod_id) +{ + const struct firmware *fw; + struct skl_module_table *skl_module; + unsigned int size; + int ret; + + ret = request_firmware(&fw, mod_name, ctx->dev); + if (ret < 0) { + dev_err(ctx->dev, "Request Module %s failed :%d\n", + mod_name, ret); + return NULL; + } + + skl_module = devm_kzalloc(ctx->dev, sizeof(*skl_module), GFP_KERNEL); + if (skl_module == NULL) { + release_firmware(fw); + return NULL; + } + + size = sizeof(*skl_module->mod_info); + skl_module->mod_info = devm_kzalloc(ctx->dev, size, GFP_KERNEL); + if (skl_module->mod_info == NULL) { + release_firmware(fw); + return NULL; + } + + skl_module->mod_info->mod_id = mod_id; + skl_module->mod_info->fw = fw; + list_add(&skl_module->list, &ctx->module_list); + + return skl_module; +} + +/* get a module from it's unique ID */ +static struct skl_module_table *skl_module_get_from_id( + struct sst_dsp *ctx, u16 mod_id) +{ + struct skl_module_table *module; + + if (list_empty(&ctx->module_list)) { + dev_err(ctx->dev, "Module list is empty\n"); + return NULL; + } + + list_for_each_entry(module, &ctx->module_list, list) { + if (module->mod_info->mod_id == mod_id) + return module; + } + + return NULL; +} + +static int skl_transfer_module(struct sst_dsp *ctx, + struct skl_load_module_info *module) +{ + int ret; + struct skl_sst *skl = ctx->thread_context; + + ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, module->fw->data, + module->fw->size); + if (ret < 0) + return ret; + + ret = skl_ipc_load_modules(&skl->ipc, SKL_NUM_MODULES, + (void *)&module->mod_id); + if (ret < 0) + dev_err(ctx->dev, "Failed to Load module: %d\n", ret); + + ctx->cl_dev.ops.cl_stop_dma(ctx); + + return ret; +} + +static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, char *guid) +{ + struct skl_module_table *module_entry = NULL; + int ret = 0; + char mod_name[64]; /* guid str = 32 chars + 4 hyphens */ + + snprintf(mod_name, sizeof(mod_name), "%s%s%s", + "intel/dsp_fw_", guid, ".bin"); + + module_entry = skl_module_get_from_id(ctx, mod_id); + if (module_entry == NULL) { + module_entry = skl_fill_module_table(ctx, mod_name, mod_id); + if (module_entry == NULL) { + dev_err(ctx->dev, "Failed to Load module\n"); + return -EINVAL; + } + } + + if (!module_entry->usage_cnt) { + ret = skl_transfer_module(ctx, module_entry->mod_info); + if (ret < 0) { + dev_err(ctx->dev, "Failed to Load module\n"); + return ret; + } + } + + ret = skl_get_module(ctx, mod_id); + + return ret; +} + +static int skl_unload_module(struct sst_dsp *ctx, u16 mod_id) +{ + int usage_cnt; + struct skl_sst *skl = ctx->thread_context; + int ret = 0; + + usage_cnt = skl_put_module(ctx, mod_id); + if (usage_cnt < 0) { + dev_err(ctx->dev, "Module bad usage cnt!:%d\n", usage_cnt); + return -EIO; + } + ret = skl_ipc_unload_modules(&skl->ipc, + SKL_NUM_MODULES, &mod_id); + if (ret < 0) { + dev_err(ctx->dev, "Failed to UnLoad module\n"); + skl_get_module(ctx, mod_id); + return ret; + } + + return ret; +} + +static void skl_clear_module_table(struct sst_dsp *ctx) +{ + struct skl_module_table *module, *tmp; + + if (list_empty(&ctx->module_list)) + return; + + list_for_each_entry_safe(module, tmp, &ctx->module_list, list) { + list_del(&module->list); + release_firmware(module->mod_info->fw); + } +} + static struct skl_dsp_fw_ops skl_fw_ops = { .set_state_D0 = skl_set_dsp_D0, .set_state_D3 = skl_set_dsp_D3, .load_fw = skl_load_base_firmware, .get_fw_errcode = skl_get_errorcode, + .load_mod = skl_load_module, + .unload_mod = skl_unload_module, }; static struct sst_ops skl_ops = { @@ -223,7 +397,7 @@ static struct sst_dsp_device skl_dev = { }; int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp) + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp) { struct skl_sst *skl; struct sst_dsp *sst; @@ -244,11 +418,13 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, sst = skl->dsp; + sst->fw_name = fw_name; sst->addr.lpe = mmio_base; sst->addr.shim = mmio_base; sst_dsp_mailbox_init(sst, (SKL_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ), SKL_ADSP_W0_UP_SZ, SKL_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ); + INIT_LIST_HEAD(&sst->module_list); sst->dsp_ops = dsp_ops; sst->fw_ops = skl_fw_ops; @@ -259,23 +435,24 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, ret = sst->fw_ops.load_fw(sst); if (ret < 0) { dev_err(dev, "Load base fw failed : %d", ret); - return ret; + goto cleanup; } if (dsp) *dsp = skl; - return 0; + return ret; - skl_ipc_free(&skl->ipc); +cleanup: + skl_sst_dsp_cleanup(dev, skl); return ret; } EXPORT_SYMBOL_GPL(skl_sst_dsp_init); void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) { + skl_clear_module_table(ctx->dsp); skl_ipc_free(&ctx->ipc); - ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp); ctx->dsp->ops->free(ctx->dsp); } EXPORT_SYMBOL_GPL(skl_sst_dsp_cleanup); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index a7854c8fc523..5315b7422b98 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -26,6 +26,8 @@ #include "skl-topology.h" #include "skl.h" #include "skl-tplg-interface.h" +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" #define SKL_CH_FIXUP_MASK (1 << 0) #define SKL_RATE_FIXUP_MASK (1 << 1) @@ -129,17 +131,15 @@ static void skl_dump_mconfig(struct skl_sst *ctx, { dev_dbg(ctx->dev, "Dumping config\n"); dev_dbg(ctx->dev, "Input Format:\n"); - dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt.channels); - dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt.s_freq); - dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt.ch_cfg); - dev_dbg(ctx->dev, "valid bit depth = %d\n", - mcfg->in_fmt.valid_bit_depth); + dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt[0].channels); + dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt[0].s_freq); + dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt[0].ch_cfg); + dev_dbg(ctx->dev, "valid bit depth = %d\n", mcfg->in_fmt[0].valid_bit_depth); dev_dbg(ctx->dev, "Output Format:\n"); - dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt.channels); - dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt.s_freq); - dev_dbg(ctx->dev, "valid bit depth = %d\n", - mcfg->out_fmt.valid_bit_depth); - dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt.ch_cfg); + dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt[0].channels); + dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt[0].s_freq); + dev_dbg(ctx->dev, "valid bit depth = %d\n", mcfg->out_fmt[0].valid_bit_depth); + dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt[0].ch_cfg); } static void skl_tplg_update_params(struct skl_module_fmt *fmt, @@ -149,8 +149,24 @@ static void skl_tplg_update_params(struct skl_module_fmt *fmt, fmt->s_freq = params->s_freq; if (fixup & SKL_CH_FIXUP_MASK) fmt->channels = params->ch; - if (fixup & SKL_FMT_FIXUP_MASK) - fmt->valid_bit_depth = params->s_fmt; + if (fixup & SKL_FMT_FIXUP_MASK) { + fmt->valid_bit_depth = skl_get_bit_depth(params->s_fmt); + + /* + * 16 bit is 16 bit container whereas 24 bit is in 32 bit + * container so update bit depth accordingly + */ + switch (fmt->valid_bit_depth) { + case SKL_DEPTH_16BIT: + fmt->bit_depth = fmt->valid_bit_depth; + break; + + default: + fmt->bit_depth = SKL_DEPTH_32BIT; + break; + } + } + } /* @@ -171,8 +187,9 @@ static void skl_tplg_update_params_fixup(struct skl_module_cfg *m_cfg, int in_fixup, out_fixup; struct skl_module_fmt *in_fmt, *out_fmt; - in_fmt = &m_cfg->in_fmt; - out_fmt = &m_cfg->out_fmt; + /* Fixups will be applied to pin 0 only */ + in_fmt = &m_cfg->in_fmt[0]; + out_fmt = &m_cfg->out_fmt[0]; if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (is_fe) { @@ -209,18 +226,25 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx, struct skl_module_cfg *mcfg) { int multiplier = 1; + struct skl_module_fmt *in_fmt, *out_fmt; + + + /* Since fixups is applied to pin 0 only, ibs, obs needs + * change for pin 0 only + */ + in_fmt = &mcfg->in_fmt[0]; + out_fmt = &mcfg->out_fmt[0]; if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT) multiplier = 5; - - mcfg->ibs = (mcfg->in_fmt.s_freq / 1000) * - (mcfg->in_fmt.channels) * - (mcfg->in_fmt.bit_depth >> 3) * + mcfg->ibs = (in_fmt->s_freq / 1000) * + (mcfg->in_fmt->channels) * + (mcfg->in_fmt->bit_depth >> 3) * multiplier; - mcfg->obs = (mcfg->out_fmt.s_freq / 1000) * - (mcfg->out_fmt.channels) * - (mcfg->out_fmt.bit_depth >> 3) * + mcfg->obs = (mcfg->out_fmt->s_freq / 1000) * + (mcfg->out_fmt->channels) * + (mcfg->out_fmt->bit_depth >> 3) * multiplier; } @@ -292,6 +316,83 @@ static int skl_tplg_alloc_pipe_widget(struct device *dev, } /* + * some modules can have multiple params set from user control and + * need to be set after module is initialized. If set_param flag is + * set module params will be done after module is initialised. + */ +static int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx) +{ + int i, ret; + struct skl_module_cfg *mconfig = w->priv; + const struct snd_kcontrol_new *k; + struct soc_bytes_ext *sb; + struct skl_algo_data *bc; + struct skl_specific_cfg *sp_cfg; + + if (mconfig->formats_config.caps_size > 0 && + mconfig->formats_config.set_params == SKL_PARAM_SET) { + sp_cfg = &mconfig->formats_config; + ret = skl_set_module_params(ctx, sp_cfg->caps, + sp_cfg->caps_size, + sp_cfg->param_id, mconfig); + if (ret < 0) + return ret; + } + + for (i = 0; i < w->num_kcontrols; i++) { + k = &w->kcontrol_news[i]; + if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + sb = (void *) k->private_value; + bc = (struct skl_algo_data *)sb->dobj.private; + + if (bc->set_params == SKL_PARAM_SET) { + ret = skl_set_module_params(ctx, + (u32 *)bc->params, bc->max, + bc->param_id, mconfig); + if (ret < 0) + return ret; + } + } + } + + return 0; +} + +/* + * some module param can set from user control and this is required as + * when module is initailzed. if module param is required in init it is + * identifed by set_param flag. if set_param flag is not set, then this + * parameter needs to set as part of module init. + */ +static int skl_tplg_set_module_init_data(struct snd_soc_dapm_widget *w) +{ + const struct snd_kcontrol_new *k; + struct soc_bytes_ext *sb; + struct skl_algo_data *bc; + struct skl_module_cfg *mconfig = w->priv; + int i; + + for (i = 0; i < w->num_kcontrols; i++) { + k = &w->kcontrol_news[i]; + if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + sb = (struct soc_bytes_ext *)k->private_value; + bc = (struct skl_algo_data *)sb->dobj.private; + + if (bc->set_params != SKL_PARAM_INIT) + continue; + + mconfig->formats_config.caps = (u32 *)&bc->params; + mconfig->formats_config.caps_size = bc->max; + + break; + } + } + + return 0; +} + +/* * Inside a pipe instance, we can have various modules. These modules need * to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by * skl_init_module() routine, so invoke that for all modules in a pipeline @@ -313,12 +414,25 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) return -ENOMEM; + if (mconfig->is_loadable && ctx->dsp->fw_ops.load_mod) { + ret = ctx->dsp->fw_ops.load_mod(ctx->dsp, + mconfig->id.module_id, mconfig->guid); + if (ret < 0) + return ret; + } + /* * apply fix/conversion to module params based on * FE/BE params */ skl_tplg_update_module_params(w, ctx); - ret = skl_init_module(ctx, mconfig, NULL); + + skl_tplg_set_module_init_data(w); + ret = skl_init_module(ctx, mconfig); + if (ret < 0) + return ret; + + ret = skl_tplg_set_module_params(w, ctx); if (ret < 0) return ret; } @@ -326,6 +440,24 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) return 0; } +static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx, + struct skl_pipe *pipe) +{ + struct skl_pipe_module *w_module = NULL; + struct skl_module_cfg *mconfig = NULL; + + list_for_each_entry(w_module, &pipe->w_list, node) { + mconfig = w_module->w->priv; + + if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod) + return ctx->dsp->fw_ops.unload_mod(ctx->dsp, + mconfig->id.module_id); + } + + /* no modules to unload in this path, so return */ + return 0; +} + /* * Mixer module represents a pipeline. So in the Pre-PMU event of mixer we * need create the pipeline. So we do following: @@ -397,41 +529,24 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, return 0; } -/* - * A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA - * we need to do following: - * - Bind to sink pipeline - * Since the sink pipes can be running and we don't get mixer event on - * connect for already running mixer, we need to find the sink pipes - * here and bind to them. This way dynamic connect works. - * - Start sink pipeline, if not running - * - Then run current pipe - */ -static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, - struct skl *skl) +static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w, + struct skl *skl, + struct skl_module_cfg *src_mconfig) { struct snd_soc_dapm_path *p; - struct skl_dapm_path_list *path_list; - struct snd_soc_dapm_widget *source, *sink; - struct skl_module_cfg *src_mconfig, *sink_mconfig; + struct snd_soc_dapm_widget *sink = NULL, *next_sink = NULL; + struct skl_module_cfg *sink_mconfig; struct skl_sst *ctx = skl->skl_sst; - int ret = 0; - - source = w; - src_mconfig = source->priv; + int ret; - /* - * find which sink it is connected to, bind with the sink, - * if sink is not started, start sink pipe first, then start - * this pipe - */ - snd_soc_dapm_widget_for_each_source_path(w, p) { + snd_soc_dapm_widget_for_each_sink_path(w, p) { if (!p->connect) continue; dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name); dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name); + next_sink = p->sink; /* * here we will check widgets in sink pipelines, so that * can be any widgets type and we are only interested if @@ -441,7 +556,6 @@ static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, is_skl_dsp_widget_type(p->sink)) { sink = p->sink; - src_mconfig = source->priv; sink_mconfig = sink->priv; /* Bind source to sink, mixin is always source */ @@ -451,32 +565,89 @@ static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, /* Start sinks pipe first */ if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) { - ret = skl_run_pipe(ctx, sink_mconfig->pipe); + if (sink_mconfig->pipe->conn_type != + SKL_PIPE_CONN_TYPE_FE) + ret = skl_run_pipe(ctx, + sink_mconfig->pipe); if (ret) return ret; } - - path_list = kzalloc( - sizeof(struct skl_dapm_path_list), - GFP_KERNEL); - if (path_list == NULL) - return -ENOMEM; - - /* Add connected path to one global list */ - path_list->dapm_path = p; - list_add_tail(&path_list->node, &skl->dapm_path_list); - break; } } - /* Start source pipe last after starting all sinks */ - ret = skl_run_pipe(ctx, src_mconfig->pipe); + if (!sink) + return skl_tplg_bind_sinks(next_sink, skl, src_mconfig); + + return 0; +} + +/* + * A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA + * we need to do following: + * - Bind to sink pipeline + * Since the sink pipes can be running and we don't get mixer event on + * connect for already running mixer, we need to find the sink pipes + * here and bind to them. This way dynamic connect works. + * - Start sink pipeline, if not running + * - Then run current pipe + */ +static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct skl_module_cfg *src_mconfig; + struct skl_sst *ctx = skl->skl_sst; + int ret = 0; + + src_mconfig = w->priv; + + /* + * find which sink it is connected to, bind with the sink, + * if sink is not started, start sink pipe first, then start + * this pipe + */ + ret = skl_tplg_bind_sinks(w, skl, src_mconfig); if (ret) return ret; + /* Start source pipe last after starting all sinks */ + if (src_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE) + return skl_run_pipe(ctx, src_mconfig->pipe); + return 0; } +static struct snd_soc_dapm_widget *skl_get_src_dsp_widget( + struct snd_soc_dapm_widget *w, struct skl *skl) +{ + struct snd_soc_dapm_path *p; + struct snd_soc_dapm_widget *src_w = NULL; + struct skl_sst *ctx = skl->skl_sst; + + snd_soc_dapm_widget_for_each_source_path(w, p) { + src_w = p->source; + if (!p->connect) + continue; + + dev_dbg(ctx->dev, "sink widget=%s\n", w->name); + dev_dbg(ctx->dev, "src widget=%s\n", p->source->name); + + /* + * here we will check widgets in sink pipelines, so that can + * be any widgets type and we are only interested if they are + * ones used for SKL so check that first + */ + if ((p->source->priv != NULL) && + is_skl_dsp_widget_type(p->source)) { + return p->source; + } + } + + if (src_w != NULL) + return skl_get_src_dsp_widget(src_w, skl); + + return NULL; +} + /* * in the Post-PMU event of mixer we need to do following: * - Check if this pipe is running @@ -490,7 +661,6 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w, struct skl *skl) { int ret = 0; - struct snd_soc_dapm_path *p; struct snd_soc_dapm_widget *source, *sink; struct skl_module_cfg *src_mconfig, *sink_mconfig; struct skl_sst *ctx = skl->skl_sst; @@ -504,32 +674,18 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w, * one more sink before this sink got connected, Since source is * started, bind this sink to source and start this pipe. */ - snd_soc_dapm_widget_for_each_sink_path(w, p) { - if (!p->connect) - continue; - - dev_dbg(ctx->dev, "sink widget=%s\n", w->name); - dev_dbg(ctx->dev, "src widget=%s\n", p->source->name); + source = skl_get_src_dsp_widget(w, skl); + if (source != NULL) { + src_mconfig = source->priv; + sink_mconfig = sink->priv; + src_pipe_started = 1; /* - * here we will check widgets in sink pipelines, so that - * can be any widgets type and we are only interested if - * they are ones used for SKL so check that first + * check pipe state, then no need to bind or start the + * pipe */ - if ((p->source->priv != NULL) && - is_skl_dsp_widget_type(p->source)) { - source = p->source; - src_mconfig = source->priv; - sink_mconfig = sink->priv; - src_pipe_started = 1; - - /* - * check pipe state, then no need to bind or start - * the pipe - */ - if (src_mconfig->pipe->state != SKL_PIPE_STARTED) - src_pipe_started = 0; - } + if (src_mconfig->pipe->state != SKL_PIPE_STARTED) + src_pipe_started = 0; } if (src_pipe_started) { @@ -537,7 +693,8 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w, if (ret) return ret; - ret = skl_run_pipe(ctx, sink_mconfig->pipe); + if (sink_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE) + ret = skl_run_pipe(ctx, sink_mconfig->pipe); } return ret; @@ -552,52 +709,35 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w, static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w, struct skl *skl) { - struct snd_soc_dapm_widget *source, *sink; struct skl_module_cfg *src_mconfig, *sink_mconfig; - int ret = 0, path_found = 0; - struct skl_dapm_path_list *path_list, *tmp_list; + int ret = 0, i; struct skl_sst *ctx = skl->skl_sst; - sink = w; - sink_mconfig = sink->priv; + sink_mconfig = w->priv; /* Stop the pipe */ ret = skl_stop_pipe(ctx, sink_mconfig->pipe); if (ret) return ret; - /* - * This list, dapm_path_list handling here does not need any locks - * as we are under dapm lock while handling widget events. - * List can be manipulated safely only under dapm widgets handler - * routines - */ - list_for_each_entry_safe(path_list, tmp_list, - &skl->dapm_path_list, node) { - if (path_list->dapm_path->sink == sink) { - dev_dbg(ctx->dev, "Path found = %s\n", - path_list->dapm_path->name); - source = path_list->dapm_path->source; - src_mconfig = source->priv; - path_found = 1; - - list_del(&path_list->node); - kfree(path_list); - break; - } - } - - /* - * If path_found == 1, that means pmd for source pipe has - * not occurred, source is connected to some other sink. - * so its responsibility of sink to unbind itself from source. - */ - if (path_found) { - ret = skl_stop_pipe(ctx, src_mconfig->pipe); - if (ret < 0) - return ret; + for (i = 0; i < sink_mconfig->max_in_queue; i++) { + if (sink_mconfig->m_in_pin[i].pin_state == SKL_PIN_BIND_DONE) { + src_mconfig = sink_mconfig->m_in_pin[i].tgt_mcfg; + if (!src_mconfig) + continue; + /* + * If path_found == 1, that means pmd for source + * pipe has not occurred, source is connected to + * some other sink. so its responsibility of sink + * to unbind itself from source. + */ + ret = skl_stop_pipe(ctx, src_mconfig->pipe); + if (ret < 0) + return ret; - ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig); + ret = skl_unbind_modules(ctx, + src_mconfig, sink_mconfig); + } } return ret; @@ -622,10 +762,12 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, int ret = 0; skl_tplg_free_pipe_mcps(skl, mconfig); + skl_tplg_free_pipe_mem(skl, mconfig); list_for_each_entry(w_module, &s_pipe->w_list, node) { dst_module = w_module->w->priv; + skl_tplg_free_pipe_mcps(skl, dst_module); if (src_module == NULL) { src_module = dst_module; continue; @@ -639,9 +781,8 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, } ret = skl_delete_pipe(ctx, mconfig->pipe); - skl_tplg_free_pipe_mem(skl, mconfig); - return ret; + return skl_tplg_unload_pipe_modules(ctx, s_pipe); } /* @@ -653,47 +794,34 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, struct skl *skl) { - struct snd_soc_dapm_widget *source, *sink; struct skl_module_cfg *src_mconfig, *sink_mconfig; - int ret = 0, path_found = 0; - struct skl_dapm_path_list *path_list, *tmp_path_list; + int ret = 0, i; struct skl_sst *ctx = skl->skl_sst; - source = w; - src_mconfig = source->priv; + src_mconfig = w->priv; - skl_tplg_free_pipe_mcps(skl, src_mconfig); /* Stop the pipe since this is a mixin module */ ret = skl_stop_pipe(ctx, src_mconfig->pipe); if (ret) return ret; - list_for_each_entry_safe(path_list, tmp_path_list, &skl->dapm_path_list, node) { - if (path_list->dapm_path->source == source) { - dev_dbg(ctx->dev, "Path found = %s\n", - path_list->dapm_path->name); - sink = path_list->dapm_path->sink; - sink_mconfig = sink->priv; - path_found = 1; - - list_del(&path_list->node); - kfree(path_list); - break; + for (i = 0; i < src_mconfig->max_out_queue; i++) { + if (src_mconfig->m_out_pin[i].pin_state == SKL_PIN_BIND_DONE) { + sink_mconfig = src_mconfig->m_out_pin[i].tgt_mcfg; + if (!sink_mconfig) + continue; + /* + * This is a connecter and if path is found that means + * unbind between source and sink has not happened yet + */ + ret = skl_stop_pipe(ctx, sink_mconfig->pipe); + if (ret < 0) + return ret; + ret = skl_unbind_modules(ctx, src_mconfig, + sink_mconfig); } } - /* - * This is a connector and if path is found that means - * unbind between source and sink has not happened yet - */ - if (path_found) { - ret = skl_stop_pipe(ctx, src_mconfig->pipe); - if (ret < 0) - return ret; - - ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig); - } - return ret; } @@ -774,6 +902,67 @@ static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w, return 0; } +static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol, + unsigned int __user *data, unsigned int size) +{ + struct soc_bytes_ext *sb = + (struct soc_bytes_ext *)kcontrol->private_value; + struct skl_algo_data *bc = (struct skl_algo_data *)sb->dobj.private; + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct skl_module_cfg *mconfig = w->priv; + struct skl *skl = get_skl_ctx(w->dapm->dev); + + if (w->power) + skl_get_module_params(skl->skl_sst, (u32 *)bc->params, + bc->max, bc->param_id, mconfig); + + if (bc->params) { + if (copy_to_user(data, &bc->param_id, sizeof(u32))) + return -EFAULT; + if (copy_to_user(data + 1, &size, sizeof(u32))) + return -EFAULT; + if (copy_to_user(data + 2, bc->params, size)) + return -EFAULT; + } + + return 0; +} + +#define SKL_PARAM_VENDOR_ID 0xff + +static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol, + const unsigned int __user *data, unsigned int size) +{ + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct skl_module_cfg *mconfig = w->priv; + struct soc_bytes_ext *sb = + (struct soc_bytes_ext *)kcontrol->private_value; + struct skl_algo_data *ac = (struct skl_algo_data *)sb->dobj.private; + struct skl *skl = get_skl_ctx(w->dapm->dev); + + if (ac->params) { + /* + * if the param_is is of type Vendor, firmware expects actual + * parameter id and size from the control. + */ + if (ac->param_id == SKL_PARAM_VENDOR_ID) { + if (copy_from_user(ac->params, data, size)) + return -EFAULT; + } else { + if (copy_from_user(ac->params, + data + 2 * sizeof(u32), size)) + return -EFAULT; + } + + if (w->power) + return skl_set_module_params(skl->skl_sst, + (u32 *)ac->params, ac->max, + ac->param_id, mconfig); + } + + return 0; +} + /* * The FE params are passed by hw_params of the DAI. * On hw_params, the params are stored in Gateway module of the FE and we @@ -790,9 +979,9 @@ int skl_tplg_update_pipe_params(struct device *dev, memcpy(pipe->p_params, params, sizeof(*params)); if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) - format = &mconfig->in_fmt; + format = &mconfig->in_fmt[0]; else - format = &mconfig->out_fmt; + format = &mconfig->out_fmt[0]; /* set the hw_params */ format->s_freq = params->s_freq; @@ -809,6 +998,7 @@ int skl_tplg_update_pipe_params(struct device *dev, break; case SKL_DEPTH_24BIT: + case SKL_DEPTH_32BIT: format->bit_depth = SKL_DEPTH_32BIT; break; @@ -846,7 +1036,7 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) w = dai->playback_widget; snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connect && p->sink->power && - is_skl_dsp_widget_type(p->sink)) + !is_skl_dsp_widget_type(p->sink)) continue; if (p->sink->priv) { @@ -859,7 +1049,7 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) w = dai->capture_widget; snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connect && p->source->power && - is_skl_dsp_widget_type(p->source)) + !is_skl_dsp_widget_type(p->source)) continue; if (p->source->priv) { @@ -920,6 +1110,9 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, memcpy(pipe->p_params, params, sizeof(*params)); + if (link_type == NHLT_LINK_HDA) + return 0; + /* update the blob based on virtual bus_id*/ cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, params->s_fmt, params->ch, @@ -950,18 +1143,13 @@ static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai, if (p->connect && is_skl_dsp_widget_type(p->source) && p->source->priv) { - if (!p->source->power) { - ret = skl_tplg_be_fill_pipe_params( - dai, p->source->priv, - params); - if (ret < 0) - return ret; - } else { - return -EBUSY; - } + ret = skl_tplg_be_fill_pipe_params(dai, + p->source->priv, params); + if (ret < 0) + return ret; } else { - ret = skl_tplg_be_set_src_pipe_params( - dai, p->source, params); + ret = skl_tplg_be_set_src_pipe_params(dai, + p->source, params); if (ret < 0) return ret; } @@ -980,15 +1168,10 @@ static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai, if (p->connect && is_skl_dsp_widget_type(p->sink) && p->sink->priv) { - if (!p->sink->power) { - ret = skl_tplg_be_fill_pipe_params( - dai, p->sink->priv, params); - if (ret < 0) - return ret; - } else { - return -EBUSY; - } - + ret = skl_tplg_be_fill_pipe_params(dai, + p->sink->priv, params); + if (ret < 0) + return ret; } else { ret = skl_tplg_be_set_sink_pipe_params( dai, p->sink, params); @@ -1030,6 +1213,11 @@ static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = { {SKL_PGA_EVENT, skl_tplg_pga_event}, }; +static const struct snd_soc_tplg_bytes_ext_ops skl_tlv_ops[] = { + {SKL_CONTROL_TYPE_BYTE_TLV, skl_tplg_tlv_control_get, + skl_tplg_tlv_control_set}, +}; + /* * The topology binary passes the pin info for a module so initialize the pin * info passed into module instance @@ -1045,6 +1233,7 @@ static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin, m_pin[i].id.instance_id = dfw_pin[i].instance_id; m_pin[i].in_use = false; m_pin[i].is_dynamic = is_dynamic; + m_pin[i].pin_state = SKL_PIN_UNBIND; } } @@ -1092,6 +1281,24 @@ static struct skl_pipe *skl_tplg_add_pipe(struct device *dev, return ppl->pipe; } +static void skl_tplg_fill_fmt(struct skl_module_fmt *dst_fmt, + struct skl_dfw_module_fmt *src_fmt, + int pins) +{ + int i; + + for (i = 0; i < pins; i++) { + dst_fmt[i].channels = src_fmt[i].channels; + dst_fmt[i].s_freq = src_fmt[i].freq; + dst_fmt[i].bit_depth = src_fmt[i].bit_depth; + dst_fmt[i].valid_bit_depth = src_fmt[i].valid_bit_depth; + dst_fmt[i].ch_cfg = src_fmt[i].ch_cfg; + dst_fmt[i].ch_map = src_fmt[i].ch_map; + dst_fmt[i].interleaving_style = src_fmt[i].interleaving_style; + dst_fmt[i].sample_type = src_fmt[i].sample_type; + } +} + /* * Topology core widget load callback * @@ -1130,22 +1337,16 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, mconfig->max_in_queue = dfw_config->max_in_queue; mconfig->max_out_queue = dfw_config->max_out_queue; mconfig->is_loadable = dfw_config->is_loadable; - mconfig->in_fmt.channels = dfw_config->in_fmt.channels; - mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq; - mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth; - mconfig->in_fmt.valid_bit_depth = - dfw_config->in_fmt.valid_bit_depth; - mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg; - mconfig->out_fmt.channels = dfw_config->out_fmt.channels; - mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq; - mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth; - mconfig->out_fmt.valid_bit_depth = - dfw_config->out_fmt.valid_bit_depth; - mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg; + skl_tplg_fill_fmt(mconfig->in_fmt, dfw_config->in_fmt, + MODULE_MAX_IN_PINS); + skl_tplg_fill_fmt(mconfig->out_fmt, dfw_config->out_fmt, + MODULE_MAX_OUT_PINS); + mconfig->params_fixup = dfw_config->params_fixup; mconfig->converter = dfw_config->converter; mconfig->m_type = dfw_config->module_type; mconfig->vbus_id = dfw_config->vbus_id; + mconfig->mem_pages = dfw_config->mem_pages; pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe); if (pipe) @@ -1156,10 +1357,13 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, mconfig->time_slot = dfw_config->time_slot; mconfig->formats_config.caps_size = dfw_config->caps.caps_size; - mconfig->m_in_pin = devm_kzalloc(bus->dev, - (mconfig->max_in_queue) * - sizeof(*mconfig->m_in_pin), - GFP_KERNEL); + if (dfw_config->is_loadable) + memcpy(mconfig->guid, dfw_config->uuid, + ARRAY_SIZE(dfw_config->uuid)); + + mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * + sizeof(*mconfig->m_in_pin), + GFP_KERNEL); if (!mconfig->m_in_pin) return -ENOMEM; @@ -1188,7 +1392,9 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, return -ENOMEM; memcpy(mconfig->formats_config.caps, dfw_config->caps.caps, - dfw_config->caps.caps_size); + dfw_config->caps.caps_size); + mconfig->formats_config.param_id = dfw_config->caps.param_id; + mconfig->formats_config.set_params = dfw_config->caps.set_params; bind_event: if (tplg_w->event_type == 0) { @@ -1209,8 +1415,70 @@ bind_event: return 0; } +static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be, + struct snd_soc_tplg_bytes_control *bc) +{ + struct skl_algo_data *ac; + struct skl_dfw_algo_data *dfw_ac = + (struct skl_dfw_algo_data *)bc->priv.data; + + ac = devm_kzalloc(dev, sizeof(*ac), GFP_KERNEL); + if (!ac) + return -ENOMEM; + + /* Fill private data */ + ac->max = dfw_ac->max; + ac->param_id = dfw_ac->param_id; + ac->set_params = dfw_ac->set_params; + + if (ac->max) { + ac->params = (char *) devm_kzalloc(dev, ac->max, GFP_KERNEL); + if (!ac->params) + return -ENOMEM; + + if (dfw_ac->params) + memcpy(ac->params, dfw_ac->params, ac->max); + } + + be->dobj.private = ac; + return 0; +} + +static int skl_tplg_control_load(struct snd_soc_component *cmpnt, + struct snd_kcontrol_new *kctl, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct soc_bytes_ext *sb; + struct snd_soc_tplg_bytes_control *tplg_bc; + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); + struct hdac_bus *bus = ebus_to_hbus(ebus); + + switch (hdr->ops.info) { + case SND_SOC_TPLG_CTL_BYTES: + tplg_bc = container_of(hdr, + struct snd_soc_tplg_bytes_control, hdr); + if (kctl->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + sb = (struct soc_bytes_ext *)kctl->private_value; + if (tplg_bc->priv.size) + return skl_init_algo_data( + bus->dev, sb, tplg_bc); + } + break; + + default: + dev_warn(bus->dev, "Control load not supported %d:%d:%d\n", + hdr->ops.get, hdr->ops.put, hdr->ops.info); + break; + } + + return 0; +} + static struct snd_soc_tplg_ops skl_tplg_ops = { .widget_load = skl_tplg_widget_load, + .control_load = skl_tplg_control_load, + .bytes_ext_ops = skl_tlv_ops, + .bytes_ext_ops_count = ARRAY_SIZE(skl_tlv_ops), }; /* This will be read from topology manifest, currently defined here */ @@ -1240,6 +1508,7 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) */ ret = snd_soc_tplg_component_load(&platform->component, &skl_tplg_ops, fw, 0); + release_firmware(fw); if (ret < 0) { dev_err(bus->dev, "tplg component load failed%d\n", ret); return -EINVAL; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 76053a8de41c..9aa2a2b6598a 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -36,6 +36,9 @@ /* Maximum number of coefficients up down mixer module */ #define UP_DOWN_MIXER_MAX_COEFF 6 +#define MODULE_MAX_IN_PINS 8 +#define MODULE_MAX_OUT_PINS 8 + enum skl_channel_index { SKL_CHANNEL_LEFT = 0, SKL_CHANNEL_RIGHT = 1, @@ -55,12 +58,6 @@ enum skl_bitdepth { SKL_DEPTH_INVALID }; -enum skl_interleaving { - /* [s1_ch1...s1_chN,...,sM_ch1...sM_chN] */ - SKL_INTERLEAVING_PER_CHANNEL = 0, - /* [s1_ch1...sM_ch1,...,s1_chN...sM_chN] */ - SKL_INTERLEAVING_PER_SAMPLE = 1, -}; enum skl_s_freq { SKL_FS_8000 = 8000, @@ -143,6 +140,16 @@ struct skl_up_down_mixer_cfg { s32 coeff[UP_DOWN_MIXER_MAX_COEFF]; } __packed; +struct skl_algo_cfg { + struct skl_base_cfg base_cfg; + char params[0]; +} __packed; + +struct skl_base_outfmt_cfg { + struct skl_base_cfg base_cfg; + struct skl_audio_data_format out_fmt; +} __packed; + enum skl_dma_type { SKL_DMA_HDA_HOST_OUTPUT_CLASS = 0, SKL_DMA_HDA_HOST_INPUT_CLASS = 1, @@ -178,21 +185,34 @@ struct skl_module_fmt { u32 bit_depth; u32 valid_bit_depth; u32 ch_cfg; + u32 interleaving_style; + u32 sample_type; + u32 ch_map; }; +struct skl_module_cfg; + struct skl_module_inst_id { u32 module_id; u32 instance_id; }; +enum skl_module_pin_state { + SKL_PIN_UNBIND = 0, + SKL_PIN_BIND_DONE = 1, +}; + struct skl_module_pin { struct skl_module_inst_id id; - u8 pin_index; bool is_dynamic; bool in_use; + enum skl_module_pin_state pin_state; + struct skl_module_cfg *tgt_mcfg; }; struct skl_specific_cfg { + u32 set_params; + u32 param_id; u32 caps_size; u32 *caps; }; @@ -238,9 +258,13 @@ enum skl_module_state { }; struct skl_module_cfg { + char guid[SKL_UUID_STR_SZ]; struct skl_module_inst_id id; - struct skl_module_fmt in_fmt; - struct skl_module_fmt out_fmt; + u8 domain; + bool homogenous_inputs; + bool homogenous_outputs; + struct skl_module_fmt in_fmt[MODULE_MAX_IN_PINS]; + struct skl_module_fmt out_fmt[MODULE_MAX_OUT_PINS]; u8 max_in_queue; u8 max_out_queue; u8 in_queue_mask; @@ -258,6 +282,7 @@ struct skl_module_cfg { u32 params_fixup; u32 converter; u32 vbus_id; + u32 mem_pages; struct skl_module_pin *m_in_pin; struct skl_module_pin *m_out_pin; enum skl_module_type m_type; @@ -267,13 +292,15 @@ struct skl_module_cfg { struct skl_specific_cfg formats_config; }; -struct skl_pipeline { - struct skl_pipe *pipe; - struct list_head node; +struct skl_algo_data { + u32 param_id; + u32 set_params; + u32 max; + char *params; }; -struct skl_dapm_path_list { - struct snd_soc_dapm_path *dapm_path; +struct skl_pipeline { + struct skl_pipe *pipe; struct list_head node; }; @@ -305,8 +332,7 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); -int skl_init_module(struct skl_sst *ctx, struct skl_module_cfg *module_config, - char *param); +int skl_init_module(struct skl_sst *ctx, struct skl_module_cfg *module_config); int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg *src_module, struct skl_module_cfg *dst_module); @@ -314,5 +340,10 @@ int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg int skl_unbind_modules(struct skl_sst *ctx, struct skl_module_cfg *src_module, struct skl_module_cfg *dst_module); +int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size, + u32 param_id, struct skl_module_cfg *mcfg); +int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, + u32 param_id, struct skl_module_cfg *mcfg); + enum skl_bitdepth skl_get_bit_depth(int params); #endif diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index 2bc396d54cbe..c9ae010b3cc8 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -23,15 +23,13 @@ * Default types range from 0~12. type can range from 0 to 0xff * SST types start at higher to avoid any overlapping in future */ -#define SOC_CONTROL_TYPE_HDA_SST_ALGO_PARAMS 0x100 -#define SOC_CONTROL_TYPE_HDA_SST_MUX 0x101 -#define SOC_CONTROL_TYPE_HDA_SST_MIX 0x101 -#define SOC_CONTROL_TYPE_HDA_SST_BYTE 0x103 +#define SKL_CONTROL_TYPE_BYTE_TLV 0x100 #define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ #define MAX_IN_QUEUE 8 #define MAX_OUT_QUEUE 8 +#define SKL_UUID_STR_SZ 40 /* Event types goes here */ /* Reserve event type 0 for no event handlers */ enum skl_event_types { @@ -72,6 +70,7 @@ enum skl_ch_cfg { SKL_CH_CFG_DUAL_MONO = 9, SKL_CH_CFG_I2S_DUAL_STEREO_0 = 10, SKL_CH_CFG_I2S_DUAL_STEREO_1 = 11, + SKL_CH_CFG_4_CHANNEL = 12, SKL_CH_CFG_INVALID }; @@ -79,7 +78,9 @@ enum skl_module_type { SKL_MODULE_TYPE_MIXER = 0, SKL_MODULE_TYPE_COPIER, SKL_MODULE_TYPE_UPDWMIX, - SKL_MODULE_TYPE_SRCINT + SKL_MODULE_TYPE_SRCINT, + SKL_MODULE_TYPE_ALGO, + SKL_MODULE_TYPE_BASE_OUTFMT }; enum skl_core_affinity { @@ -110,6 +111,42 @@ enum skl_dev_type { SKL_DEVICE_NONE }; +/** + * enum skl_interleaving - interleaving style + * + * @SKL_INTERLEAVING_PER_CHANNEL: [s1_ch1...s1_chN,...,sM_ch1...sM_chN] + * @SKL_INTERLEAVING_PER_SAMPLE: [s1_ch1...sM_ch1,...,s1_chN...sM_chN] + */ +enum skl_interleaving { + SKL_INTERLEAVING_PER_CHANNEL = 0, + SKL_INTERLEAVING_PER_SAMPLE = 1, +}; + +enum skl_sample_type { + SKL_SAMPLE_TYPE_INT_MSB = 0, + SKL_SAMPLE_TYPE_INT_LSB = 1, + SKL_SAMPLE_TYPE_INT_SIGNED = 2, + SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, + SKL_SAMPLE_TYPE_FLOAT = 4 +}; + +enum module_pin_type { + /* All pins of the module takes same PCM inputs or outputs + * e.g. mixout + */ + SKL_PIN_TYPE_HOMOGENEOUS, + /* All pins of the module takes different PCM inputs or outputs + * e.g mux + */ + SKL_PIN_TYPE_HETEROGENEOUS, +}; + +enum skl_module_param_type { + SKL_PARAM_DEFAULT = 0, + SKL_PARAM_INIT, + SKL_PARAM_SET +}; + struct skl_dfw_module_pin { u16 module_id; u16 instance_id; @@ -121,9 +158,15 @@ struct skl_dfw_module_fmt { u32 bit_depth; u32 valid_bit_depth; u32 ch_cfg; + u32 interleaving_style; + u32 sample_type; + u32 ch_map; } __packed; struct skl_dfw_module_caps { + u32 set_params:2; + u32 rsvd:30; + u32 param_id; u32 caps_size; u32 caps[HDA_SST_CFG_MAX]; }; @@ -131,41 +174,57 @@ struct skl_dfw_module_caps { struct skl_dfw_pipe { u8 pipe_id; u8 pipe_priority; - u16 conn_type; - u32 memory_pages; + u16 conn_type:4; + u16 rsvd:4; + u16 memory_pages:8; } __packed; struct skl_dfw_module { + char uuid[SKL_UUID_STR_SZ]; + u16 module_id; u16 instance_id; u32 max_mcps; - u8 core_id; - u8 max_in_queue; - u8 max_out_queue; - u8 is_loadable; - u8 conn_type; - u8 dev_type; - u8 hw_conn_type; - u8 time_slot; + u32 mem_pages; u32 obs; u32 ibs; - u32 params_fixup; - u32 converter; - u32 module_type; u32 vbus_id; - u8 is_dynamic_in_pin; - u8 is_dynamic_out_pin; + + u32 max_in_queue:8; + u32 max_out_queue:8; + u32 time_slot:8; + u32 core_id:4; + u32 rsvd1:4; + + u32 module_type:8; + u32 conn_type:4; + u32 dev_type:4; + u32 hw_conn_type:4; + u32 rsvd2:12; + + u32 params_fixup:8; + u32 converter:8; + u32 input_pin_type:1; + u32 output_pin_type:1; + u32 is_dynamic_in_pin:1; + u32 is_dynamic_out_pin:1; + u32 is_loadable:1; + u32 rsvd3:11; + struct skl_dfw_pipe pipe; - struct skl_dfw_module_fmt in_fmt; - struct skl_dfw_module_fmt out_fmt; + struct skl_dfw_module_fmt in_fmt[MAX_IN_QUEUE]; + struct skl_dfw_module_fmt out_fmt[MAX_OUT_QUEUE]; struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE]; struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE]; struct skl_dfw_module_caps caps; } __packed; struct skl_dfw_algo_data { + u32 set_params:2; + u32 rsvd:30; + u32 param_id; u32 max; - char *params; + char params[0]; } __packed; #endif diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 5319529aedf7..c38bf99ced10 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -26,6 +26,7 @@ #include <linux/pm_runtime.h> #include <linux/platform_device.h> #include <sound/pcm.h> +#include "../common/sst-acpi.h" #include "skl.h" /* @@ -129,6 +130,37 @@ static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect) return 0; } +#ifdef CONFIG_PM +static int _skl_suspend(struct hdac_ext_bus *ebus) +{ + struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = ebus_to_hbus(ebus); + int ret; + + snd_hdac_ext_bus_link_power_down_all(ebus); + + ret = skl_suspend_dsp(skl); + if (ret < 0) + return ret; + + snd_hdac_bus_stop_chip(bus); + snd_hdac_bus_enter_link_reset(bus); + + return 0; +} + +static int _skl_resume(struct hdac_ext_bus *ebus) +{ + struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = ebus_to_hbus(ebus); + + skl_init_pci(skl); + snd_hdac_bus_init_chip(bus, true); + + return skl_resume_dsp(skl); +} +#endif + #ifdef CONFIG_PM_SLEEP /* * power management @@ -137,26 +169,40 @@ static int skl_suspend(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct hdac_bus *bus = ebus_to_hbus(ebus); - - snd_hdac_bus_stop_chip(bus); - snd_hdac_bus_enter_link_reset(bus); + struct skl *skl = ebus_to_skl(ebus); - return 0; + /* + * Do not suspend if streams which are marked ignore suspend are + * running, we need to save the state for these and continue + */ + if (skl->supend_active) { + pci_save_state(pci); + pci_disable_device(pci); + return 0; + } else { + return _skl_suspend(ebus); + } } static int skl_resume(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl *hda = ebus_to_skl(ebus); - - skl_init_pci(hda); + struct skl *skl = ebus_to_skl(ebus); + int ret; - snd_hdac_bus_init_chip(bus, 1); + /* + * resume only when we are not in suspend active, otherwise need to + * restore the device + */ + if (skl->supend_active) { + pci_restore_state(pci); + ret = pci_enable_device(pci); + } else { + ret = _skl_resume(ebus); + } - return 0; + return ret; } #endif /* CONFIG_PM_SLEEP */ @@ -166,24 +212,10 @@ static int skl_runtime_suspend(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl *skl = ebus_to_skl(ebus); - int ret; dev_dbg(bus->dev, "in %s\n", __func__); - /* enable controller wake up event */ - snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK); - - snd_hdac_ext_bus_link_power_down_all(ebus); - - ret = skl_suspend_dsp(skl); - if (ret < 0) - return ret; - - snd_hdac_bus_stop_chip(bus); - snd_hdac_bus_enter_link_reset(bus); - - return 0; + return _skl_suspend(ebus); } static int skl_runtime_resume(struct device *dev) @@ -191,20 +223,10 @@ static int skl_runtime_resume(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl *skl = ebus_to_skl(ebus); - int status; dev_dbg(bus->dev, "in %s\n", __func__); - /* Read STATESTS before controller reset */ - status = snd_hdac_chip_readw(bus, STATESTS); - - skl_init_pci(skl); - snd_hdac_bus_init_chip(bus, true); - /* disable controller Wake Up event */ - snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0); - - return skl_resume_dsp(skl); + return _skl_resume(ebus); } #endif /* CONFIG_PM */ @@ -241,6 +263,43 @@ static int skl_free(struct hdac_ext_bus *ebus) return 0; } +static int skl_machine_device_register(struct skl *skl, void *driver_data) +{ + struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); + struct platform_device *pdev; + struct sst_acpi_mach *mach = driver_data; + int ret; + + mach = sst_acpi_find_machine(mach); + if (mach == NULL) { + dev_err(bus->dev, "No matching machine driver found\n"); + return -ENODEV; + } + skl->fw_name = mach->fw_filename; + + pdev = platform_device_alloc(mach->drv_name, -1); + if (pdev == NULL) { + dev_err(bus->dev, "platform device alloc failed\n"); + return -EIO; + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(bus->dev, "failed to add machine device\n"); + platform_device_put(pdev); + return -EIO; + } + skl->i2s_dev = pdev; + + return 0; +} + +static void skl_machine_device_unregister(struct skl *skl) +{ + if (skl->i2s_dev) + platform_device_unregister(skl->i2s_dev); +} + static int skl_dmic_device_register(struct skl *skl) { struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); @@ -434,8 +493,7 @@ static int skl_first_init(struct hdac_ext_bus *ebus) /* codec detection */ if (!bus->codec_mask) { - dev_err(bus->dev, "no codecs found!\n"); - return -ENODEV; + dev_info(bus->dev, "no hda codecs found!\n"); } return 0; @@ -470,10 +528,15 @@ static int skl_probe(struct pci_dev *pci, /* check if dsp is there */ if (ebus->ppcap) { + err = skl_machine_device_register(skl, + (void *)pci_id->driver_data); + if (err < 0) + goto out_free; + err = skl_init_dsp(skl); if (err < 0) { dev_dbg(bus->dev, "error failed to register dsp\n"); - goto out_free; + goto out_mach_free; } } if (ebus->mlcap) @@ -508,6 +571,8 @@ out_dmic_free: skl_dmic_device_unregister(skl); out_dsp_free: skl_free_dsp(skl); +out_mach_free: + skl_machine_device_unregister(skl); out_free: skl->init_failed = 1; skl_free(ebus); @@ -525,15 +590,26 @@ static void skl_remove(struct pci_dev *pci) pci_dev_put(pci); skl_platform_unregister(&pci->dev); skl_free_dsp(skl); + skl_machine_device_unregister(skl); skl_dmic_device_unregister(skl); skl_free(ebus); dev_set_drvdata(&pci->dev, NULL); } +static struct sst_acpi_mach sst_skl_devdata[] = { + { "INT343A", "skl_alc286s_i2s", "intel/dsp_fw_release.bin", NULL, NULL, NULL }, + { "INT343B", "skl_nau88l25_ssm4567_i2s", "intel/dsp_fw_release.bin", + NULL, NULL, NULL }, + { "MX98357A", "skl_nau88l25_max98357a_i2s", "intel/dsp_fw_release.bin", + NULL, NULL, NULL }, + {} +}; + /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ - { PCI_DEVICE(0x8086, 0x9d70), 0}, + { PCI_DEVICE(0x8086, 0x9d70), + .driver_data = (unsigned long)&sst_skl_devdata}, { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index dd2e79ae45a8..3d167eed0f59 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -61,13 +61,17 @@ struct skl { unsigned int init_failed:1; /* delayed init failed */ struct platform_device *dmic_dev; + struct platform_device *i2s_dev; void *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ struct skl_dsp_resource resource; struct list_head ppl_list; - struct list_head dapm_path_list; + + const char *fw_name; + + int supend_active; }; #define skl_to_ebus(s) (&(s)->ebus) diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h index cc4393cb1130..9b1af1a70874 100644 --- a/sound/soc/mediatek/mtk-afe-common.h +++ b/sound/soc/mediatek/mtk-afe-common.h @@ -92,7 +92,6 @@ struct mtk_afe_memif_data { struct mtk_afe_memif { unsigned int phys_buf_addr; int buffer_size; - unsigned int hw_ptr; /* Previous IRQ's HW ptr */ struct snd_pcm_substream *substream; const struct mtk_afe_memif_data *data; const struct mtk_afe_irq_data *irqdata; diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c index f5baf3c38863..08af9f5dc4ab 100644 --- a/sound/soc/mediatek/mtk-afe-pcm.c +++ b/sound/soc/mediatek/mtk-afe-pcm.c @@ -175,8 +175,17 @@ static snd_pcm_uframes_t mtk_afe_pcm_pointer struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + unsigned int hw_ptr; + int ret; + + ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur, &hw_ptr); + if (ret || hw_ptr == 0) { + dev_err(afe->dev, "%s hw_ptr err\n", __func__); + hw_ptr = memif->phys_buf_addr; + } - return bytes_to_frames(substream->runtime, memif->hw_ptr); + return bytes_to_frames(substream->runtime, + hw_ptr - memif->phys_buf_addr); } static const struct snd_pcm_ops mtk_afe_pcm_ops = { @@ -299,8 +308,6 @@ static int mtk_afe_dais_enable_clks(struct mtk_afe *afe, dev_err(afe->dev, "Failed to enable m_ck\n"); return ret; } - regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, - AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0); } if (b_ck) { @@ -340,12 +347,8 @@ static int mtk_afe_dais_set_clks(struct mtk_afe *afe, static void mtk_afe_dais_disable_clks(struct mtk_afe *afe, struct clk *m_ck, struct clk *b_ck) { - if (m_ck) { - regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, - AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, - AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M); + if (m_ck) clk_disable_unprepare(m_ck); - } if (b_ck) clk_disable_unprepare(b_ck); } @@ -360,6 +363,8 @@ static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream, return 0; mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0); return 0; } @@ -373,10 +378,10 @@ static void mtk_afe_i2s_shutdown(struct snd_pcm_substream *substream, return; mtk_afe_set_i2s_enable(afe, false); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, + AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M); mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); - - /* disable AFE */ - regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0); } static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream, @@ -425,9 +430,6 @@ static void mtk_afe_hdmi_shutdown(struct snd_pcm_substream *substream, mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S3_M], afe->clocks[MTK_CLK_I2S3_B]); - - /* disable AFE */ - regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0); } static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream, @@ -603,7 +605,6 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream, memif->phys_buf_addr = substream->runtime->dma_addr; memif->buffer_size = substream->runtime->dma_bytes; - memif->hw_ptr = 0; /* start */ regmap_write(afe->regmap, @@ -672,17 +673,6 @@ static int mtk_afe_dais_hw_free(struct snd_pcm_substream *substream, return snd_pcm_lib_free_pages(substream); } -static int mtk_afe_dais_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); - - /* enable AFE */ - regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1); - return 0; -} - static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -738,7 +728,6 @@ static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd, /* and clear pending IRQ */ regmap_write(afe->regmap, AFE_IRQ_CLR, 1 << memif->data->irq_clr_shift); - memif->hw_ptr = 0; return 0; default: return -EINVAL; @@ -751,7 +740,6 @@ static const struct snd_soc_dai_ops mtk_afe_dai_ops = { .shutdown = mtk_afe_dais_shutdown, .hw_params = mtk_afe_dais_hw_params, .hw_free = mtk_afe_dais_hw_free, - .prepare = mtk_afe_dais_prepare, .trigger = mtk_afe_dais_trigger, }; @@ -1082,7 +1070,7 @@ static const struct regmap_config mtk_afe_regmap_config = { static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id) { struct mtk_afe *afe = dev_id; - unsigned int reg_value, hw_ptr; + unsigned int reg_value; int i, ret; ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, ®_value); @@ -1098,13 +1086,6 @@ static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id) if (!(reg_value & (1 << memif->data->irq_clr_shift))) continue; - ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur, - &hw_ptr); - if (ret || hw_ptr == 0) { - dev_err(afe->dev, "%s hw_ptr err\n", __func__); - hw_ptr = memif->phys_buf_addr; - } - memif->hw_ptr = hw_ptr - memif->phys_buf_addr; snd_pcm_period_elapsed(memif->substream); } @@ -1119,6 +1100,9 @@ static int mtk_afe_runtime_suspend(struct device *dev) { struct mtk_afe *afe = dev_get_drvdata(dev); + /* disable AFE */ + regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0); + /* disable AFE clk */ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE); @@ -1165,6 +1149,9 @@ static int mtk_afe_runtime_resume(struct device *dev) /* unmask all IRQs */ regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN, 0xff, 0xff); + + /* enable AFE */ + regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1); return 0; err_bck0: diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c index 584b2372339e..f83cc2bc0fc4 100644 --- a/sound/soc/omap/omap-hdmi-audio.c +++ b/sound/soc/omap/omap-hdmi-audio.c @@ -368,6 +368,8 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) card->owner = THIS_MODULE; card->dai_link = devm_kzalloc(dev, sizeof(*(card->dai_link)), GFP_KERNEL); + if (!card->dai_link) + return -ENOMEM; card->dai_link->name = card->name; card->dai_link->stream_name = card->name; card->dai_link->cpu_dai_name = dev_name(ad->dssdev); diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c index 6147e86e9b0f..416ea646c3b1 100644 --- a/sound/soc/pxa/brownstone.c +++ b/sound/soc/pxa/brownstone.c @@ -63,8 +63,7 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream, sysclk = params_rate(params) * 512; sspa_mclk = params_rate(params) * 64; } - sspa_div = freq_out; - do_div(sspa_div, sspa_mclk); + sspa_div = freq_out / sspa_mclk; snd_soc_dai_set_sysclk(cpu_dai, MMP_SSPA_CLK_AUDIO, freq_out, 0); snd_soc_dai_set_pll(cpu_dai, MMP_SYSCLK, 0, freq_out, sysclk); diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index 29bc60e85e92..5c8f9db50a47 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -81,8 +81,12 @@ static int rear_amp_power(struct snd_soc_codec *codec, int power) static int rear_amp_event(struct snd_soc_dapm_widget *widget, struct snd_kcontrol *kctl, int event) { - struct snd_soc_codec *codec = widget->dapm->card->rtd[0].codec; + struct snd_soc_card *card = widget->dapm->card; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_codec *codec; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + codec = rtd->codec; return rear_amp_power(codec, SND_SOC_DAPM_EVENT_ON(event)); } diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index e5101e0d2d37..00b6c9d039cf 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -355,6 +355,7 @@ static struct regmap_config lpass_cpu_regmap_config = { .readable_reg = lpass_cpu_regmap_readable, .volatile_reg = lpass_cpu_regmap_volatile, .cache_type = REGCACHE_FLAT, + .val_format_endian = REGMAP_ENDIAN_LITTLE, }; int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 58ee64594f07..8b0a588ed622 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -34,13 +34,7 @@ struct rk_i2s_dev { struct regmap *regmap; -/* - * Used to indicate the tx/rx status. - * I2S controller hopes to start the tx and rx together, - * also to stop them when they are both try to stop. -*/ - bool tx_start; - bool rx_start; + bool is_master_mode; }; static int i2s_runtime_suspend(struct device *dev) @@ -81,37 +75,29 @@ static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on) I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_ENABLE); regmap_update_bits(i2s->regmap, I2S_XFER, - I2S_XFER_TXS_START | I2S_XFER_RXS_START, - I2S_XFER_TXS_START | I2S_XFER_RXS_START); - - i2s->tx_start = true; + I2S_XFER_TXS_START, + I2S_XFER_TXS_START); } else { - i2s->tx_start = false; - regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE); - if (!i2s->rx_start) { - regmap_update_bits(i2s->regmap, I2S_XFER, - I2S_XFER_TXS_START | - I2S_XFER_RXS_START, - I2S_XFER_TXS_STOP | - I2S_XFER_RXS_STOP); + regmap_update_bits(i2s->regmap, I2S_XFER, + I2S_XFER_TXS_START, + I2S_XFER_TXS_STOP); - regmap_update_bits(i2s->regmap, I2S_CLR, - I2S_CLR_TXC | I2S_CLR_RXC, - I2S_CLR_TXC | I2S_CLR_RXC); + regmap_update_bits(i2s->regmap, I2S_CLR, + I2S_CLR_TXC, + I2S_CLR_TXC); - regmap_read(i2s->regmap, I2S_CLR, &val); + regmap_read(i2s->regmap, I2S_CLR, &val); - /* Should wait for clear operation to finish */ - while (val) { - regmap_read(i2s->regmap, I2S_CLR, &val); - retry--; - if (!retry) { - dev_warn(i2s->dev, "fail to clear\n"); - break; - } + /* Should wait for clear operation to finish */ + while (val & I2S_CLR_TXC) { + regmap_read(i2s->regmap, I2S_CLR, &val); + retry--; + if (!retry) { + dev_warn(i2s->dev, "fail to clear\n"); + break; } } } @@ -127,37 +113,29 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on) I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_ENABLE); regmap_update_bits(i2s->regmap, I2S_XFER, - I2S_XFER_TXS_START | I2S_XFER_RXS_START, - I2S_XFER_TXS_START | I2S_XFER_RXS_START); - - i2s->rx_start = true; + I2S_XFER_RXS_START, + I2S_XFER_RXS_START); } else { - i2s->rx_start = false; - regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_DISABLE); - if (!i2s->tx_start) { - regmap_update_bits(i2s->regmap, I2S_XFER, - I2S_XFER_TXS_START | - I2S_XFER_RXS_START, - I2S_XFER_TXS_STOP | - I2S_XFER_RXS_STOP); + regmap_update_bits(i2s->regmap, I2S_XFER, + I2S_XFER_RXS_START, + I2S_XFER_RXS_STOP); - regmap_update_bits(i2s->regmap, I2S_CLR, - I2S_CLR_TXC | I2S_CLR_RXC, - I2S_CLR_TXC | I2S_CLR_RXC); + regmap_update_bits(i2s->regmap, I2S_CLR, + I2S_CLR_RXC, + I2S_CLR_RXC); - regmap_read(i2s->regmap, I2S_CLR, &val); + regmap_read(i2s->regmap, I2S_CLR, &val); - /* Should wait for clear operation to finish */ - while (val) { - regmap_read(i2s->regmap, I2S_CLR, &val); - retry--; - if (!retry) { - dev_warn(i2s->dev, "fail to clear\n"); - break; - } + /* Should wait for clear operation to finish */ + while (val & I2S_CLR_RXC) { + regmap_read(i2s->regmap, I2S_CLR, &val); + retry--; + if (!retry) { + dev_warn(i2s->dev, "fail to clear\n"); + break; } } } @@ -174,9 +152,11 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai, case SND_SOC_DAIFMT_CBS_CFS: /* Set source clock in Master mode */ val = I2S_CKR_MSS_MASTER; + i2s->is_master_mode = true; break; case SND_SOC_DAIFMT_CBM_CFM: val = I2S_CKR_MSS_SLAVE; + i2s->is_master_mode = false; break; default: return -EINVAL; @@ -228,6 +208,26 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, struct rk_i2s_dev *i2s = to_info(dai); struct snd_soc_pcm_runtime *rtd = substream->private_data; unsigned int val = 0; + unsigned int mclk_rate, bclk_rate, div_bclk, div_lrck; + + if (i2s->is_master_mode) { + mclk_rate = clk_get_rate(i2s->mclk); + bclk_rate = 2 * 32 * params_rate(params); + if (bclk_rate && mclk_rate % bclk_rate) + return -EINVAL; + + div_bclk = mclk_rate / bclk_rate; + div_lrck = bclk_rate / params_rate(params); + regmap_update_bits(i2s->regmap, I2S_CKR, + I2S_CKR_MDIV_MASK, + I2S_CKR_MDIV(div_bclk)); + + regmap_update_bits(i2s->regmap, I2S_CKR, + I2S_CKR_TSD_MASK | + I2S_CKR_RSD_MASK, + I2S_CKR_TSD(div_lrck) | + I2S_CKR_RSD(div_lrck)); + } switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: @@ -451,6 +451,7 @@ static int rockchip_i2s_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; struct rk_i2s_dev *i2s; + struct snd_soc_dai_driver *soc_dai; struct resource *res; void __iomem *regs; int ret; @@ -511,17 +512,26 @@ static int rockchip_i2s_probe(struct platform_device *pdev) goto err_pm_disable; } - /* refine capture channels */ + soc_dai = devm_kzalloc(&pdev->dev, + sizeof(*soc_dai), GFP_KERNEL); + if (!soc_dai) + return -ENOMEM; + + memcpy(soc_dai, &rockchip_i2s_dai, sizeof(*soc_dai)); + if (!of_property_read_u32(node, "rockchip,playback-channels", &val)) { + if (val >= 2 && val <= 8) + soc_dai->playback.channels_max = val; + } + if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) { if (val >= 2 && val <= 8) - rockchip_i2s_dai.capture.channels_max = val; - else - rockchip_i2s_dai.capture.channels_max = 2; + soc_dai->capture.channels_max = val; } ret = devm_snd_soc_register_component(&pdev->dev, &rockchip_i2s_component, - &rockchip_i2s_dai, 1); + soc_dai, 1); + if (ret) { dev_err(&pdev->dev, "Could not register DAI\n"); goto err_suspend; diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c index 26567b10393a..543610282cdb 100644 --- a/sound/soc/rockchip/rockchip_max98090.c +++ b/sound/soc/rockchip/rockchip_max98090.c @@ -80,11 +80,17 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream, switch (params_rate(params)) { case 8000: case 16000: + case 24000: + case 32000: case 48000: + case 64000: case 96000: mclk = 12288000; break; + case 11025: + case 22050: case 44100: + case 88200: mclk = 11289600; break; default: diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c index 68c62e4c2316..440a8026346a 100644 --- a/sound/soc/rockchip/rockchip_rt5645.c +++ b/sound/soc/rockchip/rockchip_rt5645.c @@ -79,11 +79,17 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream, switch (params_rate(params)) { case 8000: case 16000: + case 24000: + case 32000: case 48000: + case 64000: case 96000: mclk = 12288000; break; + case 11025: + case 22050: case 44100: + case 88200: mclk = 11289600; break; default: diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index a38a3029062c..5a806da89f42 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -152,8 +152,10 @@ static int rk_spdif_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR, - SPDIF_DMACR_TDE_ENABLE, - SPDIF_DMACR_TDE_ENABLE); + SPDIF_DMACR_TDE_ENABLE | + SPDIF_DMACR_TDL_MASK, + SPDIF_DMACR_TDE_ENABLE | + SPDIF_DMACR_TDL(16)); if (ret != 0) return ret; @@ -280,7 +282,7 @@ static int rk_spdif_probe(struct platform_device *pdev) int ret; match = of_match_node(rk_spdif_match, np); - if ((int) match->data == RK_SPDIF_RK3288) { + if (match->data == (void *)RK_SPDIF_RK3288) { struct regmap *grf; grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); diff --git a/sound/soc/rockchip/rockchip_spdif.h b/sound/soc/rockchip/rockchip_spdif.h index 07f86a21046a..3ef12770ae12 100644 --- a/sound/soc/rockchip/rockchip_spdif.h +++ b/sound/soc/rockchip/rockchip_spdif.h @@ -28,9 +28,9 @@ #define SPDIF_CFGR_VDW(x) (x << SPDIF_CFGR_VDW_SHIFT) #define SDPIF_CFGR_VDW_MASK (0xf << SPDIF_CFGR_VDW_SHIFT) -#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x00) -#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x01) -#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x10) +#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x0) +#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x1) +#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x2) /* * DMACR @@ -42,7 +42,7 @@ #define SPDIF_DMACR_TDL_SHIFT 0 #define SPDIF_DMACR_TDL(x) ((x) << SPDIF_DMACR_TDL_SHIFT) -#define SPDIF_DMACR_TDL_MASK (0x1f << SDPIF_DMACR_TDL_SHIFT) +#define SPDIF_DMACR_TDL_MASK (0x1f << SPDIF_DMACR_TDL_SHIFT) /* * XFER diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c index e5f05e62fa3c..3dd246fa0059 100644 --- a/sound/soc/samsung/bells.c +++ b/sound/soc/samsung/bells.c @@ -58,11 +58,16 @@ static int bells_set_bias_level(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { - struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai; - struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *codec_dai; + struct snd_soc_codec *codec; struct bells_drvdata *bells = card->drvdata; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name); + codec_dai = rtd->codec_dai; + codec = codec_dai->codec; + if (dapm->dev != codec_dai->dev) return 0; @@ -99,11 +104,16 @@ static int bells_set_bias_level_post(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { - struct snd_soc_dai *codec_dai = card->rtd[DAI_DSP_CODEC].codec_dai; - struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *codec_dai; + struct snd_soc_codec *codec; struct bells_drvdata *bells = card->drvdata; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name); + codec_dai = rtd->codec_dai; + codec = codec_dai->codec; + if (dapm->dev != codec_dai->dev) return 0; @@ -137,14 +147,22 @@ static int bells_set_bias_level_post(struct snd_soc_card *card, static int bells_late_probe(struct snd_soc_card *card) { struct bells_drvdata *bells = card->drvdata; - struct snd_soc_codec *wm0010 = card->rtd[DAI_AP_DSP].codec; - struct snd_soc_codec *codec = card->rtd[DAI_DSP_CODEC].codec; - struct snd_soc_dai *aif1_dai = card->rtd[DAI_DSP_CODEC].codec_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_codec *wm0010; + struct snd_soc_codec *codec; + struct snd_soc_dai *aif1_dai; struct snd_soc_dai *aif2_dai; struct snd_soc_dai *aif3_dai; struct snd_soc_dai *wm9081_dai; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_AP_DSP].name); + wm0010 = rtd->codec; + + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name); + codec = rtd->codec; + aif1_dai = rtd->codec_dai; + ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK, ARIZONA_CLK_SRC_FLL1, bells->sysclk_rate, @@ -181,7 +199,8 @@ static int bells_late_probe(struct snd_soc_card *card) return ret; } - aif2_dai = card->rtd[DAI_CODEC_CP].cpu_dai; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_CODEC_CP].name); + aif2_dai = rtd->cpu_dai; ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0); if (ret != 0) { @@ -192,8 +211,9 @@ static int bells_late_probe(struct snd_soc_card *card) if (card->num_rtd == DAI_CODEC_SUB) return 0; - aif3_dai = card->rtd[DAI_CODEC_SUB].cpu_dai; - wm9081_dai = card->rtd[DAI_CODEC_SUB].codec_dai; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_CODEC_SUB].name); + aif3_dai = rtd->cpu_dai; + wm9081_dai = rtd->codec_dai; ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0); if (ret != 0) { diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c index 31a820eb0ac3..7cb204e649ca 100644 --- a/sound/soc/samsung/littlemill.c +++ b/sound/soc/samsung/littlemill.c @@ -23,9 +23,13 @@ static int littlemill_set_bias_level(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { - struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *aif1_dai; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + aif1_dai = rtd->codec_dai; + if (dapm->dev != aif1_dai->dev) return 0; @@ -66,9 +70,13 @@ static int littlemill_set_bias_level_post(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { - struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *aif1_dai; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + aif1_dai = rtd->codec_dai; + if (dapm->dev != aif1_dai->dev) return 0; @@ -168,9 +176,13 @@ static int bbclk_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_card *card = w->dapm->card; - struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *aif2_dai; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name); + aif2_dai = rtd->cpu_dai; + switch (event) { case SND_SOC_DAPM_PRE_PMU: ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2, @@ -245,11 +257,19 @@ static struct snd_soc_jack littlemill_headset; static int littlemill_late_probe(struct snd_soc_card *card) { - struct snd_soc_codec *codec = card->rtd[0].codec; - struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; - struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_codec *codec; + struct snd_soc_dai *aif1_dai; + struct snd_soc_dai *aif2_dai; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + codec = rtd->codec; + aif1_dai = rtd->codec_dai; + + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name); + aif2_dai = rtd->cpu_dai; + ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, 32768, SND_SOC_CLOCK_IN); if (ret < 0) diff --git a/sound/soc/samsung/odroidx2_max98090.c b/sound/soc/samsung/odroidx2_max98090.c index 596f1180a369..04217279fe25 100644 --- a/sound/soc/samsung/odroidx2_max98090.c +++ b/sound/soc/samsung/odroidx2_max98090.c @@ -25,10 +25,15 @@ static struct snd_soc_dai_link odroidx2_dai[]; static int odroidx2_late_probe(struct snd_soc_card *card) { - struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; - struct snd_soc_dai *cpu_dai = card->rtd[0].cpu_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *codec_dai; + struct snd_soc_dai *cpu_dai; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + codec_dai = rtd->codec_dai; + cpu_dai = rtd->cpu_dai; + ret = snd_soc_dai_set_sysclk(codec_dai, 0, MAX98090_MCLK, SND_SOC_CLOCK_IN); diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c index 07ce2cfa4845..d8ac907bbb0d 100644 --- a/sound/soc/samsung/snow.c +++ b/sound/soc/samsung/snow.c @@ -35,10 +35,15 @@ static struct snd_soc_dai_link snow_dai[] = { static int snow_late_probe(struct snd_soc_card *card) { - struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; - struct snd_soc_dai *cpu_dai = card->rtd[0].cpu_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *codec_dai; + struct snd_soc_dai *cpu_dai; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + codec_dai = rtd->codec_dai; + cpu_dai = rtd->cpu_dai; + /* Set the MCLK rate for the codec */ ret = snd_soc_dai_set_sysclk(codec_dai, 0, FIN_PLL_RATE, SND_SOC_CLOCK_IN); diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index d1ae21c5e253..083ef5e21b17 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -25,9 +25,13 @@ static int speyside_set_bias_level(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { - struct snd_soc_dai *codec_dai = card->rtd[1].codec_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *codec_dai; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name); + codec_dai = rtd->codec_dai; + if (dapm->dev != codec_dai->dev) return 0; @@ -57,9 +61,13 @@ static int speyside_set_bias_level_post(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { - struct snd_soc_dai *codec_dai = card->rtd[1].codec_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *codec_dai; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[1].name); + codec_dai = rtd->codec_dai; + if (dapm->dev != codec_dai->dev) return 0; diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c index 85ccfb7188cb..3310eda7cf53 100644 --- a/sound/soc/samsung/tobermory.c +++ b/sound/soc/samsung/tobermory.c @@ -23,9 +23,13 @@ static int tobermory_set_bias_level(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { - struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *codec_dai; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + codec_dai = rtd->codec_dai; + if (dapm->dev != codec_dai->dev) return 0; @@ -62,9 +66,13 @@ static int tobermory_set_bias_level_post(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { - struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai *codec_dai; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + codec_dai = rtd->codec_dai; + if (dapm->dev != codec_dai->dev) return 0; @@ -170,10 +178,15 @@ static struct snd_soc_jack_pin tobermory_headset_pins[] = { static int tobermory_late_probe(struct snd_soc_card *card) { - struct snd_soc_codec *codec = card->rtd[0].codec; - struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_codec *codec; + struct snd_soc_dai *codec_dai; int ret; + rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); + codec = rtd->codec; + codec_dai = rtd->codec_dai; + ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK, 32768, SND_SOC_CLOCK_IN); if (ret < 0) diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 206d1edab07c..c9902a6d6fa0 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -36,7 +36,6 @@ config SND_SOC_SH4_SIU config SND_SOC_RCAR tristate "R-Car series SRU/SCU/SSIU/SSI support" - depends on DMA_OF depends on COMMON_CLK select SND_SIMPLE_CARD select REGMAP_MMIO diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index 8b258501aa35..a89ddf758695 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,4 +1,4 @@ -snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o src.o ctu.o mix.o dvc.o +snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o snd-soc-rsrc-card-objs := rsrc-card.o diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 2a5b3a293cd2..6d3ef366d536 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -68,8 +68,8 @@ static u32 rsnd_adg_calculate_rbgx(unsigned long div) static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) { - struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); - int id = rsnd_mod_id(mod); + struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); + int id = rsnd_mod_id(ssi_mod); int ws = id; if (rsnd_ssi_is_pin_sharing(io)) { @@ -90,13 +90,13 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) return (0x6 + ws) << 8; } -int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, +int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, struct rsnd_dai_stream *io) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(cmd_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - int id = rsnd_mod_id(mod); + int id = rsnd_mod_id(cmd_mod); int shift = (id % 2) ? 16 : 0; u32 mask, val; @@ -242,68 +242,6 @@ int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod, return rsnd_adg_set_src_timsel_gen2(src_mod, io, val); } -int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, - struct rsnd_mod *mod, - unsigned int src_rate, - unsigned int dst_rate) -{ - struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - struct device *dev = rsnd_priv_to_dev(priv); - int idx, sel, div, shift; - u32 mask, val; - int id = rsnd_mod_id(mod); - unsigned int sel_rate [] = { - clk_get_rate(adg->clk[CLKA]), /* 000: CLKA */ - clk_get_rate(adg->clk[CLKB]), /* 001: CLKB */ - clk_get_rate(adg->clk[CLKC]), /* 010: CLKC */ - 0, /* 011: MLBCLK (not used) */ - adg->rbga_rate_for_441khz, /* 100: RBGA */ - adg->rbgb_rate_for_48khz, /* 101: RBGB */ - }; - - /* find div (= 1/128, 1/256, 1/512, 1/1024, 1/2048 */ - for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { - for (div = 128, idx = 0; - div <= 2048; - div *= 2, idx++) { - if (src_rate == sel_rate[sel] / div) { - val = (idx << 4) | sel; - goto find_rate; - } - } - } - dev_err(dev, "can't find convert src clk\n"); - return -EINVAL; - -find_rate: - shift = (id % 4) * 8; - mask = 0xFF << shift; - val = val << shift; - - dev_dbg(dev, "adg convert src clk = %02x\n", val); - - switch (id / 4) { - case 0: - rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL3, mask, val); - break; - case 1: - rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL4, mask, val); - break; - case 2: - rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL5, mask, val); - break; - } - - /* - * Gen1 doesn't need dst_rate settings, - * since it uses SSI WS pin. - * see also rsnd_src_set_route_if_gen1() - */ - - return 0; -} - static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) { struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); @@ -337,20 +275,16 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) } } -int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod) +int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod) { - /* - * "mod" = "ssi" here. - * we can get "ssi id" from mod - */ - rsnd_adg_set_ssi_clk(mod, 0); + rsnd_adg_set_ssi_clk(ssi_mod, 0); return 0; } -int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) +int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct device *dev = rsnd_priv_to_dev(priv); struct clk *clk; @@ -394,14 +328,10 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) found_clock: - /* - * This "mod" = "ssi" here. - * we can get "ssi id" from mod - */ - rsnd_adg_set_ssi_clk(mod, data); + rsnd_adg_set_ssi_clk(ssi_mod, data); dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), + rsnd_mod_name(ssi_mod), rsnd_mod_id(ssi_mod), data, rate); return 0; @@ -418,15 +348,20 @@ static void rsnd_adg_get_clkin(struct rsnd_priv *priv, [CLKC] = "clk_c", [CLKI] = "clk_i", }; - int i; + int i, ret; for (i = 0; i < CLKMAX; i++) { clk = devm_clk_get(dev, clk_name[i]); adg->clk[i] = IS_ERR(clk) ? NULL : clk; } - for_each_rsnd_clk(clk, adg, i) + for_each_rsnd_clk(clk, adg, i) { + ret = clk_prepare_enable(clk); + if (ret < 0) + dev_warn(dev, "can't use clk %d\n", i); + dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk)); + } } static void rsnd_adg_get_clkout(struct rsnd_priv *priv, @@ -437,7 +372,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, struct device *dev = rsnd_priv_to_dev(priv); struct device_node *np = dev->of_node; u32 ckr, rbgx, rbga, rbgb; - u32 rate, req_rate, div; + u32 rate, req_rate = 0, div; uint32_t count = 0; unsigned long req_48kHz_rate, req_441kHz_rate; int i; @@ -572,9 +507,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, ckr, rbga, rbgb); } -int rsnd_adg_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) +int rsnd_adg_probe(struct rsnd_priv *priv) { struct rsnd_adg *adg; struct device *dev = rsnd_priv_to_dev(priv); @@ -600,3 +533,14 @@ int rsnd_adg_probe(struct platform_device *pdev, return 0; } + +void rsnd_adg_remove(struct rsnd_priv *priv) +{ + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct clk *clk; + int i; + + for_each_rsnd_clk(clk, adg, i) { + clk_disable_unprepare(clk); + } +} diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c new file mode 100644 index 000000000000..cd1f064e63c4 --- /dev/null +++ b/sound/soc/sh/rcar/cmd.c @@ -0,0 +1,171 @@ +/* + * Renesas R-Car CMD support + * + * Copyright (C) 2015 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.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. + */ +#include "rsnd.h" + +struct rsnd_cmd { + struct rsnd_mod mod; +}; + +#define CMD_NAME "cmd" + +#define rsnd_cmd_nr(priv) ((priv)->cmd_nr) +#define for_each_rsnd_cmd(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_cmd_nr(priv)) && \ + ((pos) = (struct rsnd_cmd *)(priv)->cmd + i); \ + i++) + +static int rsnd_cmd_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); + struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); + struct rsnd_mod *src = rsnd_io_to_mod_src(io); + struct device *dev = rsnd_priv_to_dev(priv); + u32 data; + + if (!mix && !dvc) + return 0; + + if (mix) { + struct rsnd_dai *rdai; + int i; + u32 path[] = { + [0] = 0, + [1] = 1 << 0, + [2] = 0, + [3] = 0, + [4] = 0, + [5] = 1 << 8 + }; + + /* + * it is assuming that integrater is well understanding about + * data path. Here doesn't check impossible connection, + * like src2 + src5 + */ + data = 0; + for_each_rsnd_dai(rdai, priv, i) { + io = &rdai->playback; + if (mix == rsnd_io_to_mod_mix(io)) + data |= path[rsnd_mod_id(src)]; + + io = &rdai->capture; + if (mix == rsnd_io_to_mod_mix(io)) + data |= path[rsnd_mod_id(src)]; + } + + } else { + u32 path[] = { + [0] = 0x30000, + [1] = 0x30001, + [2] = 0x40000, + [3] = 0x10000, + [4] = 0x20000, + [5] = 0x40100 + }; + + data = path[rsnd_mod_id(src)]; + } + + dev_dbg(dev, "ctu/mix path = 0x%08x", data); + + rsnd_mod_write(mod, CMD_ROUTE_SLCT, data); + rsnd_mod_write(mod, CMD_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); + + rsnd_adg_set_cmd_timsel_gen2(mod, io); + + return 0; +} + +static int rsnd_cmd_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + rsnd_mod_write(mod, CMD_CTRL, 0x10); + + return 0; +} + +static int rsnd_cmd_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + rsnd_mod_write(mod, CMD_CTRL, 0); + + return 0; +} + +static struct rsnd_mod_ops rsnd_cmd_ops = { + .name = CMD_NAME, + .init = rsnd_cmd_init, + .start = rsnd_cmd_start, + .stop = rsnd_cmd_stop, +}; + +int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id) +{ + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct rsnd_mod *mod = rsnd_cmd_mod_get(priv, id); + + return rsnd_dai_connect(mod, io, mod->type); +} + +struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_cmd_nr(priv))) + id = 0; + + return rsnd_mod_get((struct rsnd_cmd *)(priv->cmd) + id); +} + +int rsnd_cmd_probe(struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_cmd *cmd; + int i, nr, ret; + + /* This driver doesn't support Gen1 at this point */ + if (rsnd_is_gen1(priv)) + return 0; + + /* same number as DVC */ + nr = priv->dvc_nr; + if (!nr) + return 0; + + cmd = devm_kzalloc(dev, sizeof(*cmd) * nr, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + priv->cmd_nr = nr; + priv->cmd = cmd; + + for_each_rsnd_cmd(cmd, priv, i) { + ret = rsnd_mod_init(priv, rsnd_mod_get(cmd), + &rsnd_cmd_ops, NULL, RSND_MOD_CMD, i); + if (ret) + return ret; + } + + return 0; +} + +void rsnd_cmd_remove(struct rsnd_priv *priv) +{ + struct rsnd_cmd *cmd; + int i; + + for_each_rsnd_cmd(cmd, priv, i) { + rsnd_mod_quit(rsnd_mod_get(cmd)); + } +} diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index deed48ef28b8..02b4b085b8d7 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -99,34 +99,17 @@ #define RSND_RATES SNDRV_PCM_RATE_8000_96000 #define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) -static const struct rsnd_of_data rsnd_of_data_gen1 = { - .flags = RSND_GEN1, -}; - -static const struct rsnd_of_data rsnd_of_data_gen2 = { - .flags = RSND_GEN2, -}; - static const struct of_device_id rsnd_of_match[] = { - { .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 }, - { .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 }, - { .compatible = "renesas,rcar_sound-gen3", .data = &rsnd_of_data_gen2 }, /* gen2 compatible */ + { .compatible = "renesas,rcar_sound-gen1", .data = (void *)RSND_GEN1 }, + { .compatible = "renesas,rcar_sound-gen2", .data = (void *)RSND_GEN2 }, + { .compatible = "renesas,rcar_sound-gen3", .data = (void *)RSND_GEN2 }, /* gen2 compatible */ {}, }; MODULE_DEVICE_TABLE(of, rsnd_of_match); /* - * rsnd_platform functions + * rsnd_mod functions */ -#define rsnd_platform_call(priv, dai, func, param...) \ - (!(priv->info->func) ? 0 : \ - priv->info->func(param)) - -#define rsnd_is_enable_path(io, name) \ - ((io)->info ? (io)->info->name : NULL) -#define rsnd_info_id(priv, io, name) \ - ((io)->info->name - priv->info->name##_info) - void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type) { if (mod->type != type) { @@ -138,9 +121,6 @@ void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type) } } -/* - * rsnd_mod functions - */ char *rsnd_mod_name(struct rsnd_mod *mod) { if (!mod || !mod->ops) @@ -192,19 +172,16 @@ void rsnd_mod_interrupt(struct rsnd_mod *mod, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_dai_stream *io; struct rsnd_dai *rdai; - int i, j; - - for_each_rsnd_dai(rdai, priv, j) { + int i; - for (i = 0; i < RSND_MOD_MAX; i++) { - io = &rdai->playback; - if (mod == io->mod[i]) - callback(mod, io); + for_each_rsnd_dai(rdai, priv, i) { + io = &rdai->playback; + if (mod == io->mod[mod->type]) + callback(mod, io); - io = &rdai->capture; - if (mod == io->mod[i]) - callback(mod, io); - } + io = &rdai->capture; + if (mod == io->mod[mod->type]) + callback(mod, io); } } @@ -214,6 +191,43 @@ int rsnd_io_is_working(struct rsnd_dai_stream *io) return !!io->substream; } +void rsnd_set_slot(struct rsnd_dai *rdai, + int slots, int num) +{ + rdai->slots = slots; + rdai->slots_num = num; +} + +int rsnd_get_slot(struct rsnd_dai_stream *io) +{ + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + + return rdai->slots; +} + +int rsnd_get_slot_num(struct rsnd_dai_stream *io) +{ + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + + return rdai->slots_num; +} + +int rsnd_get_slot_width(struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + int chan = runtime->channels; + + /* Multi channel Mode */ + if (rsnd_ssi_multi_slaves(io)) + chan /= rsnd_get_slot_num(io); + + /* TDM Extend Mode needs 8ch */ + if (chan == 6) + chan = 8; + + return chan; +} + /* * ADINR function */ @@ -222,21 +236,17 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io) struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); - u32 adinr = runtime->channels; switch (runtime->sample_bits) { case 16: - adinr |= (8 << 16); - break; + return 8 << 16; case 32: - adinr |= (0 << 16); - break; - default: - dev_warn(dev, "not supported sample bits\n"); - return 0; + return 0 << 16; } - return adinr; + dev_warn(dev, "not supported sample bits\n"); + + return 0; } u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io) @@ -267,13 +277,22 @@ u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io) */ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { - struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); - struct rsnd_mod *target = src ? src : ssi; + struct rsnd_mod *target; struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 val = 0x76543210; u32 mask = ~0; + if (rsnd_io_is_play(io)) { + struct rsnd_mod *src = rsnd_io_to_mod_src(io); + + target = src ? src : ssi; + } else { + struct rsnd_mod *cmd = rsnd_io_to_mod_cmd(io); + + target = cmd ? cmd : ssi; + } + mask <<= runtime->channels * 4; val = val & mask; @@ -300,20 +319,22 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) /* * rsnd_dai functions */ -#define rsnd_mod_call(mod, io, func, param...) \ +#define rsnd_mod_call(idx, io, func, param...) \ ({ \ struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ + struct rsnd_mod *mod = (io)->mod[idx]; \ struct device *dev = rsnd_priv_to_dev(priv); \ + u32 *status = (io)->mod_status + idx; \ u32 mask = 0xF << __rsnd_mod_shift_##func; \ - u8 val = (mod->status >> __rsnd_mod_shift_##func) & 0xF; \ + u8 val = (*status >> __rsnd_mod_shift_##func) & 0xF; \ u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ int ret = 0; \ int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \ - mod->status = (mod->status & ~mask) + \ + *status = (*status & ~mask) + \ (add << __rsnd_mod_shift_##func); \ dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \ rsnd_mod_name(mod), rsnd_mod_id(mod), \ - mod->status, call ? #func : ""); \ + *status, call ? #func : ""); \ if (call) \ ret = (mod)->ops->func(mod, io, param); \ ret; \ @@ -327,13 +348,14 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) mod = (io)->mod[i]; \ if (!mod) \ continue; \ - ret |= rsnd_mod_call(mod, io, fn, param); \ + ret |= rsnd_mod_call(i, io, fn, param); \ } \ ret; \ }) -static int rsnd_dai_connect(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) +int rsnd_dai_connect(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + enum rsnd_mod_type type) { struct rsnd_priv *priv; struct device *dev; @@ -341,10 +363,13 @@ static int rsnd_dai_connect(struct rsnd_mod *mod, if (!mod) return -EIO; + if (io->mod[type]) + return -EINVAL; + priv = rsnd_mod_to_priv(mod); dev = rsnd_priv_to_dev(priv); - io->mod[mod->type] = mod; + io->mod[type] = mod; dev_dbg(dev, "%s[%d] is connected to io (%s)\n", rsnd_mod_name(mod), rsnd_mod_id(mod), @@ -354,9 +379,10 @@ static int rsnd_dai_connect(struct rsnd_mod *mod, } static void rsnd_dai_disconnect(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) + struct rsnd_dai_stream *io, + enum rsnd_mod_type type) { - io->mod[mod->type] = NULL; + io->mod[type] = NULL; } struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id) @@ -469,7 +495,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct rsnd_priv *priv = rsnd_dai_to_priv(dai); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); - int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io)); int ret; unsigned long flags; @@ -479,10 +504,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_START: rsnd_dai_stream_init(io, substream); - ret = rsnd_platform_call(priv, dai, start, ssi_id); - if (ret < 0) - goto dai_trigger_end; - ret = rsnd_dai_call(init, io, priv); if (ret < 0) goto dai_trigger_end; @@ -496,8 +517,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, ret |= rsnd_dai_call(quit, io, priv); - ret |= rsnd_platform_call(priv, dai, stop, ssi_id); - rsnd_dai_stream_quit(io); break; default: @@ -567,332 +586,157 @@ static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { - .trigger = rsnd_soc_dai_trigger, - .set_fmt = rsnd_soc_dai_set_fmt, -}; - -#define rsnd_path_add(priv, io, type) \ -({ \ - struct rsnd_mod *mod; \ - int ret = 0; \ - int id = -1; \ - \ - if (rsnd_is_enable_path(io, type)) { \ - id = rsnd_info_id(priv, io, type); \ - if (id >= 0) { \ - mod = rsnd_##type##_mod_get(priv, id); \ - ret = rsnd_dai_connect(mod, io); \ - } \ - } \ - ret; \ -}) - -#define rsnd_path_remove(priv, io, type) \ -{ \ - struct rsnd_mod *mod; \ - int id = -1; \ - \ - if (rsnd_is_enable_path(io, type)) { \ - id = rsnd_info_id(priv, io, type); \ - if (id >= 0) { \ - mod = rsnd_##type##_mod_get(priv, id); \ - rsnd_dai_disconnect(mod, io); \ - } \ - } \ -} - -void rsnd_path_parse(struct rsnd_priv *priv, - struct rsnd_dai_stream *io) +static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai, + u32 tx_mask, u32 rx_mask, + int slots, int slot_width) { - struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); - struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); - struct rsnd_mod *src = rsnd_io_to_mod_src(io); - struct rsnd_mod *cmd; + struct rsnd_priv *priv = rsnd_dai_to_priv(dai); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct device *dev = rsnd_priv_to_dev(priv); - u32 data; - /* Gen1 is not supported */ - if (rsnd_is_gen1(priv)) - return; - - if (!mix && !dvc) - return; - - if (mix) { - struct rsnd_dai *rdai; - int i; - u32 path[] = { - [0] = 0, - [1] = 1 << 0, - [2] = 0, - [3] = 0, - [4] = 0, - [5] = 1 << 8 - }; - - /* - * it is assuming that integrater is well understanding about - * data path. Here doesn't check impossible connection, - * like src2 + src5 - */ - data = 0; - for_each_rsnd_dai(rdai, priv, i) { - io = &rdai->playback; - if (mix == rsnd_io_to_mod_mix(io)) - data |= path[rsnd_mod_id(src)]; - - io = &rdai->capture; - if (mix == rsnd_io_to_mod_mix(io)) - data |= path[rsnd_mod_id(src)]; - } - - /* - * We can't use ctu = rsnd_io_ctu() here. - * Since, ID of dvc/mix are 0 or 1 (= same as CMD number) - * but ctu IDs are 0 - 7 (= CTU00 - CTU13) - */ - cmd = mix; - } else { - u32 path[] = { - [0] = 0x30000, - [1] = 0x30001, - [2] = 0x40000, - [3] = 0x10000, - [4] = 0x20000, - [5] = 0x40100 - }; - - data = path[rsnd_mod_id(src)]; - - cmd = dvc; + switch (slots) { + case 6: + /* TDM Extend Mode */ + rsnd_set_slot(rdai, slots, 1); + break; + default: + dev_err(dev, "unsupported TDM slots (%d)\n", slots); + return -EINVAL; } - dev_dbg(dev, "ctu/mix path = 0x%08x", data); - - rsnd_mod_write(cmd, CMD_ROUTE_SLCT, data); - - rsnd_mod_write(cmd, CMD_CTRL, 0x10); + return 0; } -static int rsnd_path_init(struct rsnd_priv *priv, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - int ret; - - /* - * Gen1 is created by SRU/SSI, and this SRU is base module of - * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU) - * - * Easy image is.. - * Gen1 SRU = Gen2 SCU + SSIU + etc - * - * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is - * using fixed path. - */ - - /* SSI */ - ret = rsnd_path_add(priv, io, ssi); - if (ret < 0) - return ret; - - /* SRC */ - ret = rsnd_path_add(priv, io, src); - if (ret < 0) - return ret; +static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { + .trigger = rsnd_soc_dai_trigger, + .set_fmt = rsnd_soc_dai_set_fmt, + .set_tdm_slot = rsnd_soc_set_dai_tdm_slot, +}; - /* CTU */ - ret = rsnd_path_add(priv, io, ctu); - if (ret < 0) - return ret; +void rsnd_parse_connect_common(struct rsnd_dai *rdai, + struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id), + struct device_node *node, + struct device_node *playback, + struct device_node *capture) +{ + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct device_node *np; + struct rsnd_mod *mod; + int i; - /* MIX */ - ret = rsnd_path_add(priv, io, mix); - if (ret < 0) - return ret; + if (!node) + return; - /* DVC */ - ret = rsnd_path_add(priv, io, dvc); - if (ret < 0) - return ret; + i = 0; + for_each_child_of_node(node, np) { + mod = mod_get(priv, i); + if (np == playback) + rsnd_dai_connect(mod, &rdai->playback, mod->type); + if (np == capture) + rsnd_dai_connect(mod, &rdai->capture, mod->type); + i++; + } - return ret; + of_node_put(node); } -static void rsnd_of_parse_dai(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) +static int rsnd_dai_probe(struct rsnd_priv *priv) { - struct device_node *dai_node, *dai_np; - struct device_node *ssi_node, *ssi_np; - struct device_node *src_node, *src_np; - struct device_node *ctu_node, *ctu_np; - struct device_node *mix_node, *mix_np; - struct device_node *dvc_node, *dvc_np; + struct device_node *dai_node; + struct device_node *dai_np; struct device_node *playback, *capture; - struct rsnd_dai_platform_info *dai_info; - struct rcar_snd_info *info = rsnd_priv_to_info(priv); - struct device *dev = &pdev->dev; - int nr, i; - int dai_i, ssi_i, src_i, ctu_i, mix_i, dvc_i; - - if (!of_data) - return; - - dai_node = of_get_child_by_name(dev->of_node, "rcar_sound,dai"); - if (!dai_node) - return; + struct rsnd_dai_stream *io_playback; + struct rsnd_dai_stream *io_capture; + struct snd_soc_dai_driver *rdrv, *drv; + struct rsnd_dai *rdai; + struct device *dev = rsnd_priv_to_dev(priv); + int nr, dai_i, io_i; + int ret; + dai_node = rsnd_dai_of_node(priv); nr = of_get_child_count(dai_node); - if (!nr) - return; + if (!nr) { + ret = -EINVAL; + goto rsnd_dai_probe_done; + } - dai_info = devm_kzalloc(dev, - sizeof(struct rsnd_dai_platform_info) * nr, - GFP_KERNEL); - if (!dai_info) { - dev_err(dev, "dai info allocation error\n"); - return; + rdrv = devm_kzalloc(dev, sizeof(*rdrv) * nr, GFP_KERNEL); + rdai = devm_kzalloc(dev, sizeof(*rdai) * nr, GFP_KERNEL); + if (!rdrv || !rdai) { + ret = -ENOMEM; + goto rsnd_dai_probe_done; } - info->dai_info_nr = nr; - info->dai_info = dai_info; - - ssi_node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi"); - src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src"); - ctu_node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu"); - mix_node = of_get_child_by_name(dev->of_node, "rcar_sound,mix"); - dvc_node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc"); - -#define mod_parse(name) \ -if (name##_node) { \ - struct rsnd_##name##_platform_info *name##_info; \ - \ - name##_i = 0; \ - for_each_child_of_node(name##_node, name##_np) { \ - name##_info = info->name##_info + name##_i; \ - \ - if (name##_np == playback) \ - dai_info->playback.name = name##_info; \ - if (name##_np == capture) \ - dai_info->capture.name = name##_info; \ - \ - name##_i++; \ - } \ -} + priv->rdai_nr = nr; + priv->daidrv = rdrv; + priv->rdai = rdai; /* * parse all dai */ dai_i = 0; for_each_child_of_node(dai_node, dai_np) { - dai_info = info->dai_info + dai_i; - - for (i = 0;; i++) { - - playback = of_parse_phandle(dai_np, "playback", i); - capture = of_parse_phandle(dai_np, "capture", i); + rdai = rsnd_rdai_get(priv, dai_i); + drv = rdrv + dai_i; + io_playback = &rdai->playback; + io_capture = &rdai->capture; + + snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i); + + rdai->priv = priv; + drv->name = rdai->name; + drv->ops = &rsnd_soc_dai_ops; + + snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE, + "DAI%d Playback", dai_i); + drv->playback.rates = RSND_RATES; + drv->playback.formats = RSND_FMTS; + drv->playback.channels_min = 2; + drv->playback.channels_max = 6; + drv->playback.stream_name = rdai->playback.name; + + snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE, + "DAI%d Capture", dai_i); + drv->capture.rates = RSND_RATES; + drv->capture.formats = RSND_FMTS; + drv->capture.channels_min = 2; + drv->capture.channels_max = 6; + drv->capture.stream_name = rdai->capture.name; + + rdai->playback.rdai = rdai; + rdai->capture.rdai = rdai; + rsnd_set_slot(rdai, 2, 1); /* default */ + + for (io_i = 0;; io_i++) { + playback = of_parse_phandle(dai_np, "playback", io_i); + capture = of_parse_phandle(dai_np, "capture", io_i); if (!playback && !capture) break; - mod_parse(ssi); - mod_parse(src); - mod_parse(ctu); - mod_parse(mix); - mod_parse(dvc); + rsnd_parse_connect_ssi(rdai, playback, capture); + rsnd_parse_connect_src(rdai, playback, capture); + rsnd_parse_connect_ctu(rdai, playback, capture); + rsnd_parse_connect_mix(rdai, playback, capture); + rsnd_parse_connect_dvc(rdai, playback, capture); of_node_put(playback); of_node_put(capture); } dai_i++; - } -} - -static int rsnd_dai_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) -{ - struct snd_soc_dai_driver *drv; - struct rcar_snd_info *info = rsnd_priv_to_info(priv); - struct rsnd_dai *rdai; - struct rsnd_ssi_platform_info *pmod, *cmod; - struct device *dev = rsnd_priv_to_dev(priv); - int dai_nr; - int i; - - rsnd_of_parse_dai(pdev, of_data, priv); - dai_nr = info->dai_info_nr; - if (!dai_nr) { - dev_err(dev, "no dai\n"); - return -EIO; + dev_dbg(dev, "%s (%s/%s)\n", rdai->name, + rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ", + rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- "); } - drv = devm_kzalloc(dev, sizeof(*drv) * dai_nr, GFP_KERNEL); - rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL); - if (!drv || !rdai) { - dev_err(dev, "dai allocate failed\n"); - return -ENOMEM; - } - - priv->rdai_nr = dai_nr; - priv->daidrv = drv; - priv->rdai = rdai; + ret = 0; - for (i = 0; i < dai_nr; i++) { +rsnd_dai_probe_done: + of_node_put(dai_node); - pmod = info->dai_info[i].playback.ssi; - cmod = info->dai_info[i].capture.ssi; - - /* - * init rsnd_dai - */ - snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i); - rdai[i].priv = priv; - - /* - * init snd_soc_dai_driver - */ - drv[i].name = rdai[i].name; - drv[i].ops = &rsnd_soc_dai_ops; - if (pmod) { - snprintf(rdai[i].playback.name, RSND_DAI_NAME_SIZE, - "DAI%d Playback", i); - - drv[i].playback.rates = RSND_RATES; - drv[i].playback.formats = RSND_FMTS; - drv[i].playback.channels_min = 2; - drv[i].playback.channels_max = 2; - drv[i].playback.stream_name = rdai[i].playback.name; - - rdai[i].playback.info = &info->dai_info[i].playback; - rdai[i].playback.rdai = rdai + i; - rsnd_path_init(priv, &rdai[i], &rdai[i].playback); - } - if (cmod) { - snprintf(rdai[i].capture.name, RSND_DAI_NAME_SIZE, - "DAI%d Capture", i); - - drv[i].capture.rates = RSND_RATES; - drv[i].capture.formats = RSND_FMTS; - drv[i].capture.channels_min = 2; - drv[i].capture.channels_max = 2; - drv[i].capture.stream_name = rdai[i].capture.name; - - rdai[i].capture.info = &info->dai_info[i].capture; - rdai[i].capture.rdai = rdai + i; - rsnd_path_init(priv, &rdai[i], &rdai[i].capture); - } - - dev_dbg(dev, "%s (%s/%s)\n", rdai[i].name, - pmod ? "play" : " -- ", - cmod ? "capture" : " -- "); - } - - return 0; + return ret; } /* @@ -1033,14 +877,13 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod, void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod)) { - struct snd_soc_card *soc_card = rtd->card; struct snd_card *card = rtd->card->snd_card; struct snd_kcontrol *kctrl; struct snd_kcontrol_new knew = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = name, .info = rsnd_kctrl_info, - .index = rtd - soc_card->rtd, + .index = rtd->num, .get = rsnd_kctrl_get, .put = rsnd_kctrl_put, .private_value = (unsigned long)cfg, @@ -1077,10 +920,14 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod, void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod), struct rsnd_kctrl_cfg_m *_cfg, + int ch_size, u32 max) { + if (ch_size > RSND_DVC_CHANNELS) + return -EINVAL; + _cfg->cfg.max = max; - _cfg->cfg.size = RSND_DVC_CHANNELS; + _cfg->cfg.size = ch_size; _cfg->cfg.val = _cfg->val; return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update); } @@ -1161,6 +1008,9 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, ret = rsnd_dai_call(probe, io, priv); if (ret == -EAGAIN) { + struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); + int i; + /* * Fallback to PIO mode */ @@ -1175,10 +1025,12 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, rsnd_dai_call(remove, io, priv); /* - * remove SRC/DVC from DAI, + * remove all mod from io + * and, re connect ssi */ - rsnd_path_remove(priv, io, src); - rsnd_path_remove(priv, io, dvc); + for (i = 0; i < RSND_MOD_MAX; i++) + rsnd_dai_disconnect((io)->mod[i], io, i); + rsnd_dai_connect(ssi_mod, io, RSND_MOD_SSI); /* * fallback @@ -1200,33 +1052,25 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, */ static int rsnd_probe(struct platform_device *pdev) { - struct rcar_snd_info *info; struct rsnd_priv *priv; struct device *dev = &pdev->dev; struct rsnd_dai *rdai; const struct of_device_id *of_id = of_match_device(rsnd_of_match, dev); - const struct rsnd_of_data *of_data; - int (*probe_func[])(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) = { + int (*probe_func[])(struct rsnd_priv *priv) = { rsnd_gen_probe, rsnd_dma_probe, rsnd_ssi_probe, + rsnd_ssiu_probe, rsnd_src_probe, rsnd_ctu_probe, rsnd_mix_probe, rsnd_dvc_probe, + rsnd_cmd_probe, rsnd_adg_probe, rsnd_dai_probe, }; int ret, i; - info = devm_kzalloc(&pdev->dev, sizeof(struct rcar_snd_info), - GFP_KERNEL); - if (!info) - return -ENOMEM; - of_data = of_id->data; - /* * init priv data */ @@ -1237,14 +1081,14 @@ static int rsnd_probe(struct platform_device *pdev) } priv->pdev = pdev; - priv->info = info; + priv->flags = (unsigned long)of_id->data; spin_lock_init(&priv->lock); /* * init each module */ for (i = 0; i < ARRAY_SIZE(probe_func); i++) { - ret = probe_func[i](pdev, of_data, priv); + ret = probe_func[i](priv); if (ret) return ret; } @@ -1297,13 +1141,15 @@ static int rsnd_remove(struct platform_device *pdev) { struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev); struct rsnd_dai *rdai; - void (*remove_func[])(struct platform_device *pdev, - struct rsnd_priv *priv) = { + void (*remove_func[])(struct rsnd_priv *priv) = { rsnd_ssi_remove, + rsnd_ssiu_remove, rsnd_src_remove, rsnd_ctu_remove, rsnd_mix_remove, rsnd_dvc_remove, + rsnd_cmd_remove, + rsnd_adg_remove, }; int ret = 0, i; @@ -1315,7 +1161,7 @@ static int rsnd_remove(struct platform_device *pdev) } for (i = 0; i < ARRAY_SIZE(remove_func); i++) - remove_func[i](pdev, priv); + remove_func[i](priv); snd_soc_unregister_component(&pdev->dev); snd_soc_unregister_platform(&pdev->dev); diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 3cb214ab848b..d53a225d19e9 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -13,7 +13,6 @@ #define CTU_NAME "ctu" struct rsnd_ctu { - struct rsnd_ctu_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; }; @@ -24,6 +23,7 @@ struct rsnd_ctu { ((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \ i++) +#define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id) #define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1) #define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0) static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable) @@ -31,6 +31,13 @@ static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable) rsnd_mod_write(mod, CTU_CTUIR, enable); } +static int rsnd_ctu_probe_(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + return rsnd_cmd_attach(io, rsnd_mod_id(mod) / 4); +} + static int rsnd_ctu_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -57,6 +64,7 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod, static struct rsnd_mod_ops rsnd_ctu_ops = { .name = CTU_NAME, + .probe = rsnd_ctu_probe_, .init = rsnd_ctu_init, .quit = rsnd_ctu_quit, }; @@ -66,51 +74,13 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv))) id = 0; - return rsnd_mod_get((struct rsnd_ctu *)(priv->ctu) + id); + return rsnd_mod_get(rsnd_ctu_get(priv, id)); } -static void rsnd_of_parse_ctu(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) +int rsnd_ctu_probe(struct rsnd_priv *priv) { struct device_node *node; - struct rsnd_ctu_platform_info *ctu_info; - struct rcar_snd_info *info = rsnd_priv_to_info(priv); - struct device *dev = &pdev->dev; - int nr; - - if (!of_data) - return; - - node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu"); - if (!node) - return; - - nr = of_get_child_count(node); - if (!nr) - goto rsnd_of_parse_ctu_end; - - ctu_info = devm_kzalloc(dev, - sizeof(struct rsnd_ctu_platform_info) * nr, - GFP_KERNEL); - if (!ctu_info) { - dev_err(dev, "ctu info allocation error\n"); - goto rsnd_of_parse_ctu_end; - } - - info->ctu_info = ctu_info; - info->ctu_info_nr = nr; - -rsnd_of_parse_ctu_end: - of_node_put(node); - -} - -int rsnd_ctu_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) -{ - struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct device_node *np; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ctu *ctu; struct clk *clk; @@ -121,20 +91,30 @@ int rsnd_ctu_probe(struct platform_device *pdev, if (rsnd_is_gen1(priv)) return 0; - rsnd_of_parse_ctu(pdev, of_data, priv); + node = rsnd_ctu_of_node(priv); + if (!node) + return 0; /* not used is not error */ - nr = info->ctu_info_nr; - if (!nr) - return 0; + nr = of_get_child_count(node); + if (!nr) { + ret = -EINVAL; + goto rsnd_ctu_probe_done; + } ctu = devm_kzalloc(dev, sizeof(*ctu) * nr, GFP_KERNEL); - if (!ctu) - return -ENOMEM; + if (!ctu) { + ret = -ENOMEM; + goto rsnd_ctu_probe_done; + } priv->ctu_nr = nr; priv->ctu = ctu; - for_each_rsnd_ctu(ctu, priv, i) { + i = 0; + ret = 0; + for_each_child_of_node(node, np) { + ctu = rsnd_ctu_get(priv, i); + /* * CTU00, CTU01, CTU02, CTU03 => CTU0 * CTU10, CTU11, CTU12, CTU13 => CTU1 @@ -143,22 +123,27 @@ int rsnd_ctu_probe(struct platform_device *pdev, CTU_NAME, i / 4); clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - ctu->info = &info->ctu_info[i]; + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto rsnd_ctu_probe_done; + } ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops, clk, RSND_MOD_CTU, i); if (ret) - return ret; + goto rsnd_ctu_probe_done; + + i++; } - return 0; + +rsnd_ctu_probe_done: + of_node_put(node); + + return ret; } -void rsnd_ctu_remove(struct platform_device *pdev, - struct rsnd_priv *priv) +void rsnd_ctu_remove(struct rsnd_priv *priv) { struct rsnd_ctu *ctu; int i; diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 5d084d040961..418e6fdd06a3 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -22,21 +22,36 @@ /* PDMACHCR */ #define PDMACHCR_DE (1 << 0) + +struct rsnd_dmaen { + struct dma_chan *chan; +}; + +struct rsnd_dmapp { + int dmapp_id; + u32 chcr; +}; + +struct rsnd_dma { + struct rsnd_mod mod; + dma_addr_t src_addr; + dma_addr_t dst_addr; + union { + struct rsnd_dmaen en; + struct rsnd_dmapp pp; + } dma; +}; + struct rsnd_dma_ctrl { void __iomem *base; + int dmaen_num; int dmapp_num; }; -struct rsnd_dma_ops { - char *name; - void (*start)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); - void (*stop)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); - int (*init)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id, - struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); - void (*quit)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); -}; - #define rsnd_priv_to_dmac(p) ((struct rsnd_dma_ctrl *)(p)->dma) +#define rsnd_mod_to_dma(_mod) container_of((_mod), struct rsnd_dma, mod) +#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en) +#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp) /* * Audio DMAC @@ -77,18 +92,24 @@ static void rsnd_dmaen_complete(void *data) rsnd_mod_interrupt(mod, __rsnd_dmaen_complete); } -static void rsnd_dmaen_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma) +static int rsnd_dmaen_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) { + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); dmaengine_terminate_all(dmaen->chan); + + return 0; } -static void rsnd_dmaen_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma) +static int rsnd_dmaen_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) { + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); - struct rsnd_mod *mod = rsnd_dma_to_mod(dma); - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct snd_pcm_substream *substream = io->substream; struct device *dev = rsnd_priv_to_dev(priv); struct dma_async_tx_descriptor *desc; @@ -103,18 +124,20 @@ static void rsnd_dmaen_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma) if (!desc) { dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); - return; + return -EIO; } desc->callback = rsnd_dmaen_complete; - desc->callback_param = mod; + desc->callback_param = rsnd_mod_get(dma); if (dmaengine_submit(desc) < 0) { dev_err(dev, "dmaengine_submit() fail\n"); - return; + return -EIO; } dma_async_issue_pending(dmaen->chan); + + return 0; } struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, @@ -152,12 +175,29 @@ static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io, return rsnd_mod_dma_req(io, mod_to); } -static int rsnd_dmaen_init(struct rsnd_dai_stream *io, +static int rsnd_dmaen_remove(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); + struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); + + if (dmaen->chan) + dma_release_channel(dmaen->chan); + + dmaen->chan = NULL; + + return 0; +} + +static int rsnd_dmaen_attach(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id, struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) { + struct rsnd_mod *mod = rsnd_mod_get(dma); struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); struct device *dev = rsnd_priv_to_dev(priv); struct dma_slave_config cfg = {}; int is_play = rsnd_io_is_play(io); @@ -191,18 +231,20 @@ static int rsnd_dmaen_init(struct rsnd_dai_stream *io, cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - dev_dbg(dev, "%s %pad -> %pad\n", - dma->ops->name, + dev_dbg(dev, "%s[%d] %pad -> %pad\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), &cfg.src_addr, &cfg.dst_addr); ret = dmaengine_slave_config(dmaen->chan, &cfg); if (ret < 0) - goto rsnd_dma_init_err; + goto rsnd_dma_attach_err; + + dmac->dmaen_num++; return 0; -rsnd_dma_init_err: - rsnd_dma_quit(io, dma); +rsnd_dma_attach_err: + rsnd_dmaen_remove(mod, io, priv); rsnd_dma_channel_err: /* @@ -214,22 +256,11 @@ rsnd_dma_channel_err: return -EAGAIN; } -static void rsnd_dmaen_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma) -{ - struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); - - if (dmaen->chan) - dma_release_channel(dmaen->chan); - - dmaen->chan = NULL; -} - -static struct rsnd_dma_ops rsnd_dmaen_ops = { +static struct rsnd_mod_ops rsnd_dmaen_ops = { .name = "audmac", .start = rsnd_dmaen_start, .stop = rsnd_dmaen_stop, - .init = rsnd_dmaen_init, - .quit = rsnd_dmaen_quit, + .remove = rsnd_dmaen_remove, }; /* @@ -307,7 +338,7 @@ static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io, (0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id)) static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg) { - struct rsnd_mod *mod = rsnd_dma_to_mod(dma); + struct rsnd_mod *mod = rsnd_mod_get(dma); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); struct device *dev = rsnd_priv_to_dev(priv); @@ -319,38 +350,48 @@ static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg) static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg) { - struct rsnd_mod *mod = rsnd_dma_to_mod(dma); + struct rsnd_mod *mod = rsnd_mod_get(dma); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); return ioread32(rsnd_dmapp_addr(dmac, dma, reg)); } -static void rsnd_dmapp_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma) +static int rsnd_dmapp_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) { + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); int i; rsnd_dmapp_write(dma, 0, PDMACHCR); for (i = 0; i < 1024; i++) { if (0 == rsnd_dmapp_read(dma, PDMACHCR)) - return; + return 0; udelay(1); } + + return -EIO; } -static void rsnd_dmapp_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma) +static int rsnd_dmapp_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) { + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); rsnd_dmapp_write(dma, dma->src_addr, PDMASAR); rsnd_dmapp_write(dma, dma->dst_addr, PDMADAR); rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR); + + return 0; } -static int rsnd_dmapp_init(struct rsnd_dai_stream *io, - struct rsnd_dma *dma, int id, - struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) +static int rsnd_dmapp_attach(struct rsnd_dai_stream *io, + struct rsnd_dma *dma, int id, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) { struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); struct rsnd_priv *priv = rsnd_io_to_priv(io); @@ -362,19 +403,16 @@ static int rsnd_dmapp_init(struct rsnd_dai_stream *io, dmac->dmapp_num++; - rsnd_dmapp_stop(io, dma); - dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n", dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr); return 0; } -static struct rsnd_dma_ops rsnd_dmapp_ops = { +static struct rsnd_mod_ops rsnd_dmapp_ops = { .name = "audmac-pp", .start = rsnd_dmapp_start, .stop = rsnd_dmapp_stop, - .init = rsnd_dmapp_init, .quit = rsnd_dmapp_stop, }; @@ -497,13 +535,12 @@ static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io, } #define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */ -static void rsnd_dma_of_path(struct rsnd_dma *dma, +static void rsnd_dma_of_path(struct rsnd_mod *this, struct rsnd_dai_stream *io, int is_play, struct rsnd_mod **mod_from, struct rsnd_mod **mod_to) { - struct rsnd_mod *this = rsnd_dma_to_mod(dma); struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io); @@ -513,7 +550,7 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma, struct rsnd_mod *mod_start, *mod_end; struct rsnd_priv *priv = rsnd_mod_to_priv(this); struct device *dev = rsnd_priv_to_dev(priv); - int nr, i; + int nr, i, idx; if (!ssi) return; @@ -542,23 +579,24 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma, mod_start = (is_play) ? NULL : ssi; mod_end = (is_play) ? ssi : NULL; - mod[0] = mod_start; + idx = 0; + mod[idx++] = mod_start; for (i = 1; i < nr; i++) { if (src) { - mod[i] = src; + mod[idx++] = src; src = NULL; } else if (ctu) { - mod[i] = ctu; + mod[idx++] = ctu; ctu = NULL; } else if (mix) { - mod[i] = mix; + mod[idx++] = mix; mix = NULL; } else if (dvc) { - mod[i] = dvc; + mod[idx++] = dvc; dvc = NULL; } } - mod[i] = mod_end; + mod[idx] = mod_end; /* * | SSI | SRC | @@ -567,8 +605,8 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma, * !is_play | * | o | */ if ((this == ssi) == (is_play)) { - *mod_from = mod[nr - 1]; - *mod_to = mod[nr]; + *mod_from = mod[idx - 1]; + *mod_to = mod[idx]; } else { *mod_from = mod[0]; *mod_to = mod[1]; @@ -576,7 +614,7 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma, dev_dbg(dev, "module connection (this is %s[%d])\n", rsnd_mod_name(this), rsnd_mod_id(this)); - for (i = 0; i <= nr; i++) { + for (i = 0; i <= idx; i++) { dev_dbg(dev, " %s[%d]%s\n", rsnd_mod_name(mod[i]), rsnd_mod_id(mod[i]), (mod[i] == *mod_from) ? " from" : @@ -584,36 +622,22 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma, } } -void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma) -{ - dma->ops->stop(io, dma); -} - -void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma) -{ - dma->ops->start(io, dma); -} - -void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma) -{ - struct rsnd_mod *mod = rsnd_dma_to_mod(dma); - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); - - if (!dmac) - return; - - dma->ops->quit(io, dma); -} - -int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id) +struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, int id) { + struct rsnd_mod *dma_mod; struct rsnd_mod *mod_from = NULL; struct rsnd_mod *mod_to = NULL; struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + struct rsnd_dma *dma; struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod_ops *ops; + enum rsnd_mod_type type; + int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); int is_play = rsnd_io_is_play(io); + int ret, dma_id; /* * DMA failed. try to PIO mode @@ -622,35 +646,64 @@ int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id) * rsnd_rdai_continuance_probe() */ if (!dmac) - return -EAGAIN; + return ERR_PTR(-EAGAIN); - rsnd_dma_of_path(dma, io, is_play, &mod_from, &mod_to); + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return ERR_PTR(-ENOMEM); + + rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to); dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); /* for Gen2 */ - if (mod_from && mod_to) - dma->ops = &rsnd_dmapp_ops; - else - dma->ops = &rsnd_dmaen_ops; + if (mod_from && mod_to) { + ops = &rsnd_dmapp_ops; + attach = rsnd_dmapp_attach; + dma_id = dmac->dmapp_num; + type = RSND_MOD_AUDMAPP; + } else { + ops = &rsnd_dmaen_ops; + attach = rsnd_dmaen_attach; + dma_id = dmac->dmaen_num; + type = RSND_MOD_AUDMA; + } /* for Gen1, overwrite */ - if (rsnd_is_gen1(priv)) - dma->ops = &rsnd_dmaen_ops; + if (rsnd_is_gen1(priv)) { + ops = &rsnd_dmaen_ops; + attach = rsnd_dmaen_attach; + dma_id = dmac->dmaen_num; + type = RSND_MOD_AUDMA; + } + + dma_mod = rsnd_mod_get(dma); + + ret = rsnd_mod_init(priv, dma_mod, + ops, NULL, type, dma_id); + if (ret < 0) + return ERR_PTR(ret); - dev_dbg(dev, "%s %s[%d] -> %s[%d]\n", - dma->ops->name, + dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n", + rsnd_mod_name(dma_mod), rsnd_mod_id(dma_mod), rsnd_mod_name(mod_from), rsnd_mod_id(mod_from), rsnd_mod_name(mod_to), rsnd_mod_id(mod_to)); - return dma->ops->init(io, dma, id, mod_from, mod_to); + ret = attach(io, dma, id, mod_from, mod_to); + if (ret < 0) + return ERR_PTR(ret); + + ret = rsnd_dai_connect(dma_mod, io, type); + if (ret < 0) + return ERR_PTR(ret); + + return rsnd_mod_get(dma); } -int rsnd_dma_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) +int rsnd_dma_probe(struct rsnd_priv *priv) { + struct platform_device *pdev = rsnd_priv_to_pdev(priv); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_dma_ctrl *dmac; struct resource *res; diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 58f690900e6d..d45ffe496397 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -15,7 +15,6 @@ #define DVC_NAME "dvc" struct rsnd_dvc { - struct rsnd_dvc_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; struct rsnd_kctrl_cfg_m volume; struct rsnd_kctrl_cfg_m mute; @@ -24,6 +23,7 @@ struct rsnd_dvc { struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ }; +#define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id) #define rsnd_dvc_nr(priv) ((priv)->dvc_nr) #define rsnd_dvc_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc") @@ -64,79 +64,142 @@ static const char * const dvc_ramp_rate[] = { "0.125 dB/8192 steps", /* 10111 */ }; -static void rsnd_dvc_soft_reset(struct rsnd_mod *mod) +static void rsnd_dvc_activation(struct rsnd_mod *mod) { rsnd_mod_write(mod, DVC_SWRSR, 0); rsnd_mod_write(mod, DVC_SWRSR, 1); } -#define rsnd_dvc_initialize_lock(mod) __rsnd_dvc_initialize_lock(mod, 1) -#define rsnd_dvc_initialize_unlock(mod) __rsnd_dvc_initialize_lock(mod, 0) -static void __rsnd_dvc_initialize_lock(struct rsnd_mod *mod, u32 enable) +static void rsnd_dvc_halt(struct rsnd_mod *mod) { - rsnd_mod_write(mod, DVC_DVUIR, enable); + rsnd_mod_write(mod, DVC_DVUIR, 1); + rsnd_mod_write(mod, DVC_SWRSR, 0); } -static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) +#define rsnd_dvc_get_vrpdr(dvc) (dvc->rup.val << 8 | dvc->rdown.val) +#define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (dvc->volume.val[0] >> 13)) + +static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); u32 val[RSND_DVC_CHANNELS]; - u32 dvucr = 0; - u32 mute = 0; int i; - for (i = 0; i < dvc->mute.cfg.size; i++) - mute |= (!!dvc->mute.cfg.val[i]) << i; + /* Enable Ramp */ + if (dvc->ren.val) + for (i = 0; i < RSND_DVC_CHANNELS; i++) + val[i] = dvc->volume.cfg.max; + else + for (i = 0; i < RSND_DVC_CHANNELS; i++) + val[i] = dvc->volume.val[i]; - /* Disable DVC Register access */ - rsnd_mod_write(mod, DVC_DVUER, 0); + /* Enable Digital Volume */ + rsnd_mod_write(mod, DVC_VOL0R, val[0]); + rsnd_mod_write(mod, DVC_VOL1R, val[1]); + rsnd_mod_write(mod, DVC_VOL2R, val[2]); + rsnd_mod_write(mod, DVC_VOL3R, val[3]); + rsnd_mod_write(mod, DVC_VOL4R, val[4]); + rsnd_mod_write(mod, DVC_VOL5R, val[5]); + rsnd_mod_write(mod, DVC_VOL6R, val[6]); + rsnd_mod_write(mod, DVC_VOL7R, val[7]); +} + +static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); + u32 adinr = 0; + u32 dvucr = 0; + u32 vrctr = 0; + u32 vrpdr = 0; + u32 vrdbr = 0; + + adinr = rsnd_get_adinr_bit(mod, io) | + rsnd_get_adinr_chan(mod, io); + + /* Enable Digital Volume, Zero Cross Mute Mode */ + dvucr |= 0x101; /* Enable Ramp */ if (dvc->ren.val) { dvucr |= 0x10; - /* Digital Volume Max */ - for (i = 0; i < RSND_DVC_CHANNELS; i++) - val[i] = dvc->volume.cfg.max; - - rsnd_mod_write(mod, DVC_VRCTR, 0xff); - rsnd_mod_write(mod, DVC_VRPDR, dvc->rup.val << 8 | - dvc->rdown.val); /* * FIXME !! * use scale-downed Digital Volume * as Volume Ramp * 7F FFFF -> 3FF */ - rsnd_mod_write(mod, DVC_VRDBR, - 0x3ff - (dvc->volume.val[0] >> 13)); - - } else { - for (i = 0; i < RSND_DVC_CHANNELS; i++) - val[i] = dvc->volume.val[i]; + vrctr = 0xff; + vrpdr = rsnd_dvc_get_vrpdr(dvc); + vrdbr = rsnd_dvc_get_vrdbr(dvc); } - /* Enable Digital Volume */ - dvucr |= 0x100; - rsnd_mod_write(mod, DVC_VOL0R, val[0]); - rsnd_mod_write(mod, DVC_VOL1R, val[1]); + /* Initialize operation */ + rsnd_mod_write(mod, DVC_DVUIR, 1); + + /* General Information */ + rsnd_mod_write(mod, DVC_ADINR, adinr); + rsnd_mod_write(mod, DVC_DVUCR, dvucr); + + /* Volume Ramp Parameter */ + rsnd_mod_write(mod, DVC_VRCTR, vrctr); + rsnd_mod_write(mod, DVC_VRPDR, vrpdr); + rsnd_mod_write(mod, DVC_VRDBR, vrdbr); + + /* Digital Volume Function Parameter */ + rsnd_dvc_volume_parameter(io, mod); + + /* cancel operation */ + rsnd_mod_write(mod, DVC_DVUIR, 0); +} + +static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); + u32 zcmcr = 0; + u32 vrpdr = 0; + u32 vrdbr = 0; + int i; + + for (i = 0; i < dvc->mute.cfg.size; i++) + zcmcr |= (!!dvc->mute.cfg.val[i]) << i; - /* Enable Mute */ - if (mute) { - dvucr |= 0x1; - rsnd_mod_write(mod, DVC_ZCMCR, mute); + if (dvc->ren.val) { + vrpdr = rsnd_dvc_get_vrpdr(dvc); + vrdbr = rsnd_dvc_get_vrdbr(dvc); } - rsnd_mod_write(mod, DVC_DVUCR, dvucr); + /* Disable DVC Register access */ + rsnd_mod_write(mod, DVC_DVUER, 0); + + /* Zero Cross Mute Function */ + rsnd_mod_write(mod, DVC_ZCMCR, zcmcr); + + /* Volume Ramp Function */ + rsnd_mod_write(mod, DVC_VRPDR, vrpdr); + rsnd_mod_write(mod, DVC_VRDBR, vrdbr); + /* add DVC_VRWTR here */ + + /* Digital Volume Function Parameter */ + rsnd_dvc_volume_parameter(io, mod); /* Enable DVC Register access */ rsnd_mod_write(mod, DVC_DVUER, 1); } -static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) +static int rsnd_dvc_probe_(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + return rsnd_cmd_attach(io, rsnd_mod_id(mod)); +} + +static int rsnd_dvc_remove_(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); @@ -155,19 +218,12 @@ static int rsnd_dvc_init(struct rsnd_mod *mod, { rsnd_mod_power_on(mod); - rsnd_dvc_soft_reset(mod); - - rsnd_dvc_initialize_lock(mod); - - rsnd_path_parse(priv, io); + rsnd_dvc_activation(mod); - rsnd_mod_write(mod, DVC_ADINR, rsnd_get_adinr_bit(mod, io)); + rsnd_dvc_volume_init(io, mod); - /* ch0/ch1 Volume */ rsnd_dvc_volume_update(io, mod); - rsnd_adg_set_cmd_timsel_gen2(mod, io); - return 0; } @@ -175,27 +231,9 @@ static int rsnd_dvc_quit(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_mod_power_off(mod); + rsnd_dvc_halt(mod); - return 0; -} - -static int rsnd_dvc_start(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - rsnd_dvc_initialize_unlock(mod); - - rsnd_mod_write(mod, CMD_CTRL, 0x10); - - return 0; -} - -static int rsnd_dvc_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - rsnd_mod_write(mod, CMD_CTRL, 0); + rsnd_mod_power_off(mod); return 0; } @@ -206,6 +244,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); int is_play = rsnd_io_is_play(io); + int slots = rsnd_get_slot(io); int ret; /* Volume */ @@ -213,7 +252,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, is_play ? "DVC Out Playback Volume" : "DVC In Capture Volume", rsnd_dvc_volume_update, - &dvc->volume, 0x00800000 - 1); + &dvc->volume, slots, + 0x00800000 - 1); if (ret < 0) return ret; @@ -222,7 +262,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, is_play ? "DVC Out Mute Switch" : "DVC In Mute Switch", rsnd_dvc_volume_update, - &dvc->mute, 1); + &dvc->mute, slots, + 1); if (ret < 0) return ret; @@ -269,11 +310,10 @@ static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io, static struct rsnd_mod_ops rsnd_dvc_ops = { .name = DVC_NAME, .dma_req = rsnd_dvc_dma_req, - .remove = rsnd_dvc_remove_gen2, + .probe = rsnd_dvc_probe_, + .remove = rsnd_dvc_remove_, .init = rsnd_dvc_init, .quit = rsnd_dvc_quit, - .start = rsnd_dvc_start, - .stop = rsnd_dvc_stop, .pcm_new = rsnd_dvc_pcm_new, }; @@ -282,50 +322,13 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv))) id = 0; - return rsnd_mod_get((struct rsnd_dvc *)(priv->dvc) + id); + return rsnd_mod_get(rsnd_dvc_get(priv, id)); } -static void rsnd_of_parse_dvc(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) +int rsnd_dvc_probe(struct rsnd_priv *priv) { struct device_node *node; - struct rsnd_dvc_platform_info *dvc_info; - struct rcar_snd_info *info = rsnd_priv_to_info(priv); - struct device *dev = &pdev->dev; - int nr; - - if (!of_data) - return; - - node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc"); - if (!node) - return; - - nr = of_get_child_count(node); - if (!nr) - goto rsnd_of_parse_dvc_end; - - dvc_info = devm_kzalloc(dev, - sizeof(struct rsnd_dvc_platform_info) * nr, - GFP_KERNEL); - if (!dvc_info) { - dev_err(dev, "dvc info allocation error\n"); - goto rsnd_of_parse_dvc_end; - } - - info->dvc_info = dvc_info; - info->dvc_info_nr = nr; - -rsnd_of_parse_dvc_end: - of_node_put(node); -} - -int rsnd_dvc_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) -{ - struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct device_node *np; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_dvc *dvc; struct clk *clk; @@ -336,40 +339,54 @@ int rsnd_dvc_probe(struct platform_device *pdev, if (rsnd_is_gen1(priv)) return 0; - rsnd_of_parse_dvc(pdev, of_data, priv); + node = rsnd_dvc_of_node(priv); + if (!node) + return 0; /* not used is not error */ - nr = info->dvc_info_nr; - if (!nr) - return 0; + nr = of_get_child_count(node); + if (!nr) { + ret = -EINVAL; + goto rsnd_dvc_probe_done; + } dvc = devm_kzalloc(dev, sizeof(*dvc) * nr, GFP_KERNEL); - if (!dvc) - return -ENOMEM; + if (!dvc) { + ret = -ENOMEM; + goto rsnd_dvc_probe_done; + } priv->dvc_nr = nr; priv->dvc = dvc; - for_each_rsnd_dvc(dvc, priv, i) { + i = 0; + ret = 0; + for_each_child_of_node(node, np) { + dvc = rsnd_dvc_get(priv, i); + snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d", DVC_NAME, i); clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - dvc->info = &info->dvc_info[i]; + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto rsnd_dvc_probe_done; + } ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops, clk, RSND_MOD_DVC, i); if (ret) - return ret; + goto rsnd_dvc_probe_done; + + i++; } - return 0; +rsnd_dvc_probe_done: + of_node_put(node); + + return ret; } -void rsnd_dvc_remove(struct platform_device *pdev, - struct rsnd_priv *priv) +void rsnd_dvc_remove(struct rsnd_priv *priv) { struct rsnd_dvc *dvc; int i; diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 76da7620904c..ea24247eba73 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -31,29 +31,33 @@ struct rsnd_gen { /* RSND_REG_MAX base */ struct regmap_field *regs[RSND_REG_MAX]; + const char *reg_name[RSND_REG_MAX]; }; #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) +#define rsnd_reg_name(gen, id) ((gen)->reg_name[id]) struct rsnd_regmap_field_conf { int idx; unsigned int reg_offset; unsigned int id_offset; + const char *reg_name; }; -#define RSND_REG_SET(id, offset, _id_offset) \ +#define RSND_REG_SET(id, offset, _id_offset, n) \ { \ .idx = id, \ .reg_offset = offset, \ .id_offset = _id_offset, \ + .reg_name = n, \ } /* single address mapping */ #define RSND_GEN_S_REG(id, offset) \ - RSND_REG_SET(RSND_REG_##id, offset, 0) + RSND_REG_SET(RSND_REG_##id, offset, 0, #id) /* multi address mapping */ #define RSND_GEN_M_REG(id, offset, _id_offset) \ - RSND_REG_SET(RSND_REG_##id, offset, _id_offset) + RSND_REG_SET(RSND_REG_##id, offset, _id_offset, #id) /* * basic function @@ -83,8 +87,9 @@ u32 rsnd_read(struct rsnd_priv *priv, regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); - dev_dbg(dev, "r %s[%d] - %4d : %08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val); + dev_dbg(dev, "r %s[%d] - %-18s (%4d) : %08x\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), + rsnd_reg_name(gen, reg), reg, val); return val; } @@ -99,10 +104,11 @@ void rsnd_write(struct rsnd_priv *priv, if (!rsnd_is_accessible_reg(priv, gen, reg)) return; - dev_dbg(dev, "w %s[%d] - %4d : %08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data); - regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data); + + dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), + rsnd_reg_name(gen, reg), reg, data); } void rsnd_force_write(struct rsnd_priv *priv, @@ -115,10 +121,11 @@ void rsnd_force_write(struct rsnd_priv *priv, if (!rsnd_is_accessible_reg(priv, gen, reg)) return; - dev_dbg(dev, "w %s[%d] - %4d : %08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data); - regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data); + + dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), + rsnd_reg_name(gen, reg), reg, data); } void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, @@ -130,11 +137,13 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, if (!rsnd_is_accessible_reg(priv, gen, reg)) return; - dev_dbg(dev, "b %s[%d] - %4d : %08x/%08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data, mask); - regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod), mask, data); + + dev_dbg(dev, "b %s[%d] - %-18s (%4d) : %08x/%08x\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), + rsnd_reg_name(gen, reg), reg, data, mask); + } phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id) @@ -150,7 +159,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, int id_size, int reg_id, const char *name, - struct rsnd_regmap_field_conf *conf, + const struct rsnd_regmap_field_conf *conf, int conf_size) { struct platform_device *pdev = rsnd_priv_to_pdev(priv); @@ -203,6 +212,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, /* RSND_REG_MAX base */ gen->regs[conf[i].idx] = regs; + gen->reg_name[conf[i].idx] = conf[i].reg_name; } return 0; @@ -211,31 +221,37 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, /* * Gen2 */ -static int rsnd_gen2_probe(struct platform_device *pdev, - struct rsnd_priv *priv) +static int rsnd_gen2_probe(struct rsnd_priv *priv) { - struct rsnd_regmap_field_conf conf_ssiu[] = { + const static struct rsnd_regmap_field_conf conf_ssiu[] = { RSND_GEN_S_REG(SSI_MODE0, 0x800), RSND_GEN_S_REG(SSI_MODE1, 0x804), + RSND_GEN_S_REG(SSI_MODE2, 0x808), + RSND_GEN_S_REG(SSI_CONTROL, 0x810), + /* FIXME: it needs SSI_MODE2/3 in the future */ RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80), RSND_GEN_M_REG(SSI_BUSIF_ADINR, 0x4, 0x80), RSND_GEN_M_REG(SSI_BUSIF_DALIGN,0x8, 0x80), + RSND_GEN_M_REG(SSI_MODE, 0xc, 0x80), RSND_GEN_M_REG(SSI_CTRL, 0x10, 0x80), RSND_GEN_M_REG(SSI_INT_ENABLE, 0x18, 0x80), }; - struct rsnd_regmap_field_conf conf_scu[] = { - RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x0, 0x20), + + const static struct rsnd_regmap_field_conf conf_scu[] = { + RSND_GEN_M_REG(SRC_I_BUSIF_MODE,0x0, 0x20), + RSND_GEN_M_REG(SRC_O_BUSIF_MODE,0x4, 0x20), RSND_GEN_M_REG(SRC_BUSIF_DALIGN,0x8, 0x20), RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20), RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20), RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20), + RSND_GEN_M_REG(CMD_BUSIF_DALIGN,0x188, 0x20), RSND_GEN_M_REG(CMD_ROUTE_SLCT, 0x18c, 0x20), RSND_GEN_M_REG(CMD_CTRL, 0x190, 0x20), RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8), RSND_GEN_S_REG(SCU_SYS_INT_EN0, 0x1cc), RSND_GEN_S_REG(SCU_SYS_STATUS1, 0x1d0), - RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1c4), + RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1d4), RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40), RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40), RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40), @@ -266,9 +282,15 @@ static int rsnd_gen2_probe(struct platform_device *pdev, RSND_GEN_M_REG(DVC_VRDBR, 0xe20, 0x100), RSND_GEN_M_REG(DVC_VOL0R, 0xe28, 0x100), RSND_GEN_M_REG(DVC_VOL1R, 0xe2c, 0x100), + RSND_GEN_M_REG(DVC_VOL2R, 0xe30, 0x100), + RSND_GEN_M_REG(DVC_VOL3R, 0xe34, 0x100), + RSND_GEN_M_REG(DVC_VOL4R, 0xe38, 0x100), + RSND_GEN_M_REG(DVC_VOL5R, 0xe3c, 0x100), + RSND_GEN_M_REG(DVC_VOL6R, 0xe40, 0x100), + RSND_GEN_M_REG(DVC_VOL7R, 0xe44, 0x100), RSND_GEN_M_REG(DVC_DVUER, 0xe48, 0x100), }; - struct rsnd_regmap_field_conf conf_adg[] = { + const static struct rsnd_regmap_field_conf conf_adg[] = { RSND_GEN_S_REG(BRRA, 0x00), RSND_GEN_S_REG(BRRB, 0x04), RSND_GEN_S_REG(SSICKR, 0x08), @@ -288,7 +310,7 @@ static int rsnd_gen2_probe(struct platform_device *pdev, RSND_GEN_S_REG(SRCOUT_TIMSEL4, 0x58), RSND_GEN_S_REG(CMDOUT_TIMSEL, 0x5c), }; - struct rsnd_regmap_field_conf conf_ssi[] = { + const static struct rsnd_regmap_field_conf conf_ssi[] = { RSND_GEN_M_REG(SSICR, 0x00, 0x40), RSND_GEN_M_REG(SSISR, 0x04, 0x40), RSND_GEN_M_REG(SSITDR, 0x08, 0x40), @@ -317,65 +339,30 @@ static int rsnd_gen2_probe(struct platform_device *pdev, * Gen1 */ -static int rsnd_gen1_probe(struct platform_device *pdev, - struct rsnd_priv *priv) +static int rsnd_gen1_probe(struct rsnd_priv *priv) { - struct rsnd_regmap_field_conf conf_sru[] = { - RSND_GEN_S_REG(SRC_ROUTE_SEL, 0x00), - RSND_GEN_S_REG(SRC_TMG_SEL0, 0x08), - RSND_GEN_S_REG(SRC_TMG_SEL1, 0x0c), - RSND_GEN_S_REG(SRC_TMG_SEL2, 0x10), - RSND_GEN_S_REG(SRC_ROUTE_CTRL, 0xc0), - RSND_GEN_S_REG(SSI_MODE0, 0xD0), - RSND_GEN_S_REG(SSI_MODE1, 0xD4), - RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x20, 0x4), - RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0x50, 0x8), - RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40), - RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40), - RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40), - RSND_GEN_M_REG(SRC_IFSCR, 0x21c, 0x40), - RSND_GEN_M_REG(SRC_IFSVR, 0x220, 0x40), - RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40), - RSND_GEN_M_REG(SRC_MNFSR, 0x228, 0x40), - /* - * ADD US - * - * SRC_STATUS - * SRC_INT_EN - * SCU_SYS_STATUS0 - * SCU_SYS_STATUS1 - * SCU_SYS_INT_EN0 - * SCU_SYS_INT_EN1 - */ - }; - struct rsnd_regmap_field_conf conf_adg[] = { + const static struct rsnd_regmap_field_conf conf_adg[] = { RSND_GEN_S_REG(BRRA, 0x00), RSND_GEN_S_REG(BRRB, 0x04), RSND_GEN_S_REG(SSICKR, 0x08), RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c), RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10), - RSND_GEN_S_REG(AUDIO_CLK_SEL3, 0x18), - RSND_GEN_S_REG(AUDIO_CLK_SEL4, 0x1c), - RSND_GEN_S_REG(AUDIO_CLK_SEL5, 0x20), }; - struct rsnd_regmap_field_conf conf_ssi[] = { + const static struct rsnd_regmap_field_conf conf_ssi[] = { RSND_GEN_M_REG(SSICR, 0x00, 0x40), RSND_GEN_M_REG(SSISR, 0x04, 0x40), RSND_GEN_M_REG(SSITDR, 0x08, 0x40), RSND_GEN_M_REG(SSIRDR, 0x0c, 0x40), RSND_GEN_M_REG(SSIWSR, 0x20, 0x40), }; - int ret_sru; int ret_adg; int ret_ssi; - ret_sru = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SRU, "sru", conf_sru); ret_adg = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, "adg", conf_adg); ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, "ssi", conf_ssi); - if (ret_sru < 0 || - ret_adg < 0 || + if (ret_adg < 0 || ret_ssi < 0) - return ret_sru | ret_adg | ret_ssi; + return ret_adg | ret_ssi; return 0; } @@ -383,28 +370,12 @@ static int rsnd_gen1_probe(struct platform_device *pdev, /* * Gen */ -static void rsnd_of_parse_gen(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) -{ - struct rcar_snd_info *info = priv->info; - - if (!of_data) - return; - - info->flags = of_data->flags; -} - -int rsnd_gen_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) +int rsnd_gen_probe(struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_gen *gen; int ret; - rsnd_of_parse_gen(pdev, of_data, priv); - gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); if (!gen) { dev_err(dev, "GEN allocate failed\n"); @@ -415,9 +386,9 @@ int rsnd_gen_probe(struct platform_device *pdev, ret = -ENODEV; if (rsnd_is_gen1(priv)) - ret = rsnd_gen1_probe(pdev, priv); + ret = rsnd_gen1_probe(priv); else if (rsnd_is_gen2(priv)) - ret = rsnd_gen2_probe(pdev, priv); + ret = rsnd_gen2_probe(priv); if (ret < 0) dev_err(dev, "unknown generation R-Car sound device\n"); diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 953dd0be9b60..65542b6a89e9 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -13,10 +13,10 @@ #define MIX_NAME "mix" struct rsnd_mix { - struct rsnd_mix_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; }; +#define rsnd_mix_get(priv, id) ((struct rsnd_mix *)(priv->mix) + id) #define rsnd_mix_nr(priv) ((priv)->mix_nr) #define for_each_rsnd_mix(pos, priv, i) \ for ((i) = 0; \ @@ -24,58 +24,77 @@ struct rsnd_mix { ((pos) = (struct rsnd_mix *)(priv)->mix + i); \ i++) - -static void rsnd_mix_soft_reset(struct rsnd_mod *mod) +static void rsnd_mix_activation(struct rsnd_mod *mod) { rsnd_mod_write(mod, MIX_SWRSR, 0); rsnd_mod_write(mod, MIX_SWRSR, 1); } -#define rsnd_mix_initialize_lock(mod) __rsnd_mix_initialize_lock(mod, 1) -#define rsnd_mix_initialize_unlock(mod) __rsnd_mix_initialize_lock(mod, 0) -static void __rsnd_mix_initialize_lock(struct rsnd_mod *mod, u32 enable) +static void rsnd_mix_halt(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, MIX_MIXIR, 1); + rsnd_mod_write(mod, MIX_SWRSR, 0); +} + +static void rsnd_mix_volume_parameter(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - rsnd_mod_write(mod, MIX_MIXIR, enable); + rsnd_mod_write(mod, MIX_MDBAR, 0); + rsnd_mod_write(mod, MIX_MDBBR, 0); + rsnd_mod_write(mod, MIX_MDBCR, 0); + rsnd_mod_write(mod, MIX_MDBDR, 0); +} + +static void rsnd_mix_volume_init(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, MIX_MIXIR, 1); + + /* General Information */ + rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io)); + + /* volume step */ + rsnd_mod_write(mod, MIX_MIXMR, 0); + rsnd_mod_write(mod, MIX_MVPDR, 0); + + /* common volume parameter */ + rsnd_mix_volume_parameter(io, mod); + + rsnd_mod_write(mod, MIX_MIXIR, 0); } static void rsnd_mix_volume_update(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { - /* Disable MIX dB setting */ rsnd_mod_write(mod, MIX_MDBER, 0); - rsnd_mod_write(mod, MIX_MDBAR, 0); - rsnd_mod_write(mod, MIX_MDBBR, 0); - rsnd_mod_write(mod, MIX_MDBCR, 0); - rsnd_mod_write(mod, MIX_MDBDR, 0); + /* common volume parameter */ + rsnd_mix_volume_parameter(io, mod); /* Enable MIX dB setting */ rsnd_mod_write(mod, MIX_MDBER, 1); } +static int rsnd_mix_probe_(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + return rsnd_cmd_attach(io, rsnd_mod_id(mod)); +} + static int rsnd_mix_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { rsnd_mod_power_on(mod); - rsnd_mix_soft_reset(mod); - - rsnd_mix_initialize_lock(mod); - - rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io)); - - rsnd_path_parse(priv, io); + rsnd_mix_activation(mod); - /* volume step */ - rsnd_mod_write(mod, MIX_MIXMR, 0); - rsnd_mod_write(mod, MIX_MVPDR, 0); + rsnd_mix_volume_init(io, mod); rsnd_mix_volume_update(io, mod); - rsnd_mix_initialize_unlock(mod); - return 0; } @@ -83,6 +102,8 @@ static int rsnd_mix_quit(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { + rsnd_mix_halt(mod); + rsnd_mod_power_off(mod); return 0; @@ -90,6 +111,7 @@ static int rsnd_mix_quit(struct rsnd_mod *mod, static struct rsnd_mod_ops rsnd_mix_ops = { .name = MIX_NAME, + .probe = rsnd_mix_probe_, .init = rsnd_mix_init, .quit = rsnd_mix_quit, }; @@ -99,51 +121,13 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv))) id = 0; - return rsnd_mod_get((struct rsnd_mix *)(priv->mix) + id); + return rsnd_mod_get(rsnd_mix_get(priv, id)); } -static void rsnd_of_parse_mix(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) +int rsnd_mix_probe(struct rsnd_priv *priv) { struct device_node *node; - struct rsnd_mix_platform_info *mix_info; - struct rcar_snd_info *info = rsnd_priv_to_info(priv); - struct device *dev = &pdev->dev; - int nr; - - if (!of_data) - return; - - node = of_get_child_by_name(dev->of_node, "rcar_sound,mix"); - if (!node) - return; - - nr = of_get_child_count(node); - if (!nr) - goto rsnd_of_parse_mix_end; - - mix_info = devm_kzalloc(dev, - sizeof(struct rsnd_mix_platform_info) * nr, - GFP_KERNEL); - if (!mix_info) { - dev_err(dev, "mix info allocation error\n"); - goto rsnd_of_parse_mix_end; - } - - info->mix_info = mix_info; - info->mix_info_nr = nr; - -rsnd_of_parse_mix_end: - of_node_put(node); - -} - -int rsnd_mix_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) -{ - struct rcar_snd_info *info = rsnd_priv_to_info(priv); + struct device_node *np; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mix *mix; struct clk *clk; @@ -154,40 +138,54 @@ int rsnd_mix_probe(struct platform_device *pdev, if (rsnd_is_gen1(priv)) return 0; - rsnd_of_parse_mix(pdev, of_data, priv); + node = rsnd_mix_of_node(priv); + if (!node) + return 0; /* not used is not error */ - nr = info->mix_info_nr; - if (!nr) - return 0; + nr = of_get_child_count(node); + if (!nr) { + ret = -EINVAL; + goto rsnd_mix_probe_done; + } mix = devm_kzalloc(dev, sizeof(*mix) * nr, GFP_KERNEL); - if (!mix) - return -ENOMEM; + if (!mix) { + ret = -ENOMEM; + goto rsnd_mix_probe_done; + } priv->mix_nr = nr; priv->mix = mix; - for_each_rsnd_mix(mix, priv, i) { + i = 0; + ret = 0; + for_each_child_of_node(node, np) { + mix = rsnd_mix_get(priv, i); + snprintf(name, MIX_NAME_SIZE, "%s.%d", MIX_NAME, i); clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - mix->info = &info->mix_info[i]; + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto rsnd_mix_probe_done; + } ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, clk, RSND_MOD_MIX, i); if (ret) - return ret; + goto rsnd_mix_probe_done; + + i++; } - return 0; +rsnd_mix_probe_done: + of_node_put(node); + + return ret; } -void rsnd_mix_remove(struct platform_device *pdev, - struct rsnd_priv *priv) +void rsnd_mix_remove(struct rsnd_priv *priv) { struct rsnd_mix *mix; int i; diff --git a/sound/soc/sh/rcar/rcar_snd.h b/sound/soc/sh/rcar/rcar_snd.h deleted file mode 100644 index d8e33d38da43..000000000000 --- a/sound/soc/sh/rcar/rcar_snd.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Renesas R-Car SRU/SCU/SSIU/SSI support - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.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. - */ - -#ifndef RCAR_SND_H -#define RCAR_SND_H - - -#define RSND_GEN1_SRU 0 -#define RSND_GEN1_ADG 1 -#define RSND_GEN1_SSI 2 - -#define RSND_GEN2_SCU 0 -#define RSND_GEN2_ADG 1 -#define RSND_GEN2_SSIU 2 -#define RSND_GEN2_SSI 3 - -#define RSND_BASE_MAX 4 - -/* - * flags - * - * 0xAB000000 - * - * A : clock sharing settings - * B : SSI direction - */ -#define RSND_SSI_CLK_PIN_SHARE (1 << 31) -#define RSND_SSI_NO_BUSIF (1 << 30) /* SSI+DMA without BUSIF */ - -#define RSND_SSI(_dma_id, _irq, _flags) \ -{ .dma_id = _dma_id, .irq = _irq, .flags = _flags } -#define RSND_SSI_UNUSED \ -{ .dma_id = -1, .irq = -1, .flags = 0 } - -struct rsnd_ssi_platform_info { - int dma_id; - int irq; - u32 flags; -}; - -#define RSND_SRC(rate, _dma_id) \ -{ .convert_rate = rate, .dma_id = _dma_id, } -#define RSND_SRC_UNUSED \ -{ .convert_rate = 0, .dma_id = -1, } - -struct rsnd_src_platform_info { - u32 convert_rate; /* sampling rate convert */ - int dma_id; /* for Gen2 SCU */ - int irq; -}; - -/* - * flags - */ -struct rsnd_ctu_platform_info { - u32 flags; -}; - -struct rsnd_mix_platform_info { - u32 flags; -}; - -struct rsnd_dvc_platform_info { - u32 flags; -}; - -struct rsnd_dai_path_info { - struct rsnd_ssi_platform_info *ssi; - struct rsnd_src_platform_info *src; - struct rsnd_ctu_platform_info *ctu; - struct rsnd_mix_platform_info *mix; - struct rsnd_dvc_platform_info *dvc; -}; - -struct rsnd_dai_platform_info { - struct rsnd_dai_path_info playback; - struct rsnd_dai_path_info capture; -}; - -/* - * flags - * - * 0x0000000A - * - * A : generation - */ -#define RSND_GEN_MASK (0xF << 0) -#define RSND_GEN1 (1 << 0) /* fixme */ -#define RSND_GEN2 (2 << 0) /* fixme */ - -struct rcar_snd_info { - u32 flags; - struct rsnd_ssi_platform_info *ssi_info; - int ssi_info_nr; - struct rsnd_src_platform_info *src_info; - int src_info_nr; - struct rsnd_ctu_platform_info *ctu_info; - int ctu_info_nr; - struct rsnd_mix_platform_info *mix_info; - int mix_info_nr; - struct rsnd_dvc_platform_info *dvc_info; - int dvc_info_nr; - struct rsnd_dai_platform_info *dai_info; - int dai_info_nr; - int (*start)(int id); - int (*stop)(int id); -}; - -#endif diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 085329878525..317dd793149a 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -24,7 +24,16 @@ #include <sound/soc.h> #include <sound/pcm_params.h> -#include "rcar_snd.h" +#define RSND_GEN1_SRU 0 +#define RSND_GEN1_ADG 1 +#define RSND_GEN1_SSI 2 + +#define RSND_GEN2_SCU 0 +#define RSND_GEN2_ADG 1 +#define RSND_GEN2_SSIU 2 +#define RSND_GEN2_SSI 3 + +#define RSND_BASE_MAX 4 /* * pseudo register @@ -34,10 +43,19 @@ * see gen1/gen2 for detail */ enum rsnd_reg { - /* SRU/SCU/SSIU */ + /* SCU (SRC/SSIU/MIX/CTU/DVC) */ + RSND_REG_SSI_MODE, /* Gen2 only */ RSND_REG_SSI_MODE0, RSND_REG_SSI_MODE1, - RSND_REG_SRC_BUSIF_MODE, + RSND_REG_SSI_MODE2, + RSND_REG_SSI_CONTROL, + RSND_REG_SSI_CTRL, /* Gen2 only */ + RSND_REG_SSI_BUSIF_MODE, /* Gen2 only */ + RSND_REG_SSI_BUSIF_ADINR, /* Gen2 only */ + RSND_REG_SSI_BUSIF_DALIGN, /* Gen2 only */ + RSND_REG_SSI_INT_ENABLE, /* Gen2 only */ + RSND_REG_SRC_I_BUSIF_MODE, + RSND_REG_SRC_O_BUSIF_MODE, RSND_REG_SRC_ROUTE_MODE0, RSND_REG_SRC_SWRSR, RSND_REG_SRC_SRCIR, @@ -45,9 +63,29 @@ enum rsnd_reg { RSND_REG_SRC_IFSCR, RSND_REG_SRC_IFSVR, RSND_REG_SRC_SRCCR, + RSND_REG_SRC_CTRL, /* Gen2 only */ + RSND_REG_SRC_BSDSR, /* Gen2 only */ + RSND_REG_SRC_BSISR, /* Gen2 only */ + RSND_REG_SRC_INT_ENABLE0, /* Gen2 only */ + RSND_REG_SRC_BUSIF_DALIGN, /* Gen2 only */ + RSND_REG_SRCIN_TIMSEL0, /* Gen2 only */ + RSND_REG_SRCIN_TIMSEL1, /* Gen2 only */ + RSND_REG_SRCIN_TIMSEL2, /* Gen2 only */ + RSND_REG_SRCIN_TIMSEL3, /* Gen2 only */ + RSND_REG_SRCIN_TIMSEL4, /* Gen2 only */ + RSND_REG_SRCOUT_TIMSEL0, /* Gen2 only */ + RSND_REG_SRCOUT_TIMSEL1, /* Gen2 only */ + RSND_REG_SRCOUT_TIMSEL2, /* Gen2 only */ + RSND_REG_SRCOUT_TIMSEL3, /* Gen2 only */ + RSND_REG_SRCOUT_TIMSEL4, /* Gen2 only */ RSND_REG_SCU_SYS_STATUS0, + RSND_REG_SCU_SYS_STATUS1, /* Gen2 only */ RSND_REG_SCU_SYS_INT_EN0, + RSND_REG_SCU_SYS_INT_EN1, /* Gen2 only */ + RSND_REG_CMD_CTRL, /* Gen2 only */ + RSND_REG_CMD_BUSIF_DALIGN, /* Gen2 only */ RSND_REG_CMD_ROUTE_SLCT, + RSND_REG_CMDOUT_TIMSEL, /* Gen2 only */ RSND_REG_CTU_CTUIR, RSND_REG_CTU_ADINR, RSND_REG_MIX_SWRSR, @@ -67,14 +105,25 @@ enum rsnd_reg { RSND_REG_DVC_ZCMCR, RSND_REG_DVC_VOL0R, RSND_REG_DVC_VOL1R, + RSND_REG_DVC_VOL2R, + RSND_REG_DVC_VOL3R, + RSND_REG_DVC_VOL4R, + RSND_REG_DVC_VOL5R, + RSND_REG_DVC_VOL6R, + RSND_REG_DVC_VOL7R, RSND_REG_DVC_DVUER, + RSND_REG_DVC_VRCTR, /* Gen2 only */ + RSND_REG_DVC_VRPDR, /* Gen2 only */ + RSND_REG_DVC_VRDBR, /* Gen2 only */ /* ADG */ RSND_REG_BRRA, RSND_REG_BRRB, RSND_REG_SSICKR, + RSND_REG_DIV_EN, /* Gen2 only */ RSND_REG_AUDIO_CLK_SEL0, RSND_REG_AUDIO_CLK_SEL1, + RSND_REG_AUDIO_CLK_SEL2, /* Gen2 only */ /* SSI */ RSND_REG_SSICR, @@ -83,83 +132,9 @@ enum rsnd_reg { RSND_REG_SSIRDR, RSND_REG_SSIWSR, - /* SHARE see below */ - RSND_REG_SHARE01, - RSND_REG_SHARE02, - RSND_REG_SHARE03, - RSND_REG_SHARE04, - RSND_REG_SHARE05, - RSND_REG_SHARE06, - RSND_REG_SHARE07, - RSND_REG_SHARE08, - RSND_REG_SHARE09, - RSND_REG_SHARE10, - RSND_REG_SHARE11, - RSND_REG_SHARE12, - RSND_REG_SHARE13, - RSND_REG_SHARE14, - RSND_REG_SHARE15, - RSND_REG_SHARE16, - RSND_REG_SHARE17, - RSND_REG_SHARE18, - RSND_REG_SHARE19, - RSND_REG_SHARE20, - RSND_REG_SHARE21, - RSND_REG_SHARE22, - RSND_REG_SHARE23, - RSND_REG_SHARE24, - RSND_REG_SHARE25, - RSND_REG_SHARE26, - RSND_REG_SHARE27, - RSND_REG_SHARE28, - RSND_REG_SHARE29, - RSND_REG_MAX, }; -/* Gen1 only */ -#define RSND_REG_SRC_ROUTE_SEL RSND_REG_SHARE01 -#define RSND_REG_SRC_TMG_SEL0 RSND_REG_SHARE02 -#define RSND_REG_SRC_TMG_SEL1 RSND_REG_SHARE03 -#define RSND_REG_SRC_TMG_SEL2 RSND_REG_SHARE04 -#define RSND_REG_SRC_ROUTE_CTRL RSND_REG_SHARE05 -#define RSND_REG_SRC_MNFSR RSND_REG_SHARE06 -#define RSND_REG_AUDIO_CLK_SEL3 RSND_REG_SHARE07 -#define RSND_REG_AUDIO_CLK_SEL4 RSND_REG_SHARE08 -#define RSND_REG_AUDIO_CLK_SEL5 RSND_REG_SHARE09 - -/* Gen2 only */ -#define RSND_REG_SRC_CTRL RSND_REG_SHARE01 -#define RSND_REG_SSI_CTRL RSND_REG_SHARE02 -#define RSND_REG_SSI_BUSIF_MODE RSND_REG_SHARE03 -#define RSND_REG_SSI_BUSIF_ADINR RSND_REG_SHARE04 -#define RSND_REG_SSI_INT_ENABLE RSND_REG_SHARE05 -#define RSND_REG_SRC_BSDSR RSND_REG_SHARE06 -#define RSND_REG_SRC_BSISR RSND_REG_SHARE07 -#define RSND_REG_DIV_EN RSND_REG_SHARE08 -#define RSND_REG_SRCIN_TIMSEL0 RSND_REG_SHARE09 -#define RSND_REG_SRCIN_TIMSEL1 RSND_REG_SHARE10 -#define RSND_REG_SRCIN_TIMSEL2 RSND_REG_SHARE11 -#define RSND_REG_SRCIN_TIMSEL3 RSND_REG_SHARE12 -#define RSND_REG_SRCIN_TIMSEL4 RSND_REG_SHARE13 -#define RSND_REG_SRCOUT_TIMSEL0 RSND_REG_SHARE14 -#define RSND_REG_SRCOUT_TIMSEL1 RSND_REG_SHARE15 -#define RSND_REG_SRCOUT_TIMSEL2 RSND_REG_SHARE16 -#define RSND_REG_SRCOUT_TIMSEL3 RSND_REG_SHARE17 -#define RSND_REG_SRCOUT_TIMSEL4 RSND_REG_SHARE18 -#define RSND_REG_AUDIO_CLK_SEL2 RSND_REG_SHARE19 -#define RSND_REG_CMD_CTRL RSND_REG_SHARE20 -#define RSND_REG_CMDOUT_TIMSEL RSND_REG_SHARE21 -#define RSND_REG_SSI_BUSIF_DALIGN RSND_REG_SHARE22 -#define RSND_REG_DVC_VRCTR RSND_REG_SHARE23 -#define RSND_REG_DVC_VRPDR RSND_REG_SHARE24 -#define RSND_REG_DVC_VRDBR RSND_REG_SHARE25 -#define RSND_REG_SCU_SYS_STATUS1 RSND_REG_SHARE26 -#define RSND_REG_SCU_SYS_INT_EN1 RSND_REG_SHARE27 -#define RSND_REG_SRC_INT_ENABLE0 RSND_REG_SHARE28 -#define RSND_REG_SRC_BUSIF_DALIGN RSND_REG_SHARE29 - -struct rsnd_of_data; struct rsnd_priv; struct rsnd_mod; struct rsnd_dai; @@ -187,43 +162,13 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io); u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io); u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io); -void rsnd_path_parse(struct rsnd_priv *priv, - struct rsnd_dai_stream *io); /* * R-Car DMA */ -struct rsnd_dma; - -struct rsnd_dmaen { - struct dma_chan *chan; -}; - -struct rsnd_dmapp { - int dmapp_id; - u32 chcr; -}; - -struct rsnd_dma { - struct rsnd_dma_ops *ops; - dma_addr_t src_addr; - dma_addr_t dst_addr; - union { - struct rsnd_dmaen en; - struct rsnd_dmapp pp; - } dma; -}; -#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en) -#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp) -#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) - -void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma); -void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma); -int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id); -void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma); -int rsnd_dma_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv); +struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, int id); +int rsnd_dma_probe(struct rsnd_priv *priv); struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, struct rsnd_mod *mod, char *name); @@ -231,11 +176,19 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, * R-Car sound mod */ enum rsnd_mod_type { - RSND_MOD_DVC = 0, + RSND_MOD_AUDMAPP, + RSND_MOD_AUDMA, + RSND_MOD_DVC, RSND_MOD_MIX, RSND_MOD_CTU, + RSND_MOD_CMD, RSND_MOD_SRC, + RSND_MOD_SSIM3, /* SSI multi 3 */ + RSND_MOD_SSIM2, /* SSI multi 2 */ + RSND_MOD_SSIM1, /* SSI multi 1 */ + RSND_MOD_SSIP, /* SSI parent */ RSND_MOD_SSI, + RSND_MOD_SSIU, RSND_MOD_MAX, }; @@ -278,10 +231,8 @@ struct rsnd_mod { int id; enum rsnd_mod_type type; struct rsnd_mod_ops *ops; - struct rsnd_dma dma; struct rsnd_priv *priv; struct clk *clk; - u32 status; }; /* * status @@ -328,7 +279,6 @@ struct rsnd_mod { #define __rsnd_mod_call_hw_params 0 #define rsnd_mod_to_priv(mod) ((mod)->priv) -#define rsnd_mod_to_dma(mod) (&(mod)->dma) #define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1) #define rsnd_mod_power_on(mod) clk_enable((mod)->clk) #define rsnd_mod_power_off(mod) clk_disable((mod)->clk) @@ -347,6 +297,17 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, void rsnd_mod_interrupt(struct rsnd_mod *mod, void (*callback)(struct rsnd_mod *mod, struct rsnd_dai_stream *io)); +void rsnd_parse_connect_common(struct rsnd_dai *rdai, + struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id), + struct device_node *node, + struct device_node *playback, + struct device_node *capture); + +void rsnd_set_slot(struct rsnd_dai *rdai, + int slots, int slots_total); +int rsnd_get_slot(struct rsnd_dai_stream *io); +int rsnd_get_slot_width(struct rsnd_dai_stream *io); +int rsnd_get_slot_num(struct rsnd_dai_stream *io); /* * R-Car sound DAI @@ -358,6 +319,7 @@ struct rsnd_dai_stream { struct rsnd_mod *mod[RSND_MOD_MAX]; struct rsnd_dai_path_info *info; /* rcar_snd.h */ struct rsnd_dai *rdai; + u32 mod_status[RSND_MOD_MAX]; int byte_pos; int period_pos; int byte_per_period; @@ -365,10 +327,12 @@ struct rsnd_dai_stream { }; #define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL) #define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI) +#define rsnd_io_to_mod_ssip(io) rsnd_io_to_mod((io), RSND_MOD_SSIP) #define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC) #define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU) #define rsnd_io_to_mod_mix(io) rsnd_io_to_mod((io), RSND_MOD_MIX) #define rsnd_io_to_mod_dvc(io) rsnd_io_to_mod((io), RSND_MOD_DVC) +#define rsnd_io_to_mod_cmd(io) rsnd_io_to_mod((io), RSND_MOD_CMD) #define rsnd_io_to_rdai(io) ((io)->rdai) #define rsnd_io_to_priv(io) (rsnd_rdai_to_priv(rsnd_io_to_rdai(io))) #define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io) @@ -382,6 +346,9 @@ struct rsnd_dai { struct rsnd_dai_stream capture; struct rsnd_priv *priv; + int slots; + int slots_num; + unsigned int clk_master:1; unsigned int bit_clk_inv:1; unsigned int frm_clk_inv:1; @@ -403,33 +370,28 @@ struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id); bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io); int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); +int rsnd_dai_connect(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + enum rsnd_mod_type type); +#define rsnd_dai_of_node(priv) \ + of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dai") /* * R-Car Gen1/Gen2 */ -int rsnd_gen_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv); +int rsnd_gen_probe(struct rsnd_priv *priv); void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id); -#define rsnd_is_gen1(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1) -#define rsnd_is_gen2(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2) - /* * R-Car ADG */ int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod); int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate); -int rsnd_adg_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv); -int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, - struct rsnd_mod *mod, - unsigned int src_rate, - unsigned int dst_rate); +int rsnd_adg_probe(struct rsnd_priv *priv); +void rsnd_adg_remove(struct rsnd_priv *priv); int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io, unsigned int src_rate, @@ -442,15 +404,14 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, /* * R-Car sound priv */ -struct rsnd_of_data { - u32 flags; -}; - struct rsnd_priv { struct platform_device *pdev; - struct rcar_snd_info *info; spinlock_t lock; + unsigned long flags; +#define RSND_GEN_MASK (0xF << 0) +#define RSND_GEN1 (1 << 0) +#define RSND_GEN2 (2 << 0) /* * below value will be filled on rsnd_gen_probe() @@ -474,6 +435,12 @@ struct rsnd_priv { int ssi_nr; /* + * below value will be filled on rsnd_ssiu_probe() + */ + void *ssiu; + int ssiu_nr; + + /* * below value will be filled on rsnd_src_probe() */ void *src; @@ -498,6 +465,12 @@ struct rsnd_priv { int dvc_nr; /* + * below value will be filled on rsnd_cmd_probe() + */ + void *cmd; + int cmd_nr; + + /* * below value will be filled on rsnd_dai_probe() */ struct snd_soc_dai_driver *daidrv; @@ -507,7 +480,9 @@ struct rsnd_priv { #define rsnd_priv_to_pdev(priv) ((priv)->pdev) #define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev)) -#define rsnd_priv_to_info(priv) ((priv)->info) + +#define rsnd_is_gen1(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN1) +#define rsnd_is_gen2(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN2) /* * rsnd_kctrl @@ -523,7 +498,7 @@ struct rsnd_kctrl_cfg { struct snd_kcontrol *kctrl; }; -#define RSND_DVC_CHANNELS 2 +#define RSND_DVC_CHANNELS 8 struct rsnd_kctrl_cfg_m { struct rsnd_kctrl_cfg cfg; u32 val[RSND_DVC_CHANNELS]; @@ -544,6 +519,7 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod, void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod), struct rsnd_kctrl_cfg_m *_cfg, + int ch_size, u32 max); int rsnd_kctrl_new_s(struct rsnd_mod *mod, struct rsnd_dai_stream *io, @@ -566,70 +542,93 @@ int rsnd_kctrl_new_e(struct rsnd_mod *mod, /* * R-Car SSI */ -int rsnd_ssi_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv); -void rsnd_ssi_remove(struct platform_device *pdev, - struct rsnd_priv *priv); +int rsnd_ssi_probe(struct rsnd_priv *priv); +void rsnd_ssi_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); +u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io); #define rsnd_ssi_is_pin_sharing(io) \ __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); +#define rsnd_ssi_of_node(priv) \ + of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi") +void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, + struct device_node *playback, + struct device_node *capture); + +/* + * R-Car SSIU + */ +int rsnd_ssiu_attach(struct rsnd_dai_stream *io, + struct rsnd_mod *mod); +int rsnd_ssiu_probe(struct rsnd_priv *priv); +void rsnd_ssiu_remove(struct rsnd_priv *priv); + /* * R-Car SRC */ -int rsnd_src_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv); -void rsnd_src_remove(struct platform_device *pdev, - struct rsnd_priv *priv); +int rsnd_src_probe(struct rsnd_priv *priv); +void rsnd_src_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id); unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, struct rsnd_dai_stream *io, struct snd_pcm_runtime *runtime); -int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, - struct rsnd_dai_stream *io, - int use_busif); -int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, - struct rsnd_dai_stream *io); -int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod); -int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod); +#define rsnd_src_of_node(priv) \ + of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src") +#define rsnd_parse_connect_src(rdai, playback, capture) \ + rsnd_parse_connect_common(rdai, rsnd_src_mod_get, \ + rsnd_src_of_node(rsnd_rdai_to_priv(rdai)), \ + playback, capture) /* * R-Car CTU */ -int rsnd_ctu_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv); - -void rsnd_ctu_remove(struct platform_device *pdev, - struct rsnd_priv *priv); +int rsnd_ctu_probe(struct rsnd_priv *priv); +void rsnd_ctu_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); +#define rsnd_ctu_of_node(priv) \ + of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ctu") +#define rsnd_parse_connect_ctu(rdai, playback, capture) \ + rsnd_parse_connect_common(rdai, rsnd_ctu_mod_get, \ + rsnd_ctu_of_node(rsnd_rdai_to_priv(rdai)), \ + playback, capture) /* * R-Car MIX */ -int rsnd_mix_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv); - -void rsnd_mix_remove(struct platform_device *pdev, - struct rsnd_priv *priv); +int rsnd_mix_probe(struct rsnd_priv *priv); +void rsnd_mix_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id); +#define rsnd_mix_of_node(priv) \ + of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,mix") +#define rsnd_parse_connect_mix(rdai, playback, capture) \ + rsnd_parse_connect_common(rdai, rsnd_mix_mod_get, \ + rsnd_mix_of_node(rsnd_rdai_to_priv(rdai)), \ + playback, capture) /* * R-Car DVC */ -int rsnd_dvc_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv); -void rsnd_dvc_remove(struct platform_device *pdev, - struct rsnd_priv *priv); +int rsnd_dvc_probe(struct rsnd_priv *priv); +void rsnd_dvc_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id); +#define rsnd_dvc_of_node(priv) \ + of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc") +#define rsnd_parse_connect_dvc(rdai, playback, capture) \ + rsnd_parse_connect_common(rdai, rsnd_dvc_mod_get, \ + rsnd_dvc_of_node(rsnd_rdai_to_priv(rdai)), \ + playback, capture) + +/* + * R-Car CMD + */ +int rsnd_cmd_probe(struct rsnd_priv *priv); +void rsnd_cmd_remove(struct rsnd_priv *priv); +int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id); +struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id); #ifdef DEBUG void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type); diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c index d61db9c385ea..8a357fdf1077 100644 --- a/sound/soc/sh/rcar/rsrc-card.c +++ b/sound/soc/sh/rcar/rsrc-card.c @@ -48,8 +48,11 @@ MODULE_DEVICE_TABLE(of, rsrc_card_of_match); #define DAI_NAME_NUM 32 struct rsrc_card_dai { - unsigned int fmt; unsigned int sysclk; + unsigned int tx_slot_mask; + unsigned int rx_slot_mask; + int slots; + int slot_width; struct clk *clk; char dai_name[DAI_NAME_NUM]; }; @@ -75,7 +78,7 @@ static int rsrc_card_startup(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct rsrc_card_dai *dai_props = - rsrc_priv_to_props(priv, rtd - rtd->card->rtd); + rsrc_priv_to_props(priv, rtd->num); return clk_prepare_enable(dai_props->clk); } @@ -85,7 +88,7 @@ static void rsrc_card_shutdown(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct rsrc_card_dai *dai_props = - rsrc_priv_to_props(priv, rtd - rtd->card->rtd); + rsrc_priv_to_props(priv, rtd->num); clk_disable_unprepare(dai_props->clk); } @@ -101,7 +104,7 @@ static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dai *dai; struct snd_soc_dai_link *dai_link; struct rsrc_card_dai *dai_props; - int num = rtd - rtd->card->rtd; + int num = rtd->num; int ret; dai_link = rsrc_priv_to_link(priv, num); @@ -110,18 +113,22 @@ static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd) rtd->cpu_dai : rtd->codec_dai; - if (dai_props->fmt) { - ret = snd_soc_dai_set_fmt(dai, dai_props->fmt); + if (dai_props->sysclk) { + ret = snd_soc_dai_set_sysclk(dai, 0, dai_props->sysclk, 0); if (ret && ret != -ENOTSUPP) { - dev_err(dai->dev, "set_fmt error\n"); + dev_err(dai->dev, "set_sysclk error\n"); goto err; } } - if (dai_props->sysclk) { - ret = snd_soc_dai_set_sysclk(dai, 0, dai_props->sysclk, 0); + if (dai_props->slots) { + ret = snd_soc_dai_set_tdm_slot(dai, + dai_props->tx_slot_mask, + dai_props->rx_slot_mask, + dai_props->slots, + dai_props->slot_width); if (ret && ret != -ENOTSUPP) { - dev_err(dai->dev, "set_sysclk error\n"); + dev_err(dai->dev, "set_tdm_slot error\n"); goto err; } } @@ -148,14 +155,13 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, } static int rsrc_card_parse_daifmt(struct device_node *node, - struct device_node *np, + struct device_node *codec, struct rsrc_card_priv *priv, - int idx, bool is_fe) + struct snd_soc_dai_link *dai_link, + unsigned int *retfmt) { - struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx); struct device_node *bitclkmaster = NULL; struct device_node *framemaster = NULL; - struct device_node *codec = is_fe ? NULL : np; unsigned int daifmt; daifmt = snd_soc_of_parse_daifmt(node, NULL, @@ -172,11 +178,11 @@ static int rsrc_card_parse_daifmt(struct device_node *node, daifmt |= (codec == framemaster) ? SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; - dai_props->fmt = daifmt; - of_node_put(bitclkmaster); of_node_put(framemaster); + *retfmt = daifmt; + return 0; } @@ -198,6 +204,15 @@ static int rsrc_card_parse_links(struct device_node *np, if (ret) return ret; + /* Parse TDM slot */ + ret = snd_soc_of_parse_tdm_slot(np, + &dai_props->tx_slot_mask, + &dai_props->rx_slot_mask, + &dai_props->slots, + &dai_props->slot_width); + if (ret) + return ret; + if (is_fe) { /* BE is dummy */ dai_link->codec_of_node = NULL; @@ -208,7 +223,9 @@ static int rsrc_card_parse_links(struct device_node *np, dai_link->dynamic = 1; dai_link->dpcm_merged_format = 1; dai_link->cpu_of_node = args.np; - snd_soc_of_get_dai_name(np, &dai_link->cpu_dai_name); + ret = snd_soc_of_get_dai_name(np, &dai_link->cpu_dai_name); + if (ret < 0) + return ret; /* set dai_name */ snprintf(dai_props->dai_name, DAI_NAME_NUM, "fe.%s", @@ -240,7 +257,9 @@ static int rsrc_card_parse_links(struct device_node *np, dai_link->no_pcm = 1; dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup; dai_link->codec_of_node = args.np; - snd_soc_of_get_dai_name(np, &dai_link->codec_dai_name); + ret = snd_soc_of_get_dai_name(np, &dai_link->codec_dai_name); + if (ret < 0) + return ret; /* additional name prefix */ if (of_data) { @@ -305,23 +324,16 @@ static int rsrc_card_parse_clk(struct device_node *np, return 0; } -static int rsrc_card_dai_link_of(struct device_node *node, - struct device_node *np, - struct rsrc_card_priv *priv, - int idx) +static int rsrc_card_dai_sub_link_of(struct device_node *node, + struct device_node *np, + struct rsrc_card_priv *priv, + int idx, bool is_fe) { struct device *dev = rsrc_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx); struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx); - bool is_fe = false; int ret; - if (0 == strcmp(np->name, "cpu")) - is_fe = true; - - ret = rsrc_card_parse_daifmt(node, np, priv, idx, is_fe); - if (ret < 0) - return ret; - ret = rsrc_card_parse_links(np, priv, idx, is_fe); if (ret < 0) return ret; @@ -332,12 +344,54 @@ static int rsrc_card_dai_link_of(struct device_node *node, dev_dbg(dev, "\t%s / %04x / %d\n", dai_props->dai_name, - dai_props->fmt, + dai_link->dai_fmt, dai_props->sysclk); return ret; } +static int rsrc_card_dai_link_of(struct device_node *node, + struct rsrc_card_priv *priv) +{ + struct snd_soc_dai_link *dai_link; + struct device_node *np; + unsigned int daifmt = 0; + int ret, i; + bool is_fe; + + /* find 1st codec */ + i = 0; + for_each_child_of_node(node, np) { + dai_link = rsrc_priv_to_link(priv, i); + + if (strcmp(np->name, "codec") == 0) { + ret = rsrc_card_parse_daifmt(node, np, priv, + dai_link, &daifmt); + if (ret < 0) + return ret; + break; + } + i++; + } + + i = 0; + for_each_child_of_node(node, np) { + dai_link = rsrc_priv_to_link(priv, i); + dai_link->dai_fmt = daifmt; + + is_fe = false; + if (strcmp(np->name, "cpu") == 0) + is_fe = true; + + ret = rsrc_card_dai_sub_link_of(node, np, priv, i, is_fe); + if (ret < 0) + return ret; + i++; + } + + return 0; +} + static int rsrc_card_parse_of(struct device_node *node, struct rsrc_card_priv *priv, struct device *dev) @@ -345,9 +399,8 @@ static int rsrc_card_parse_of(struct device_node *node, const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); struct rsrc_card_dai *props; struct snd_soc_dai_link *links; - struct device_node *np; int ret; - int i, num; + int num; if (!node) return -EINVAL; @@ -388,13 +441,9 @@ static int rsrc_card_parse_of(struct device_node *node, priv->snd_card.name ? priv->snd_card.name : "", priv->convert_rate); - i = 0; - for_each_child_of_node(node, np) { - ret = rsrc_card_dai_link_of(node, np, priv, i); - if (ret < 0) - return ret; - i++; - } + ret = rsrc_card_dai_link_of(node, priv); + if (ret < 0) + return ret; if (!priv->snd_card.name) priv->snd_card.name = priv->snd_card.dai_link->name; diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 261b50217c48..5eda056d9f20 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -20,20 +20,21 @@ #define OUF_SRC(id) ((1 << (id + 16)) | (1 << id)) struct rsnd_src { - struct rsnd_src_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; + struct rsnd_mod *dma; struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ struct rsnd_kctrl_cfg_s sync; /* sync convert */ u32 convert_rate; /* sampling rate convert */ int err; + int irq; }; #define RSND_SRC_NAME_SIZE 16 +#define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id) +#define rsnd_src_to_dma(src) ((src)->dma) #define rsnd_src_nr(priv) ((priv)->src_nr) #define rsnd_enable_sync_convert(src) ((src)->sen.val) -#define rsnd_src_of_node(priv) \ - of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src") #define rsnd_mod_to_src(_mod) \ container_of((_mod), struct rsnd_src, mod) @@ -69,67 +70,16 @@ struct rsnd_src { * |-----------------| */ -/* - * How to use SRC bypass mode for debugging - * - * SRC has bypass mode, and it is useful for debugging. - * In Gen2 case, - * SRCm_MODE controls whether SRC is used or not - * SSI_MODE0 controls whether SSIU which receives SRC data - * is used or not. - * Both SRCm_MODE/SSI_MODE0 settings are needed if you use SRC, - * but SRC bypass mode needs SSI_MODE0 only. - * - * This driver request - * struct rsnd_src_platform_info { - * u32 convert_rate; - * int dma_id; - * } - * - * rsnd_src_convert_rate() indicates - * above convert_rate, and it controls - * whether SRC is used or not. - * - * ex) doesn't use SRC - * static struct rsnd_dai_platform_info rsnd_dai = { - * .playback = { .ssi = &rsnd_ssi[0], }, - * }; - * - * ex) uses SRC - * static struct rsnd_src_platform_info rsnd_src[] = { - * RSND_SCU(48000, 0), - * ... - * }; - * static struct rsnd_dai_platform_info rsnd_dai = { - * .playback = { .ssi = &rsnd_ssi[0], .src = &rsnd_src[0] }, - * }; - * - * ex) uses SRC bypass mode - * static struct rsnd_src_platform_info rsnd_src[] = { - * RSND_SCU(0, 0), - * ... - * }; - * static struct rsnd_dai_platform_info rsnd_dai = { - * .playback = { .ssi = &rsnd_ssi[0], .src = &rsnd_src[0] }, - * }; - * - */ - -/* - * Gen1/Gen2 common functions - */ -static void rsnd_src_soft_reset(struct rsnd_mod *mod) +static void rsnd_src_activation(struct rsnd_mod *mod) { rsnd_mod_write(mod, SRC_SWRSR, 0); rsnd_mod_write(mod, SRC_SWRSR, 1); } - -#define rsnd_src_initialize_lock(mod) __rsnd_src_initialize_lock(mod, 1) -#define rsnd_src_initialize_unlock(mod) __rsnd_src_initialize_lock(mod, 0) -static void __rsnd_src_initialize_lock(struct rsnd_mod *mod, u32 enable) +static void rsnd_src_halt(struct rsnd_mod *mod) { - rsnd_mod_write(mod, SRC_SRCIR, enable); + rsnd_mod_write(mod, SRC_SRCIR, 1); + rsnd_mod_write(mod, SRC_SWRSR, 0); } static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, @@ -143,99 +93,6 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, is_play ? "rx" : "tx"); } -int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, - struct rsnd_dai_stream *io, - int use_busif) -{ - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - int ssi_id = rsnd_mod_id(ssi_mod); - - /* - * SSI_MODE0 - */ - rsnd_mod_bset(ssi_mod, SSI_MODE0, (1 << ssi_id), - !use_busif << ssi_id); - - /* - * SSI_MODE1 - */ - if (rsnd_ssi_is_pin_sharing(io)) { - int shift = -1; - switch (ssi_id) { - case 1: - shift = 0; - break; - case 2: - shift = 2; - break; - case 4: - shift = 16; - break; - } - - if (shift >= 0) - rsnd_mod_bset(ssi_mod, SSI_MODE1, - 0x3 << shift, - rsnd_rdai_is_clk_master(rdai) ? - 0x2 << shift : 0x1 << shift); - } - - /* - * DMA settings for SSIU - */ - if (use_busif) { - u32 val = rsnd_get_dalign(ssi_mod, io); - - rsnd_mod_write(ssi_mod, SSI_BUSIF_ADINR, - rsnd_get_adinr_bit(ssi_mod, io)); - rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1); - rsnd_mod_write(ssi_mod, SSI_CTRL, 0x1); - - rsnd_mod_write(ssi_mod, SSI_BUSIF_DALIGN, val); - } - - return 0; -} - -int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, - struct rsnd_dai_stream *io) -{ - /* - * DMA settings for SSIU - */ - rsnd_mod_write(ssi_mod, SSI_CTRL, 0); - - return 0; -} - -int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); - - if (rsnd_is_gen1(priv)) - return 0; - - /* enable SSI interrupt if Gen2 */ - rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, - rsnd_ssi_is_dma_mode(ssi_mod) ? - 0x0e000000 : 0x0f000000); - - return 0; -} - -int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); - - if (rsnd_is_gen1(priv)) - return 0; - - /* disable SSI interrupt if Gen2 */ - rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000); - - return 0; -} - static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, struct rsnd_src *src) { @@ -283,34 +140,6 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, return rate; } -static int rsnd_src_set_convert_rate(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) -{ - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 convert_rate = rsnd_src_convert_rate(io, src); - u32 fsrate = 0; - - if (convert_rate) - fsrate = 0x0400000 / convert_rate * runtime->rate; - - /* Set channel number and output bit length */ - rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr_bit(mod, io)); - - /* Enable the initial value of IFS */ - if (fsrate) { - rsnd_mod_write(mod, SRC_IFSCR, 1); - - /* Set initial value of IFS */ - rsnd_mod_write(mod, SRC_IFSVR, fsrate); - } - - /* use DMA transfer */ - rsnd_mod_write(mod, SRC_BUSIF_MODE, 1); - - return 0; -} - static int rsnd_src_hw_params(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_pcm_substream *substream, @@ -319,9 +148,6 @@ static int rsnd_src_hw_params(struct rsnd_mod *mod, struct rsnd_src *src = rsnd_mod_to_src(mod); struct snd_soc_pcm_runtime *fe = substream->private_data; - /* default value (mainly for non-DT) */ - src->convert_rate = src->info->convert_rate; - /* * SRC assumes that it is used under DPCM if user want to use * sampling rate convert. Then, SRC should be FE. @@ -347,250 +173,112 @@ static int rsnd_src_hw_params(struct rsnd_mod *mod, return 0; } -static int rsnd_src_init(struct rsnd_mod *mod, - struct rsnd_priv *priv) -{ - struct rsnd_src *src = rsnd_mod_to_src(mod); - - rsnd_mod_power_on(mod); - - rsnd_src_soft_reset(mod); - - rsnd_src_initialize_lock(mod); - - src->err = 0; - - /* reset sync convert_rate */ - src->sync.val = 0; - - return 0; -} - -static int rsnd_src_quit(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) +static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - struct rsnd_src *src = rsnd_mod_to_src(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 convert_rate = rsnd_src_convert_rate(io, src); + u32 ifscr, fsrate, adinr; + u32 cr, route; + u32 bsdsr, bsisr; + uint ratio; - rsnd_mod_power_off(mod); - - if (src->err) - dev_warn(dev, "%s[%d] under/over flow err = %d\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), src->err); - - src->convert_rate = 0; - - /* reset sync convert_rate */ - src->sync.val = 0; - - return 0; -} - -static int rsnd_src_start(struct rsnd_mod *mod) -{ - rsnd_src_initialize_unlock(mod); - - return 0; -} - -static int rsnd_src_stop(struct rsnd_mod *mod) -{ - /* nothing to do */ - return 0; -} + if (!runtime) + return; -/* - * Gen1 functions - */ -static int rsnd_src_set_route_gen1(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct src_route_config { - u32 mask; - int shift; - } routes[] = { - { 0xF, 0, }, /* 0 */ - { 0xF, 4, }, /* 1 */ - { 0xF, 8, }, /* 2 */ - { 0x7, 12, }, /* 3 */ - { 0x7, 16, }, /* 4 */ - { 0x7, 20, }, /* 5 */ - { 0x7, 24, }, /* 6 */ - { 0x3, 28, }, /* 7 */ - { 0x3, 30, }, /* 8 */ - }; - u32 mask; - u32 val; - int id; + /* 6 - 1/6 are very enough ratio for SRC_BSDSR */ + if (!convert_rate) + ratio = 0; + else if (convert_rate > runtime->rate) + ratio = 100 * convert_rate / runtime->rate; + else + ratio = 100 * runtime->rate / convert_rate; - id = rsnd_mod_id(mod); - if (id < 0 || id >= ARRAY_SIZE(routes)) - return -EIO; + if (ratio > 600) { + dev_err(dev, "FSO/FSI ratio error\n"); + return; + } /* - * SRC_ROUTE_SELECT + * SRC_ADINR */ - val = rsnd_io_is_play(io) ? 0x1 : 0x2; - val = val << routes[id].shift; - mask = routes[id].mask << routes[id].shift; - - rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val); - - return 0; -} - -static int rsnd_src_set_convert_timing_gen1(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_src *src = rsnd_mod_to_src(mod); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - u32 convert_rate = rsnd_src_convert_rate(io, src); - u32 mask; - u32 val; - int shift; - int id = rsnd_mod_id(mod); - int ret; + adinr = rsnd_get_adinr_bit(mod, io) | + rsnd_get_adinr_chan(mod, io); /* - * SRC_TIMING_SELECT + * SRC_IFSCR / SRC_IFSVR */ - shift = (id % 4) * 8; - mask = 0x1F << shift; + ifscr = 0; + fsrate = 0; + if (convert_rate) { + ifscr = 1; + fsrate = 0x0400000 / convert_rate * runtime->rate; + } /* - * ADG is used as source clock if SRC was used, - * then, SSI WS is used as destination clock. - * SSI WS is used as source clock if SRC is not used - * (when playback, source/destination become reverse when capture) + * SRC_SRCCR / SRC_ROUTE_MODE0 */ - ret = 0; + cr = 0x00011110; + route = 0x0; if (convert_rate) { - /* use ADG */ - val = 0; - ret = rsnd_adg_set_convert_clk_gen1(priv, mod, - runtime->rate, - convert_rate); - } else if (8 == id) { - /* use SSI WS, but SRU8 is special */ - val = id << shift; - } else { - /* use SSI WS */ - val = (id + 1) << shift; - } + route = 0x1; - if (ret < 0) - return ret; + if (rsnd_enable_sync_convert(src)) { + cr |= 0x1; + route |= rsnd_io_is_play(io) ? + (0x1 << 24) : (0x1 << 25); + } + } - switch (id / 4) { - case 0: - rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); - break; - case 1: - rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); + /* + * SRC_BSDSR / SRC_BSISR + */ + switch (rsnd_mod_id(mod)) { + case 5: + case 6: + case 7: + case 8: + bsdsr = 0x02400000; /* 6 - 1/6 */ + bsisr = 0x00100060; /* 6 - 1/6 */ break; - case 2: - rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); + default: + bsdsr = 0x01800000; /* 6 - 1/6 */ + bsisr = 0x00100060 ;/* 6 - 1/6 */ break; } - return 0; -} - -static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) -{ - struct rsnd_src *src = rsnd_mod_to_src(mod); - int ret; - - ret = rsnd_src_set_convert_rate(mod, io); - if (ret < 0) - return ret; - - /* Select SRC mode (fixed value) */ - rsnd_mod_write(mod, SRC_SRCCR, 0x00010110); - - /* Set the restriction value of the FS ratio (98%) */ - rsnd_mod_write(mod, SRC_MNFSR, - rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98); - - /* Gen1/Gen2 are not compatible */ - if (rsnd_src_convert_rate(io, src)) - rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); - - /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */ - - return 0; -} - -static int rsnd_src_init_gen1(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - int ret; - - ret = rsnd_src_init(mod, priv); - if (ret < 0) - return ret; - - ret = rsnd_src_set_route_gen1(io, mod); - if (ret < 0) - return ret; - - ret = rsnd_src_set_convert_rate_gen1(mod, io); - if (ret < 0) - return ret; - - ret = rsnd_src_set_convert_timing_gen1(io, mod); - if (ret < 0) - return ret; - - return 0; -} - -static int rsnd_src_start_gen1(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - int id = rsnd_mod_id(mod); - - rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id)); - - return rsnd_src_start(mod); -} - -static int rsnd_src_stop_gen1(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - int id = rsnd_mod_id(mod); + rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */ + rsnd_mod_write(mod, SRC_ADINR, adinr); + rsnd_mod_write(mod, SRC_IFSCR, ifscr); + rsnd_mod_write(mod, SRC_IFSVR, fsrate); + rsnd_mod_write(mod, SRC_SRCCR, cr); + rsnd_mod_write(mod, SRC_BSDSR, bsdsr); + rsnd_mod_write(mod, SRC_BSISR, bsisr); + rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */ - rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0); + rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); + rsnd_mod_write(mod, SRC_I_BUSIF_MODE, 1); + rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1); + rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); - return rsnd_src_stop(mod); + if (convert_rate) + rsnd_adg_set_convert_clk_gen2(mod, io, + runtime->rate, + convert_rate); + else + rsnd_adg_set_convert_timing_gen2(mod, io); } -static struct rsnd_mod_ops rsnd_src_gen1_ops = { - .name = SRC_NAME, - .dma_req = rsnd_src_dma_req, - .init = rsnd_src_init_gen1, - .quit = rsnd_src_quit, - .start = rsnd_src_start_gen1, - .stop = rsnd_src_stop_gen1, - .hw_params = rsnd_src_hw_params, -}; - -/* - * Gen2 functions - */ -#define rsnd_src_irq_enable_gen2(mod) rsnd_src_irq_ctrol_gen2(mod, 1) -#define rsnd_src_irq_disable_gen2(mod) rsnd_src_irq_ctrol_gen2(mod, 0) -static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable) +#define rsnd_src_irq_enable(mod) rsnd_src_irq_ctrol(mod, 1) +#define rsnd_src_irq_disable(mod) rsnd_src_irq_ctrol(mod, 0) +static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable) { struct rsnd_src *src = rsnd_mod_to_src(mod); u32 sys_int_val, int_val, sys_int_mask; - int irq = src->info->irq; + int irq = src->irq; int id = rsnd_mod_id(mod); sys_int_val = @@ -600,7 +288,7 @@ static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable) /* * IRQ is not supported on non-DT * see - * rsnd_src_probe_gen2() + * rsnd_src_probe_() */ if ((irq <= 0) || !enable) { sys_int_val = 0; @@ -620,7 +308,7 @@ static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable) rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); } -static void rsnd_src_error_clear_gen2(struct rsnd_mod *mod) +static void rsnd_src_status_clear(struct rsnd_mod *mod) { u32 val = OUF_SRC(rsnd_mod_id(mod)); @@ -628,7 +316,7 @@ static void rsnd_src_error_clear_gen2(struct rsnd_mod *mod) rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val); } -static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod) +static bool rsnd_src_record_error(struct rsnd_mod *mod) { struct rsnd_src *src = rsnd_mod_to_src(mod); u32 val0, val1; @@ -652,22 +340,16 @@ static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod) ret = true; } - /* clear error static */ - rsnd_src_error_clear_gen2(mod); - return ret; } -static int _rsnd_src_start_gen2(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) +static int rsnd_src_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); u32 val; - val = rsnd_get_dalign(mod, io); - - rsnd_mod_write(mod, SRC_BUSIF_DALIGN, val); - /* * WORKAROUND * @@ -678,251 +360,154 @@ static int _rsnd_src_start_gen2(struct rsnd_mod *mod, rsnd_mod_write(mod, SRC_CTRL, val); - rsnd_src_error_clear_gen2(mod); - - rsnd_src_start(mod); + return 0; +} - rsnd_src_irq_enable_gen2(mod); +static int rsnd_src_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + /* + * stop SRC output only + * see rsnd_src_quit + */ + rsnd_mod_write(mod, SRC_CTRL, 0x01); return 0; } -static int _rsnd_src_stop_gen2(struct rsnd_mod *mod) +static int rsnd_src_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) { - rsnd_src_irq_disable_gen2(mod); + struct rsnd_src *src = rsnd_mod_to_src(mod); - rsnd_mod_write(mod, SRC_CTRL, 0); + rsnd_mod_power_on(mod); - rsnd_src_error_record_gen2(mod); + rsnd_src_activation(mod); + + rsnd_src_set_convert_rate(io, mod); + + rsnd_src_status_clear(mod); + + rsnd_src_irq_enable(mod); + + src->err = 0; + + /* reset sync convert_rate */ + src->sync.val = 0; - return rsnd_src_stop(mod); + return 0; } -static void __rsnd_src_interrupt_gen2(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) +static int rsnd_src_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_src *src = rsnd_mod_to_src(mod); + struct device *dev = rsnd_priv_to_dev(priv); - spin_lock(&priv->lock); + rsnd_src_irq_disable(mod); - /* ignore all cases if not working */ - if (!rsnd_io_is_working(io)) - goto rsnd_src_interrupt_gen2_out; + /* stop both out/in */ + rsnd_mod_write(mod, SRC_CTRL, 0); - if (rsnd_src_error_record_gen2(mod)) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_src *src = rsnd_mod_to_src(mod); - struct device *dev = rsnd_priv_to_dev(priv); + rsnd_src_halt(mod); - dev_dbg(dev, "%s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - _rsnd_src_stop_gen2(mod); - if (src->err < 1024) - _rsnd_src_start_gen2(mod, io); - else - dev_warn(dev, "no more SRC restart\n"); - } + rsnd_mod_power_off(mod); -rsnd_src_interrupt_gen2_out: - spin_unlock(&priv->lock); -} + if (src->err) + dev_warn(dev, "%s[%d] under/over flow err = %d\n", + rsnd_mod_name(mod), rsnd_mod_id(mod), src->err); -static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) -{ - struct rsnd_mod *mod = data; + src->convert_rate = 0; - rsnd_mod_interrupt(mod, __rsnd_src_interrupt_gen2); + /* reset sync convert_rate */ + src->sync.val = 0; - return IRQ_HANDLED; + return 0; } -static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) +static void __rsnd_src_interrupt(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 convert_rate = rsnd_src_convert_rate(io, src); - u32 cr, route; - uint ratio; - int ret; + struct device *dev = rsnd_priv_to_dev(priv); - /* 6 - 1/6 are very enough ratio for SRC_BSDSR */ - if (!convert_rate) - ratio = 0; - else if (convert_rate > runtime->rate) - ratio = 100 * convert_rate / runtime->rate; - else - ratio = 100 * runtime->rate / convert_rate; + spin_lock(&priv->lock); - if (ratio > 600) { - dev_err(dev, "FSO/FSI ratio error\n"); - return -EINVAL; - } + /* ignore all cases if not working */ + if (!rsnd_io_is_working(io)) + goto rsnd_src_interrupt_out; - ret = rsnd_src_set_convert_rate(mod, io); - if (ret < 0) - return ret; + if (rsnd_src_record_error(mod)) { - cr = 0x00011110; - route = 0x0; - if (convert_rate) { - route = 0x1; + dev_dbg(dev, "%s[%d] restart\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); - if (rsnd_enable_sync_convert(src)) { - cr |= 0x1; - route |= rsnd_io_is_play(io) ? - (0x1 << 24) : (0x1 << 25); - } + rsnd_src_stop(mod, io, priv); + rsnd_src_start(mod, io, priv); } - rsnd_mod_write(mod, SRC_SRCCR, cr); - rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); + if (src->err > 1024) { + rsnd_src_irq_disable(mod); - switch (rsnd_mod_id(mod)) { - case 5: - case 6: - case 7: - case 8: - rsnd_mod_write(mod, SRC_BSDSR, 0x02400000); - break; - default: - rsnd_mod_write(mod, SRC_BSDSR, 0x01800000); - break; + dev_warn(dev, "no more %s[%d] restart\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); } - rsnd_mod_write(mod, SRC_BSISR, 0x00100060); + rsnd_src_status_clear(mod); +rsnd_src_interrupt_out: - return 0; + spin_unlock(&priv->lock); } -static int rsnd_src_set_convert_timing_gen2(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) +static irqreturn_t rsnd_src_interrupt(int irq, void *data) { - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 convert_rate = rsnd_src_convert_rate(io, src); - int ret; + struct rsnd_mod *mod = data; - if (convert_rate) - ret = rsnd_adg_set_convert_clk_gen2(mod, io, - runtime->rate, - convert_rate); - else - ret = rsnd_adg_set_convert_timing_gen2(mod, io); + rsnd_mod_interrupt(mod, __rsnd_src_interrupt); - return ret; + return IRQ_HANDLED; } -static int rsnd_src_probe_gen2(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) +static int rsnd_src_probe_(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); struct device *dev = rsnd_priv_to_dev(priv); - int irq = src->info->irq; + int irq = src->irq; int ret; if (irq > 0) { /* * IRQ is not supported on non-DT * see - * rsnd_src_irq_enable_gen2() + * rsnd_src_irq_enable() */ ret = devm_request_irq(dev, irq, - rsnd_src_interrupt_gen2, + rsnd_src_interrupt, IRQF_SHARED, dev_name(dev), mod); if (ret) return ret; } - ret = rsnd_dma_init(io, - rsnd_mod_to_dma(mod), - src->info->dma_id); + src->dma = rsnd_dma_attach(io, mod, 0); + if (IS_ERR(src->dma)) + return PTR_ERR(src->dma); return ret; } -static int rsnd_src_remove_gen2(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - rsnd_dma_quit(io, rsnd_mod_to_dma(mod)); - - return 0; -} - -static int rsnd_src_init_gen2(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - int ret; - - ret = rsnd_src_init(mod, priv); - if (ret < 0) - return ret; - - ret = rsnd_src_set_convert_rate_gen2(mod, io); - if (ret < 0) - return ret; - - ret = rsnd_src_set_convert_timing_gen2(io, mod); - if (ret < 0) - return ret; - - return 0; -} - -static int rsnd_src_start_gen2(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - rsnd_dma_start(io, rsnd_mod_to_dma(mod)); - - return _rsnd_src_start_gen2(mod, io); -} - -static int rsnd_src_stop_gen2(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - int ret; - - ret = _rsnd_src_stop_gen2(mod); - - rsnd_dma_stop(io, rsnd_mod_to_dma(mod)); - - return ret; -} - -static void rsnd_src_reconvert_update(struct rsnd_dai_stream *io, - struct rsnd_mod *mod) -{ - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 convert_rate = rsnd_src_convert_rate(io, src); - u32 fsrate; - - if (!runtime) - return; - - if (!convert_rate) - convert_rate = runtime->rate; - - fsrate = 0x0400000 / convert_rate * runtime->rate; - - /* update IFS */ - rsnd_mod_write(mod, SRC_IFSVR, fsrate); -} - -static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod, +static int rsnd_src_pcm_new(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; @@ -937,13 +522,19 @@ static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod, return 0; /* + * SRC In doesn't work if DVC was enabled + */ + if (dvc && !rsnd_io_is_play(io)) + return 0; + + /* * enable sync convert */ ret = rsnd_kctrl_new_s(mod, io, rtd, rsnd_io_is_play(io) ? "SRC Out Rate Switch" : "SRC In Rate Switch", - rsnd_src_reconvert_update, + rsnd_src_set_convert_rate, &src->sen, 1); if (ret < 0) return ret; @@ -952,23 +543,22 @@ static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod, rsnd_io_is_play(io) ? "SRC Out Rate" : "SRC In Rate", - rsnd_src_reconvert_update, + rsnd_src_set_convert_rate, &src->sync, 192000); return ret; } -static struct rsnd_mod_ops rsnd_src_gen2_ops = { +static struct rsnd_mod_ops rsnd_src_ops = { .name = SRC_NAME, .dma_req = rsnd_src_dma_req, - .probe = rsnd_src_probe_gen2, - .remove = rsnd_src_remove_gen2, - .init = rsnd_src_init_gen2, + .probe = rsnd_src_probe_, + .init = rsnd_src_init, .quit = rsnd_src_quit, - .start = rsnd_src_start_gen2, - .stop = rsnd_src_stop_gen2, + .start = rsnd_src_start, + .stop = rsnd_src_stop, .hw_params = rsnd_src_hw_params, - .pcm_new = rsnd_src_pcm_new_gen2, + .pcm_new = rsnd_src_pcm_new, }; struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) @@ -976,113 +566,78 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv))) id = 0; - return rsnd_mod_get((struct rsnd_src *)(priv->src) + id); + return rsnd_mod_get(rsnd_src_get(priv, id)); } -static void rsnd_of_parse_src(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) +int rsnd_src_probe(struct rsnd_priv *priv) { - struct device_node *src_node; + struct device_node *node; struct device_node *np; - struct rcar_snd_info *info = rsnd_priv_to_info(priv); - struct rsnd_src_platform_info *src_info; - struct device *dev = &pdev->dev; - int nr, i; - - if (!of_data) - return; - - src_node = rsnd_src_of_node(priv); - if (!src_node) - return; - - nr = of_get_child_count(src_node); - if (!nr) - goto rsnd_of_parse_src_end; - - src_info = devm_kzalloc(dev, - sizeof(struct rsnd_src_platform_info) * nr, - GFP_KERNEL); - if (!src_info) { - dev_err(dev, "src info allocation error\n"); - goto rsnd_of_parse_src_end; - } - - info->src_info = src_info; - info->src_info_nr = nr; - - i = 0; - for_each_child_of_node(src_node, np) { - src_info[i].irq = irq_of_parse_and_map(np, 0); - - i++; - } - -rsnd_of_parse_src_end: - of_node_put(src_node); -} - -int rsnd_src_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) -{ - struct rcar_snd_info *info = rsnd_priv_to_info(priv); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_src *src; - struct rsnd_mod_ops *ops; struct clk *clk; char name[RSND_SRC_NAME_SIZE]; int i, nr, ret; - ops = NULL; - if (rsnd_is_gen1(priv)) { - ops = &rsnd_src_gen1_ops; - dev_warn(dev, "Gen1 support will be removed soon\n"); - } - if (rsnd_is_gen2(priv)) - ops = &rsnd_src_gen2_ops; - if (!ops) { - dev_err(dev, "unknown Generation\n"); - return -EIO; - } + /* This driver doesn't support Gen1 at this point */ + if (rsnd_is_gen1(priv)) + return 0; - rsnd_of_parse_src(pdev, of_data, priv); + node = rsnd_src_of_node(priv); + if (!node) + return 0; /* not used is not error */ - /* - * init SRC - */ - nr = info->src_info_nr; - if (!nr) - return 0; + nr = of_get_child_count(node); + if (!nr) { + ret = -EINVAL; + goto rsnd_src_probe_done; + } src = devm_kzalloc(dev, sizeof(*src) * nr, GFP_KERNEL); - if (!src) - return -ENOMEM; + if (!src) { + ret = -ENOMEM; + goto rsnd_src_probe_done; + } priv->src_nr = nr; priv->src = src; - for_each_rsnd_src(src, priv, i) { + i = 0; + for_each_child_of_node(node, np) { + src = rsnd_src_get(priv, i); + snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d", SRC_NAME, i); - clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) - return PTR_ERR(clk); + src->irq = irq_of_parse_and_map(np, 0); + if (!src->irq) { + ret = -EINVAL; + goto rsnd_src_probe_done; + } - src->info = &info->src_info[i]; + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto rsnd_src_probe_done; + } - ret = rsnd_mod_init(priv, rsnd_mod_get(src), ops, clk, RSND_MOD_SRC, i); + ret = rsnd_mod_init(priv, rsnd_mod_get(src), + &rsnd_src_ops, clk, RSND_MOD_SRC, i); if (ret) - return ret; + goto rsnd_src_probe_done; + + i++; } - return 0; + ret = 0; + +rsnd_src_probe_done: + of_node_put(node); + + return ret; } -void rsnd_src_remove(struct platform_device *pdev, - struct rsnd_priv *priv) +void rsnd_src_remove(struct rsnd_priv *priv) { struct rsnd_src *src; int i; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 1427ec21bd7e..7db05fdfb656 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -24,7 +24,9 @@ #define OIEN (1 << 26) /* Overflow Interrupt Enable */ #define IIEN (1 << 25) /* Idle Mode Interrupt Enable */ #define DIEN (1 << 24) /* Data Interrupt Enable */ - +#define CHNL_4 (1 << 22) /* Channels */ +#define CHNL_6 (2 << 22) /* Channels */ +#define CHNL_8 (3 << 22) /* Channels */ #define DWL_8 (0 << 19) /* Data Word Length */ #define DWL_16 (1 << 19) /* Data Word Length */ #define DWL_18 (2 << 19) /* Data Word Length */ @@ -39,6 +41,7 @@ #define SCKP (1 << 13) /* Serial Bit Clock Polarity */ #define SWSP (1 << 12) /* Serial WS Polarity */ #define SDTA (1 << 10) /* Serial Data Alignment */ +#define PDTA (1 << 9) /* Parallel Data Alignment */ #define DEL (1 << 8) /* Serial Data Delay */ #define CKDV(v) (v << 4) /* Serial Clock Division Ratio */ #define TRMD (1 << 1) /* Transmit/Receive Mode Select */ @@ -56,35 +59,44 @@ * SSIWSR */ #define CONT (1 << 8) /* WS Continue Function */ +#define WS_MODE (1 << 0) /* WS Mode */ #define SSI_NAME "ssi" struct rsnd_ssi { - struct rsnd_ssi_platform_info *info; /* rcar_snd.h */ struct rsnd_ssi *parent; struct rsnd_mod mod; + struct rsnd_mod *dma; + u32 flags; u32 cr_own; u32 cr_clk; + u32 cr_mode; + u32 wsr; int chan; + int rate; int err; + int irq; unsigned int usrcnt; }; +/* flags */ +#define RSND_SSI_CLK_PIN_SHARE (1 << 0) +#define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */ + #define for_each_rsnd_ssi(pos, priv, i) \ for (i = 0; \ (i < rsnd_ssi_nr(priv)) && \ ((pos) = ((struct rsnd_ssi *)(priv)->ssi + i)); \ i++) +#define rsnd_ssi_get(priv, id) ((struct rsnd_ssi *)(priv->ssi) + id) +#define rsnd_ssi_to_dma(mod) ((ssi)->dma) #define rsnd_ssi_nr(priv) ((priv)->ssi_nr) #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) -#define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0) -#define rsnd_ssi_parent(ssi) ((ssi)->parent) -#define rsnd_ssi_mode_flags(p) ((p)->info->flags) -#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) -#define rsnd_ssi_of_node(priv) \ - of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi") +#define rsnd_ssi_mode_flags(p) ((p)->flags) +#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io)) +#define rsnd_ssi_is_multi_slave(ssi, io) ((mod) != rsnd_io_to_mod_ssi(io)) int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) { @@ -103,6 +115,16 @@ int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) return use_busif; } +static void rsnd_ssi_status_clear(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, SSISR, 0); +} + +static u32 rsnd_ssi_status_get(struct rsnd_mod *mod) +{ + return rsnd_mod_read(mod, SSISR); +} + static void rsnd_ssi_status_check(struct rsnd_mod *mod, u32 bit) { @@ -112,7 +134,7 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, int i; for (i = 0; i < 1024; i++) { - status = rsnd_mod_read(mod, SSISR); + status = rsnd_ssi_status_get(mod); if (status & bit) return; @@ -122,13 +144,79 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, dev_warn(dev, "status check failed\n"); } +static int rsnd_ssi_irq_enable(struct rsnd_mod *ssi_mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + + if (rsnd_is_gen1(priv)) + return 0; + + /* enable SSI interrupt if Gen2 */ + rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, + rsnd_ssi_is_dma_mode(ssi_mod) ? + 0x0e000000 : 0x0f000000); + + return 0; +} + +static int rsnd_ssi_irq_disable(struct rsnd_mod *ssi_mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + + if (rsnd_is_gen1(priv)) + return 0; + + /* disable SSI interrupt if Gen2 */ + rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000); + + return 0; +} + +u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) +{ + struct rsnd_mod *mod; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct device *dev = rsnd_priv_to_dev(priv); + enum rsnd_mod_type types[] = { + RSND_MOD_SSIM1, + RSND_MOD_SSIM2, + RSND_MOD_SSIM3, + }; + int i, mask; + + switch (runtime->channels) { + case 2: /* Multi channel is not needed for Stereo */ + return 0; + case 6: + break; + default: + dev_err(dev, "unsupported channel\n"); + return 0; + } + + mask = 0; + for (i = 0; i < ARRAY_SIZE(types); i++) { + mod = rsnd_io_to_mod(io, types[i]); + if (!mod) + continue; + + mask |= 1 << rsnd_mod_id(mod); + } + + return mask; +} + static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_io_to_priv(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct rsnd_mod *mod = rsnd_mod_get(ssi); + struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); + int slots = rsnd_get_slot_width(io); int j, ret; int ssi_clk_mul_table[] = { 1, 2, 4, 8, 16, 6, 12, @@ -136,6 +224,24 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, unsigned int main_rate; unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime); + if (!rsnd_rdai_is_clk_master(rdai)) + return 0; + + if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io)) + return 0; + + if (rsnd_ssi_is_multi_slave(mod, io)) + return 0; + + if (ssi->usrcnt > 1) { + if (ssi->rate != rate) { + dev_err(dev, "SSI parent/child should use same rate\n"); + return -EINVAL; + } + + return 0; + } + /* * Find best clock, and try to start ADG */ @@ -143,15 +249,18 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, /* * this driver is assuming that - * system word is 64fs (= 2 x 32bit) + * system word is 32bit x slots * see rsnd_ssi_init() */ - main_rate = rate * 32 * 2 * ssi_clk_mul_table[j]; + main_rate = rate * 32 * slots * ssi_clk_mul_table[j]; ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); if (0 == ret) { ssi->cr_clk = FORCE | SWL_32 | SCKD | SWSD | CKDV(j); + ssi->wsr = CONT; + + ssi->rate = rate; dev_dbg(dev, "%s[%d] outputs %u Hz\n", rsnd_mod_name(mod), @@ -165,113 +274,91 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, return -EIO; } -static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi) +static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi, + struct rsnd_dai_stream *io) { + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct rsnd_mod *mod = rsnd_mod_get(ssi); + struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); + + if (!rsnd_rdai_is_clk_master(rdai)) + return; + + if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io)) + return; + + if (ssi->usrcnt > 1) + return; + + ssi->cr_clk = 0; + ssi->rate = 0; - ssi->cr_clk = 0; rsnd_adg_ssi_clk_stop(mod); } -static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, - struct rsnd_dai_stream *io) +static int rsnd_ssi_config_init(struct rsnd_ssi *ssi, + struct rsnd_dai_stream *io) { - struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_mod *mod = rsnd_mod_get(ssi); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 cr_own; u32 cr_mode; - u32 cr; + u32 wsr; + int is_tdm; - if (0 == ssi->usrcnt) { - rsnd_mod_power_on(mod); + is_tdm = (rsnd_get_slot_width(io) >= 6) ? 1 : 0; - if (rsnd_rdai_is_clk_master(rdai)) { - struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); + /* + * always use 32bit system word. + * see also rsnd_ssi_master_clk_enable() + */ + cr_own = FORCE | SWL_32 | PDTA; - if (ssi_parent) - rsnd_ssi_hw_start(ssi_parent, io); - else - rsnd_ssi_master_clk_start(ssi, io); - } + if (rdai->bit_clk_inv) + cr_own |= SCKP; + if (rdai->frm_clk_inv ^ is_tdm) + cr_own |= SWSP; + if (rdai->data_alignment) + cr_own |= SDTA; + if (rdai->sys_delay) + cr_own |= DEL; + if (rsnd_io_is_play(io)) + cr_own |= TRMD; + + switch (runtime->sample_bits) { + case 16: + cr_own |= DWL_16; + break; + case 32: + cr_own |= DWL_24; + break; + default: + return -EINVAL; } - if (rsnd_ssi_is_dma_mode(mod)) { + if (rsnd_ssi_is_dma_mode(rsnd_mod_get(ssi))) { cr_mode = UIEN | OIEN | /* over/under run */ DMEN; /* DMA : enable DMA */ } else { cr_mode = DIEN; /* PIO : enable Data interrupt */ } - cr = ssi->cr_own | - ssi->cr_clk | - cr_mode | - EN; - - rsnd_mod_write(mod, SSICR, cr); - - /* enable WS continue */ - if (rsnd_rdai_is_clk_master(rdai)) - rsnd_mod_write(mod, SSIWSR, CONT); - - /* clear error status */ - rsnd_mod_write(mod, SSISR, 0); - - ssi->usrcnt++; - - dev_dbg(dev, "%s[%d] hw started\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); -} - -static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi) -{ - struct rsnd_mod *mod = rsnd_mod_get(ssi); - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct device *dev = rsnd_priv_to_dev(priv); - u32 cr; - - if (0 == ssi->usrcnt) { - dev_err(dev, "%s called without starting\n", __func__); - return; + /* + * TDM Extend Mode + * see + * rsnd_ssiu_init_gen2() + */ + wsr = ssi->wsr; + if (is_tdm) { + wsr |= WS_MODE; + cr_own |= CHNL_8; } - ssi->usrcnt--; - - if (0 == ssi->usrcnt) { - /* - * disable all IRQ, - * and, wait all data was sent - */ - cr = ssi->cr_own | - ssi->cr_clk; - - rsnd_mod_write(mod, SSICR, cr | EN); - rsnd_ssi_status_check(mod, DIRQ); - - /* - * disable SSI, - * and, wait idle state - */ - rsnd_mod_write(mod, SSICR, cr); /* disabled all */ - rsnd_ssi_status_check(mod, IIRQ); - - if (rsnd_rdai_is_clk_master(rdai)) { - struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); + ssi->cr_own = cr_own; + ssi->cr_mode = cr_mode; + ssi->wsr = wsr; - if (ssi_parent) - rsnd_ssi_hw_stop(io, ssi_parent); - else - rsnd_ssi_master_clk_stop(ssi); - } - - rsnd_mod_power_off(mod); - - ssi->chan = 0; - } - - dev_dbg(dev, "%s[%d] hw stopped\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + return 0; } /* @@ -282,49 +369,30 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - u32 cr; + int ret; - cr = FORCE; + ssi->usrcnt++; - /* - * always use 32bit system word for easy clock calculation. - * see also rsnd_ssi_master_clk_enable() - */ - cr |= SWL_32; + rsnd_mod_power_on(mod); - /* - * init clock settings for SSICR - */ - switch (runtime->sample_bits) { - case 16: - cr |= DWL_16; - break; - case 32: - cr |= DWL_24; - break; - default: - return -EIO; - } + ret = rsnd_ssi_master_clk_start(ssi, io); + if (ret < 0) + return ret; - if (rdai->bit_clk_inv) - cr |= SCKP; - if (rdai->frm_clk_inv) - cr |= SWSP; - if (rdai->data_alignment) - cr |= SDTA; - if (rdai->sys_delay) - cr |= DEL; - if (rsnd_io_is_play(io)) - cr |= TRMD; + if (rsnd_ssi_is_parent(mod, io)) + return 0; + + ret = rsnd_ssi_config_init(ssi, io); + if (ret < 0) + return ret; - /* - * set ssi parameter - */ - ssi->cr_own = cr; ssi->err = -1; /* ignore 1st error */ + /* clear error status */ + rsnd_ssi_status_clear(mod); + + rsnd_ssi_irq_enable(mod); + return 0; } @@ -335,6 +403,9 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); + if (rsnd_ssi_is_parent(mod, io)) + goto rsnd_ssi_quit_end; + if (ssi->err > 0) dev_warn(dev, "%s[%d] under/over flow err = %d\n", rsnd_mod_name(mod), rsnd_mod_id(mod), ssi->err); @@ -342,6 +413,19 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, ssi->cr_own = 0; ssi->err = 0; + rsnd_ssi_irq_disable(mod); + +rsnd_ssi_quit_end: + rsnd_ssi_master_clk_stop(ssi, io); + + rsnd_mod_power_off(mod); + + ssi->usrcnt--; + + if (ssi->usrcnt < 0) + dev_err(dev, "%s[%d] usrcnt error\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + return 0; } @@ -351,14 +435,13 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod, struct snd_pcm_hw_params *params) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); int chan = params_channels(params); /* * Already working. * It will happen if SSI has parent/child connection. */ - if (ssi->usrcnt) { + if (ssi->usrcnt > 1) { /* * it is error if child <-> parent SSI uses * different channels. @@ -367,39 +450,83 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod, return -EIO; } - /* It will be removed on rsnd_ssi_hw_stop */ ssi->chan = chan; - if (ssi_parent) - return rsnd_ssi_hw_params(rsnd_mod_get(ssi_parent), io, - substream, params); return 0; } -static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) +static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi) { struct rsnd_mod *mod = rsnd_mod_get(ssi); + u32 status = rsnd_ssi_status_get(mod); /* under/over flow error */ - if (status & (UIRQ | OIRQ)) { + if (status & (UIRQ | OIRQ)) ssi->err++; - /* clear error status */ - rsnd_mod_write(mod, SSISR, 0); - } + return status; +} + +static int __rsnd_ssi_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + u32 cr; + + cr = ssi->cr_own | + ssi->cr_clk | + ssi->cr_mode; + + /* + * EN will be set via SSIU :: SSI_CONTROL + * if Multi channel mode + */ + if (!rsnd_ssi_multi_slaves(io)) + cr |= EN; + + rsnd_mod_write(mod, SSICR, cr); + rsnd_mod_write(mod, SSIWSR, ssi->wsr); + + return 0; } static int rsnd_ssi_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { + /* + * no limit to start + * see also + * rsnd_ssi_stop + * rsnd_ssi_interrupt + */ + return __rsnd_ssi_start(mod, io, priv); +} + +static int __rsnd_ssi_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + u32 cr; - rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io)); + /* + * disable all IRQ, + * and, wait all data was sent + */ + cr = ssi->cr_own | + ssi->cr_clk; - rsnd_ssi_hw_start(ssi, io); + rsnd_mod_write(mod, SSICR, cr | EN); + rsnd_ssi_status_check(mod, DIRQ); - rsnd_src_ssi_irq_enable(mod); + /* + * disable SSI, + * and, wait idle state + */ + rsnd_mod_write(mod, SSICR, cr); /* disabled all */ + rsnd_ssi_status_check(mod, IIRQ); return 0; } @@ -410,15 +537,16 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod, { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - rsnd_src_ssi_irq_disable(mod); - - rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); - - rsnd_ssi_hw_stop(io, ssi); - - rsnd_src_ssiu_stop(mod, io); + /* + * don't stop if not last user + * see also + * rsnd_ssi_start + * rsnd_ssi_interrupt + */ + if (ssi->usrcnt > 1) + return 0; - return 0; + return __rsnd_ssi_stop(mod, io, priv); } static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, @@ -426,6 +554,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); int is_dma = rsnd_ssi_is_dma_mode(mod); u32 status; bool elapsed = false; @@ -436,7 +565,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, if (!rsnd_io_is_working(io)) goto rsnd_ssi_interrupt_out; - status = rsnd_mod_read(mod, SSISR); + status = rsnd_ssi_record_error(ssi); /* PIO only */ if (!is_dma && (status & DIRQ)) { @@ -459,23 +588,24 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, /* DMA only */ if (is_dma && (status & (UIRQ | OIRQ))) { - struct device *dev = rsnd_priv_to_dev(priv); - /* * restart SSI */ dev_dbg(dev, "%s[%d] restart\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); - rsnd_ssi_stop(mod, io, priv); - if (ssi->err < 1024) - rsnd_ssi_start(mod, io, priv); - else - dev_warn(dev, "no more SSI restart\n"); + __rsnd_ssi_stop(mod, io, priv); + __rsnd_ssi_start(mod, io, priv); } - rsnd_ssi_record_error(ssi, status); + if (ssi->err > 1024) { + rsnd_ssi_irq_disable(mod); + dev_warn(dev, "no more %s[%d] restart\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + } + + rsnd_ssi_status_clear(mod); rsnd_ssi_interrupt_out: spin_unlock(&priv->lock); @@ -495,15 +625,49 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) /* * SSI PIO */ -static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) +static void rsnd_ssi_parent_attach(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + if (!__rsnd_ssi_is_pin_sharing(mod)) + return; + + switch (rsnd_mod_id(mod)) { + case 1: + case 2: + rsnd_dai_connect(rsnd_ssi_mod_get(priv, 0), io, RSND_MOD_SSIP); + break; + case 4: + rsnd_dai_connect(rsnd_ssi_mod_get(priv, 3), io, RSND_MOD_SSIP); + break; + case 8: + rsnd_dai_connect(rsnd_ssi_mod_get(priv, 7), io, RSND_MOD_SSIP); + break; + } +} + +static int rsnd_ssi_common_probe(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); int ret; - ret = devm_request_irq(dev, ssi->info->irq, + /* + * SSIP/SSIU/IRQ are not needed on + * SSI Multi slaves + */ + if (rsnd_ssi_is_multi_slave(mod, io)) + return 0; + + rsnd_ssi_parent_attach(mod, io, priv); + + ret = rsnd_ssiu_attach(io, mod); + if (ret < 0) + return ret; + + ret = devm_request_irq(dev, ssi->irq, rsnd_ssi_interrupt, IRQF_SHARED, dev_name(dev), mod); @@ -513,7 +677,7 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .name = SSI_NAME, - .probe = rsnd_ssi_pio_probe, + .probe = rsnd_ssi_common_probe, .init = rsnd_ssi_init, .quit = rsnd_ssi_quit, .start = rsnd_ssi_start, @@ -526,20 +690,23 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct device *dev = rsnd_priv_to_dev(priv); - int dma_id = ssi->info->dma_id; + int dma_id = 0; /* not needed */ int ret; - ret = devm_request_irq(dev, ssi->info->irq, - rsnd_ssi_interrupt, - IRQF_SHARED, - dev_name(dev), mod); + /* + * SSIP/SSIU/IRQ/DMA are not needed on + * SSI Multi slaves + */ + if (rsnd_ssi_is_multi_slave(mod, io)) + return 0; + + ret = rsnd_ssi_common_probe(mod, io, priv); if (ret) return ret; - ret = rsnd_dma_init( - io, rsnd_mod_to_dma(mod), - dma_id); + ssi->dma = rsnd_dma_attach(io, mod, dma_id); + if (IS_ERR(ssi->dma)) + return PTR_ERR(ssi->dma); return ret; } @@ -550,9 +717,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); - int irq = ssi->info->irq; - - rsnd_dma_quit(io, rsnd_mod_to_dma(mod)); + int irq = ssi->irq; /* PIO will request IRQ again */ devm_free_irq(dev, irq, mod); @@ -581,32 +746,6 @@ static int rsnd_ssi_fallback(struct rsnd_mod *mod, return 0; } -static int rsnd_ssi_dma_start(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - - rsnd_dma_start(io, dma); - - rsnd_ssi_start(mod, io, priv); - - return 0; -} - -static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - - rsnd_ssi_stop(mod, io, priv); - - rsnd_dma_stop(io, dma); - - return 0; -} - static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { @@ -630,8 +769,8 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .remove = rsnd_ssi_dma_remove, .init = rsnd_ssi_init, .quit = rsnd_ssi_quit, - .start = rsnd_ssi_dma_start, - .stop = rsnd_ssi_dma_stop, + .start = rsnd_ssi_start, + .stop = rsnd_ssi_stop, .fallback = rsnd_ssi_fallback, .hw_params = rsnd_ssi_hw_params, }; @@ -652,110 +791,76 @@ static struct rsnd_mod_ops rsnd_ssi_non_ops = { /* * ssi mod function */ -struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) +static void rsnd_ssi_connect(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) - id = 0; - - return rsnd_mod_get((struct rsnd_ssi *)(priv->ssi) + id); -} - -int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - - return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE); -} - -static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi) -{ - struct rsnd_mod *mod = rsnd_mod_get(ssi); - - if (!__rsnd_ssi_is_pin_sharing(mod)) - return; + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + enum rsnd_mod_type types[] = { + RSND_MOD_SSI, + RSND_MOD_SSIM1, + RSND_MOD_SSIM2, + RSND_MOD_SSIM3, + }; + enum rsnd_mod_type type; + int i; - switch (rsnd_mod_id(mod)) { - case 1: - case 2: - ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 0)); - break; - case 4: - ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 3)); - break; - case 8: - ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 7)); - break; + /* try SSI -> SSIM1 -> SSIM2 -> SSIM3 */ + for (i = 0; i < ARRAY_SIZE(types); i++) { + type = types[i]; + if (!rsnd_io_to_mod(io, type)) { + rsnd_dai_connect(mod, io, type); + rsnd_set_slot(rdai, 2 * (i + 1), (i + 1)); + return; + } } } - -static void rsnd_of_parse_ssi(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) +void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, + struct device_node *playback, + struct device_node *capture) { + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); struct device_node *node; struct device_node *np; - struct rsnd_ssi_platform_info *ssi_info; - struct rcar_snd_info *info = rsnd_priv_to_info(priv); - struct device *dev = &pdev->dev; - int nr, i; + struct rsnd_mod *mod; + int i; node = rsnd_ssi_of_node(priv); if (!node) return; - nr = of_get_child_count(node); - if (!nr) - goto rsnd_of_parse_ssi_end; - - ssi_info = devm_kzalloc(dev, - sizeof(struct rsnd_ssi_platform_info) * nr, - GFP_KERNEL); - if (!ssi_info) { - dev_err(dev, "ssi info allocation error\n"); - goto rsnd_of_parse_ssi_end; - } - - info->ssi_info = ssi_info; - info->ssi_info_nr = nr; - - i = -1; + i = 0; for_each_child_of_node(node, np) { + mod = rsnd_ssi_mod_get(priv, i); + if (np == playback) + rsnd_ssi_connect(mod, &rdai->playback); + if (np == capture) + rsnd_ssi_connect(mod, &rdai->capture); i++; + } - ssi_info = info->ssi_info + i; - - /* - * pin settings - */ - if (of_get_property(np, "shared-pin", NULL)) - ssi_info->flags |= RSND_SSI_CLK_PIN_SHARE; + of_node_put(node); +} - /* - * irq - */ - ssi_info->irq = irq_of_parse_and_map(np, 0); +struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) + id = 0; - /* - * DMA - */ - ssi_info->dma_id = of_get_property(np, "pio-transfer", NULL) ? - 0 : 1; + return rsnd_mod_get(rsnd_ssi_get(priv, id)); +} - if (of_get_property(np, "no-busif", NULL)) - ssi_info->flags |= RSND_SSI_NO_BUSIF; - } +int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); -rsnd_of_parse_ssi_end: - of_node_put(node); + return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE); } -int rsnd_ssi_probe(struct platform_device *pdev, - const struct rsnd_of_data *of_data, - struct rsnd_priv *priv) +int rsnd_ssi_probe(struct rsnd_priv *priv) { - struct rcar_snd_info *info = rsnd_priv_to_info(priv); - struct rsnd_ssi_platform_info *pinfo; + struct device_node *node; + struct device_node *np; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mod_ops *ops; struct clk *clk; @@ -763,50 +868,73 @@ int rsnd_ssi_probe(struct platform_device *pdev, char name[RSND_SSI_NAME_SIZE]; int i, nr, ret; - rsnd_of_parse_ssi(pdev, of_data, priv); + node = rsnd_ssi_of_node(priv); + if (!node) + return -EINVAL; + + nr = of_get_child_count(node); + if (!nr) { + ret = -EINVAL; + goto rsnd_ssi_probe_done; + } - /* - * init SSI - */ - nr = info->ssi_info_nr; ssi = devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL); - if (!ssi) - return -ENOMEM; + if (!ssi) { + ret = -ENOMEM; + goto rsnd_ssi_probe_done; + } priv->ssi = ssi; priv->ssi_nr = nr; - for_each_rsnd_ssi(ssi, priv, i) { - pinfo = &info->ssi_info[i]; + i = 0; + for_each_child_of_node(node, np) { + ssi = rsnd_ssi_get(priv, i); snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d", SSI_NAME, i); clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) - return PTR_ERR(clk); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto rsnd_ssi_probe_done; + } - ssi->info = pinfo; + if (of_get_property(np, "shared-pin", NULL)) + ssi->flags |= RSND_SSI_CLK_PIN_SHARE; + + if (of_get_property(np, "no-busif", NULL)) + ssi->flags |= RSND_SSI_NO_BUSIF; + + ssi->irq = irq_of_parse_and_map(np, 0); + if (!ssi->irq) { + ret = -EINVAL; + goto rsnd_ssi_probe_done; + } ops = &rsnd_ssi_non_ops; - if (pinfo->dma_id > 0) - ops = &rsnd_ssi_dma_ops; - else if (rsnd_ssi_pio_available(ssi)) + if (of_get_property(np, "pio-transfer", NULL)) ops = &rsnd_ssi_pio_ops; + else + ops = &rsnd_ssi_dma_ops; ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, RSND_MOD_SSI, i); if (ret) - return ret; + goto rsnd_ssi_probe_done; - rsnd_ssi_parent_setup(priv, ssi); + i++; } - return 0; + ret = 0; + +rsnd_ssi_probe_done: + of_node_put(node); + + return ret; } -void rsnd_ssi_remove(struct platform_device *pdev, - struct rsnd_priv *priv) +void rsnd_ssi_remove(struct rsnd_priv *priv) { struct rsnd_ssi *ssi; int i; diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c new file mode 100644 index 000000000000..3fe9e08e81a3 --- /dev/null +++ b/sound/soc/sh/rcar/ssiu.c @@ -0,0 +1,225 @@ +/* + * Renesas R-Car SSIU support + * + * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.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. + */ +#include "rsnd.h" + +#define SSIU_NAME "ssiu" + +struct rsnd_ssiu { + struct rsnd_mod mod; +}; + +#define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr) +#define for_each_rsnd_ssiu(pos, priv, i) \ + for (i = 0; \ + (i < rsnd_ssiu_nr(priv)) && \ + ((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i)); \ + i++) + +static int rsnd_ssiu_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + u32 multi_ssi_slaves = rsnd_ssi_multi_slaves(io); + int use_busif = rsnd_ssi_use_busif(io); + int id = rsnd_mod_id(mod); + u32 mask1, val1; + u32 mask2, val2; + + /* + * SSI_MODE0 + */ + rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id); + + /* + * SSI_MODE1 + */ + mask1 = (1 << 4) | (1 << 20); /* mask sync bit */ + mask2 = (1 << 4); /* mask sync bit */ + val1 = val2 = 0; + if (rsnd_ssi_is_pin_sharing(io)) { + int shift = -1; + + switch (id) { + case 1: + shift = 0; + break; + case 2: + shift = 2; + break; + case 4: + shift = 16; + break; + default: + return -EINVAL; + } + + mask1 |= 0x3 << shift; + val1 = rsnd_rdai_is_clk_master(rdai) ? + 0x2 << shift : 0x1 << shift; + + } else if (multi_ssi_slaves) { + + mask2 |= 0x00000007; + mask1 |= 0x0000000f; + + switch (multi_ssi_slaves) { + case 0x0206: /* SSI0/1/2/9 */ + val2 = (1 << 4) | /* SSI0129 sync */ + rsnd_rdai_is_clk_master(rdai) ? 0x2 : 0x1; + /* fall through */ + case 0x0006: /* SSI0/1/2 */ + val1 = rsnd_rdai_is_clk_master(rdai) ? + 0xa : 0x5; + + if (!val2) /* SSI012 sync */ + val1 |= (1 << 4); + } + } + + rsnd_mod_bset(mod, SSI_MODE1, mask1, val1); + rsnd_mod_bset(mod, SSI_MODE2, mask2, val2); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssiu_ops_gen1 = { + .name = SSIU_NAME, + .init = rsnd_ssiu_init, +}; + +static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + int ret; + + ret = rsnd_ssiu_init(mod, io, priv); + if (ret < 0) + return ret; + + if (rsnd_get_slot_width(io) >= 6) { + /* + * TDM Extend Mode + * see + * rsnd_ssi_config_init() + */ + rsnd_mod_write(mod, SSI_MODE, 0x1); + } + + if (rsnd_ssi_use_busif(io)) { + u32 val = rsnd_get_dalign(mod, io); + + rsnd_mod_write(mod, SSI_BUSIF_ADINR, + rsnd_get_adinr_bit(mod, io) | + rsnd_get_adinr_chan(mod, io)); + rsnd_mod_write(mod, SSI_BUSIF_MODE, 1); + rsnd_mod_write(mod, SSI_BUSIF_DALIGN, val); + } + + return 0; +} + +static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + if (!rsnd_ssi_use_busif(io)) + return 0; + + rsnd_mod_write(mod, SSI_CTRL, 0x1); + + if (rsnd_ssi_multi_slaves(io)) + rsnd_mod_write(mod, SSI_CONTROL, 0x1); + + return 0; +} + +static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) +{ + if (!rsnd_ssi_use_busif(io)) + return 0; + + rsnd_mod_write(mod, SSI_CTRL, 0); + + if (rsnd_ssi_multi_slaves(io)) + rsnd_mod_write(mod, SSI_CONTROL, 0); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = { + .name = SSIU_NAME, + .init = rsnd_ssiu_init_gen2, + .start = rsnd_ssiu_start_gen2, + .stop = rsnd_ssiu_stop_gen2, +}; + +static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_ssiu_nr(priv))) + id = 0; + + return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id); +} + +int rsnd_ssiu_attach(struct rsnd_dai_stream *io, + struct rsnd_mod *ssi_mod) +{ + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct rsnd_mod *mod = rsnd_ssiu_mod_get(priv, rsnd_mod_id(ssi_mod)); + + rsnd_mod_confirm_ssi(ssi_mod); + + return rsnd_dai_connect(mod, io, mod->type); +} + +int rsnd_ssiu_probe(struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_ssiu *ssiu; + static struct rsnd_mod_ops *ops; + int i, nr, ret; + + /* same number to SSI */ + nr = priv->ssi_nr; + ssiu = devm_kzalloc(dev, sizeof(*ssiu) * nr, GFP_KERNEL); + if (!ssiu) + return -ENOMEM; + + priv->ssiu = ssiu; + priv->ssiu_nr = nr; + + if (rsnd_is_gen1(priv)) + ops = &rsnd_ssiu_ops_gen1; + else + ops = &rsnd_ssiu_ops_gen2; + + for_each_rsnd_ssiu(ssiu, priv, i) { + ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu), + ops, NULL, RSND_MOD_SSIU, i); + if (ret) + return ret; + } + + return 0; +} + +void rsnd_ssiu_remove(struct rsnd_priv *priv) +{ + struct rsnd_ssiu *ssiu; + int i; + + for_each_rsnd_ssiu(ssiu, priv, i) { + rsnd_mod_quit(rsnd_mod_get(ssiu)); + } +} diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c index d40efc9fe0a9..733f5128eeff 100644 --- a/sound/soc/soc-ac97.c +++ b/sound/soc/soc-ac97.c @@ -20,6 +20,7 @@ #include <linux/delay.h> #include <linux/export.h> #include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/init.h> #include <linux/of_gpio.h> #include <linux/of.h> @@ -38,6 +39,14 @@ struct snd_ac97_reset_cfg { int gpio_reset; }; +struct snd_ac97_gpio_priv { +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio_chip; +#endif + unsigned int gpios_set; + struct snd_soc_codec *codec; +}; + static struct snd_ac97_bus soc_ac97_bus = { .ops = NULL, /* Gets initialized in snd_soc_set_ac97_ops() */ }; @@ -47,6 +56,117 @@ static void soc_ac97_device_release(struct device *dev) kfree(to_ac97_t(dev)); } +#ifdef CONFIG_GPIOLIB +static inline struct snd_soc_codec *gpio_to_codec(struct gpio_chip *chip) +{ + struct snd_ac97_gpio_priv *gpio_priv = + container_of(chip, struct snd_ac97_gpio_priv, gpio_chip); + + return gpio_priv->codec; +} + +static int snd_soc_ac97_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + if (offset >= AC97_NUM_GPIOS) + return -EINVAL; + + return 0; +} + +static int snd_soc_ac97_gpio_direction_in(struct gpio_chip *chip, + unsigned offset) +{ + struct snd_soc_codec *codec = gpio_to_codec(chip); + + dev_dbg(codec->dev, "set gpio %d to output\n", offset); + return snd_soc_update_bits(codec, AC97_GPIO_CFG, + 1 << offset, 1 << offset); +} + +static int snd_soc_ac97_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct snd_soc_codec *codec = gpio_to_codec(chip); + int ret; + + ret = snd_soc_read(codec, AC97_GPIO_STATUS); + dev_dbg(codec->dev, "get gpio %d : %d\n", offset, + ret < 0 ? ret : ret & (1 << offset)); + + return ret < 0 ? ret : !!(ret & (1 << offset)); +} + +static void snd_soc_ac97_gpio_set(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct snd_ac97_gpio_priv *gpio_priv = + container_of(chip, struct snd_ac97_gpio_priv, gpio_chip); + struct snd_soc_codec *codec = gpio_to_codec(chip); + + gpio_priv->gpios_set &= ~(1 << offset); + gpio_priv->gpios_set |= (!!value) << offset; + snd_soc_write(codec, AC97_GPIO_STATUS, gpio_priv->gpios_set); + dev_dbg(codec->dev, "set gpio %d to %d\n", offset, !!value); +} + +static int snd_soc_ac97_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct snd_soc_codec *codec = gpio_to_codec(chip); + + dev_dbg(codec->dev, "set gpio %d to output\n", offset); + snd_soc_ac97_gpio_set(chip, offset, value); + return snd_soc_update_bits(codec, AC97_GPIO_CFG, 1 << offset, 0); +} + +static struct gpio_chip snd_soc_ac97_gpio_chip = { + .label = "snd_soc_ac97", + .owner = THIS_MODULE, + .request = snd_soc_ac97_gpio_request, + .direction_input = snd_soc_ac97_gpio_direction_in, + .get = snd_soc_ac97_gpio_get, + .direction_output = snd_soc_ac97_gpio_direction_out, + .set = snd_soc_ac97_gpio_set, + .can_sleep = 1, +}; + +static int snd_soc_ac97_init_gpio(struct snd_ac97 *ac97, + struct snd_soc_codec *codec) +{ + struct snd_ac97_gpio_priv *gpio_priv; + int ret; + + gpio_priv = devm_kzalloc(codec->dev, sizeof(*gpio_priv), GFP_KERNEL); + if (!gpio_priv) + return -ENOMEM; + ac97->gpio_priv = gpio_priv; + gpio_priv->codec = codec; + gpio_priv->gpio_chip = snd_soc_ac97_gpio_chip; + gpio_priv->gpio_chip.ngpio = AC97_NUM_GPIOS; + gpio_priv->gpio_chip.dev = codec->dev; + gpio_priv->gpio_chip.base = -1; + + ret = gpiochip_add(&gpio_priv->gpio_chip); + if (ret != 0) + dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret); + return ret; +} + +static void snd_soc_ac97_free_gpio(struct snd_ac97 *ac97) +{ + gpiochip_remove(&ac97->gpio_priv->gpio_chip); +} +#else +static int snd_soc_ac97_init_gpio(struct snd_ac97 *ac97, + struct snd_soc_codec *codec) +{ + return 0; +} + +static void snd_soc_ac97_free_gpio(struct snd_ac97 *ac97) +{ +} +#endif + /** * snd_soc_alloc_ac97_codec() - Allocate new a AC'97 device * @codec: The CODEC for which to create the AC'97 device @@ -119,6 +239,10 @@ struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec, if (ret) goto err_put_device; + ret = snd_soc_ac97_init_gpio(ac97, codec); + if (ret) + goto err_put_device; + return ac97; err_put_device: @@ -135,6 +259,7 @@ EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); */ void snd_soc_free_ac97_codec(struct snd_ac97 *ac97) { + snd_soc_ac97_free_gpio(ac97); device_del(&ac97->dev); ac97->bus = NULL; put_device(&ac97->dev); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 24b096066a07..19f7486bf335 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -537,26 +537,75 @@ static inline void snd_soc_debugfs_exit(void) struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, const char *dai_link, int stream) { - int i; + struct snd_soc_pcm_runtime *rtd; - for (i = 0; i < card->num_links; i++) { - if (card->rtd[i].dai_link->no_pcm && - !strcmp(card->rtd[i].dai_link->name, dai_link)) - return card->rtd[i].pcm->streams[stream].substream; + list_for_each_entry(rtd, &card->rtd_list, list) { + if (rtd->dai_link->no_pcm && + !strcmp(rtd->dai_link->name, dai_link)) + return rtd->pcm->streams[stream].substream; } dev_dbg(card->dev, "ASoC: failed to find dai link %s\n", dai_link); return NULL; } EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream); +static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( + struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) +{ + struct snd_soc_pcm_runtime *rtd; + + rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL); + if (!rtd) + return NULL; + + rtd->card = card; + rtd->dai_link = dai_link; + rtd->codec_dais = kzalloc(sizeof(struct snd_soc_dai *) * + dai_link->num_codecs, + GFP_KERNEL); + if (!rtd->codec_dais) { + kfree(rtd); + return NULL; + } + + return rtd; +} + +static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd) +{ + if (rtd && rtd->codec_dais) + kfree(rtd->codec_dais); + kfree(rtd); +} + +static void soc_add_pcm_runtime(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd) +{ + list_add_tail(&rtd->list, &card->rtd_list); + rtd->num = card->num_rtd; + card->num_rtd++; +} + +static void soc_remove_pcm_runtimes(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *rtd, *_rtd; + + list_for_each_entry_safe(rtd, _rtd, &card->rtd_list, list) { + list_del(&rtd->list); + soc_free_pcm_runtime(rtd); + } + + card->num_rtd = 0; +} + struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, const char *dai_link) { - int i; + struct snd_soc_pcm_runtime *rtd; - for (i = 0; i < card->num_links; i++) { - if (!strcmp(card->rtd[i].dai_link->name, dai_link)) - return &card->rtd[i]; + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!strcmp(rtd->dai_link->name, dai_link)) + return rtd; } dev_dbg(card->dev, "ASoC: failed to find rtd %s\n", dai_link); return NULL; @@ -578,7 +627,8 @@ int snd_soc_suspend(struct device *dev) { struct snd_soc_card *card = dev_get_drvdata(dev); struct snd_soc_codec *codec; - int i, j; + struct snd_soc_pcm_runtime *rtd; + int i; /* If the card is not initialized yet there is nothing to do */ if (!card->instantiated) @@ -595,13 +645,13 @@ int snd_soc_suspend(struct device *dev) snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot); /* mute any active DACs */ - for (i = 0; i < card->num_rtd; i++) { + list_for_each_entry(rtd, &card->rtd_list, list) { - if (card->rtd[i].dai_link->ignore_suspend) + if (rtd->dai_link->ignore_suspend) continue; - for (j = 0; j < card->rtd[i].num_codecs; j++) { - struct snd_soc_dai *dai = card->rtd[i].codec_dais[j]; + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *dai = rtd->codec_dais[i]; struct snd_soc_dai_driver *drv = dai->driver; if (drv->ops->digital_mute && dai->playback_active) @@ -610,20 +660,20 @@ int snd_soc_suspend(struct device *dev) } /* suspend all pcms */ - for (i = 0; i < card->num_rtd; i++) { - if (card->rtd[i].dai_link->ignore_suspend) + list_for_each_entry(rtd, &card->rtd_list, list) { + if (rtd->dai_link->ignore_suspend) continue; - snd_pcm_suspend_all(card->rtd[i].pcm); + snd_pcm_suspend_all(rtd->pcm); } if (card->suspend_pre) card->suspend_pre(card); - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; + list_for_each_entry(rtd, &card->rtd_list, list) { + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - if (card->rtd[i].dai_link->ignore_suspend) + if (rtd->dai_link->ignore_suspend) continue; if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control) @@ -631,19 +681,19 @@ int snd_soc_suspend(struct device *dev) } /* close any waiting streams */ - for (i = 0; i < card->num_rtd; i++) - flush_delayed_work(&card->rtd[i].delayed_work); + list_for_each_entry(rtd, &card->rtd_list, list) + flush_delayed_work(&rtd->delayed_work); - for (i = 0; i < card->num_rtd; i++) { + list_for_each_entry(rtd, &card->rtd_list, list) { - if (card->rtd[i].dai_link->ignore_suspend) + if (rtd->dai_link->ignore_suspend) continue; - snd_soc_dapm_stream_event(&card->rtd[i], + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_SUSPEND); - snd_soc_dapm_stream_event(&card->rtd[i], + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, SND_SOC_DAPM_STREAM_SUSPEND); } @@ -690,10 +740,10 @@ int snd_soc_suspend(struct device *dev) } } - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; + list_for_each_entry(rtd, &card->rtd_list, list) { + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - if (card->rtd[i].dai_link->ignore_suspend) + if (rtd->dai_link->ignore_suspend) continue; if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control) @@ -717,8 +767,9 @@ static void soc_resume_deferred(struct work_struct *work) { struct snd_soc_card *card = container_of(work, struct snd_soc_card, deferred_resume_work); + struct snd_soc_pcm_runtime *rtd; struct snd_soc_codec *codec; - int i, j; + int i; /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time, * so userspace apps are blocked from touching us @@ -733,10 +784,10 @@ static void soc_resume_deferred(struct work_struct *work) card->resume_pre(card); /* resume control bus DAIs */ - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; + list_for_each_entry(rtd, &card->rtd_list, list) { + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - if (card->rtd[i].dai_link->ignore_suspend) + if (rtd->dai_link->ignore_suspend) continue; if (cpu_dai->driver->resume && cpu_dai->driver->bus_control) @@ -751,28 +802,28 @@ static void soc_resume_deferred(struct work_struct *work) } } - for (i = 0; i < card->num_rtd; i++) { + list_for_each_entry(rtd, &card->rtd_list, list) { - if (card->rtd[i].dai_link->ignore_suspend) + if (rtd->dai_link->ignore_suspend) continue; - snd_soc_dapm_stream_event(&card->rtd[i], + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_RESUME); - snd_soc_dapm_stream_event(&card->rtd[i], + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, SND_SOC_DAPM_STREAM_RESUME); } /* unmute any active DACs */ - for (i = 0; i < card->num_rtd; i++) { + list_for_each_entry(rtd, &card->rtd_list, list) { - if (card->rtd[i].dai_link->ignore_suspend) + if (rtd->dai_link->ignore_suspend) continue; - for (j = 0; j < card->rtd[i].num_codecs; j++) { - struct snd_soc_dai *dai = card->rtd[i].codec_dais[j]; + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *dai = rtd->codec_dais[i]; struct snd_soc_dai_driver *drv = dai->driver; if (drv->ops->digital_mute && dai->playback_active) @@ -780,10 +831,10 @@ static void soc_resume_deferred(struct work_struct *work) } } - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; + list_for_each_entry(rtd, &card->rtd_list, list) { + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - if (card->rtd[i].dai_link->ignore_suspend) + if (rtd->dai_link->ignore_suspend) continue; if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control) @@ -795,12 +846,12 @@ static void soc_resume_deferred(struct work_struct *work) dev_dbg(card->dev, "ASoC: resume work completed\n"); - /* userspace can access us now we are back as we were before */ - snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0); - /* Recheck all endpoints too, their state is affected by suspend */ dapm_mark_endpoints_dirty(card); snd_soc_dapm_sync(&card->dapm); + + /* userspace can access us now we are back as we were before */ + snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0); } /* powers up audio subsystem after a suspend */ @@ -808,15 +859,14 @@ int snd_soc_resume(struct device *dev) { struct snd_soc_card *card = dev_get_drvdata(dev); bool bus_control = false; - int i; + struct snd_soc_pcm_runtime *rtd; /* If the card is not initialized yet there is nothing to do */ if (!card->instantiated) return 0; /* activate pins from sleep state */ - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; + list_for_each_entry(rtd, &card->rtd_list, list) { struct snd_soc_dai **codec_dais = rtd->codec_dais; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int j; @@ -837,8 +887,8 @@ int snd_soc_resume(struct device *dev) * have that problem and may take a substantial amount of time to resume * due to I/O costs and anti-pop so handle them out of line. */ - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; + list_for_each_entry(rtd, &card->rtd_list, list) { + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; bus_control |= cpu_dai->driver->bus_control; } if (bus_control) { @@ -910,18 +960,41 @@ static struct snd_soc_dai *snd_soc_find_dai( return NULL; } -static int soc_bind_dai_link(struct snd_soc_card *card, int num) +static bool soc_is_dai_link_bound(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link) { - struct snd_soc_dai_link *dai_link = &card->dai_link[num]; - struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (rtd->dai_link == dai_link) + return true; + } + + return false; +} + +static int soc_bind_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link) +{ + struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai_link_component *codecs = dai_link->codecs; struct snd_soc_dai_link_component cpu_dai_component; - struct snd_soc_dai **codec_dais = rtd->codec_dais; + struct snd_soc_dai **codec_dais; struct snd_soc_platform *platform; const char *platform_name; int i; - dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num); + dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name); + + rtd = soc_new_pcm_runtime(card, dai_link); + if (!rtd) + return -ENOMEM; + + if (soc_is_dai_link_bound(card, dai_link)) { + dev_dbg(card->dev, "ASoC: dai link %s already bound\n", + dai_link->name); + return 0; + } cpu_dai_component.name = dai_link->cpu_name; cpu_dai_component.of_node = dai_link->cpu_of_node; @@ -930,18 +1003,19 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) if (!rtd->cpu_dai) { dev_err(card->dev, "ASoC: CPU DAI %s not registered\n", dai_link->cpu_dai_name); - return -EPROBE_DEFER; + goto _err_defer; } rtd->num_codecs = dai_link->num_codecs; /* Find CODEC from registered CODECs */ + codec_dais = rtd->codec_dais; for (i = 0; i < rtd->num_codecs; i++) { codec_dais[i] = snd_soc_find_dai(&codecs[i]); if (!codec_dais[i]) { dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n", codecs[i].dai_name); - return -EPROBE_DEFER; + goto _err_defer; } } @@ -973,9 +1047,12 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) return -EPROBE_DEFER; } - card->num_rtd++; - + soc_add_pcm_runtime(card, rtd); return 0; + +_err_defer: + soc_free_pcm_runtime(rtd); + return -EPROBE_DEFER; } static void soc_remove_component(struct snd_soc_component *component) @@ -1014,9 +1091,9 @@ static void soc_remove_dai(struct snd_soc_dai *dai, int order) } } -static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order) +static void soc_remove_link_dais(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd, int order) { - struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; int i; /* unregister the rtd device */ @@ -1032,10 +1109,9 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order) soc_remove_dai(rtd->cpu_dai, order); } -static void soc_remove_link_components(struct snd_soc_card *card, int num, - int order) +static void soc_remove_link_components(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd, int order) { - struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; @@ -1061,22 +1137,199 @@ static void soc_remove_link_components(struct snd_soc_card *card, int num, static void soc_remove_dai_links(struct snd_soc_card *card) { - int dai, order; + int order; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai_link *link, *_link; for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; order++) { - for (dai = 0; dai < card->num_rtd; dai++) - soc_remove_link_dais(card, dai, order); + list_for_each_entry(rtd, &card->rtd_list, list) + soc_remove_link_dais(card, rtd, order); } for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; order++) { - for (dai = 0; dai < card->num_rtd; dai++) - soc_remove_link_components(card, dai, order); + list_for_each_entry(rtd, &card->rtd_list, list) + soc_remove_link_components(card, rtd, order); } - card->num_rtd = 0; + list_for_each_entry_safe(link, _link, &card->dai_link_list, list) { + if (link->dobj.type == SND_SOC_DOBJ_DAI_LINK) + dev_warn(card->dev, "Topology forgot to remove link %s?\n", + link->name); + + list_del(&link->list); + card->num_dai_links--; + } +} + +static int snd_soc_init_multicodec(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link) +{ + /* Legacy codec/codec_dai link is a single entry in multicodec */ + if (dai_link->codec_name || dai_link->codec_of_node || + dai_link->codec_dai_name) { + dai_link->num_codecs = 1; + + dai_link->codecs = devm_kzalloc(card->dev, + sizeof(struct snd_soc_dai_link_component), + GFP_KERNEL); + if (!dai_link->codecs) + return -ENOMEM; + + dai_link->codecs[0].name = dai_link->codec_name; + dai_link->codecs[0].of_node = dai_link->codec_of_node; + dai_link->codecs[0].dai_name = dai_link->codec_dai_name; + } + + if (!dai_link->codecs) { + dev_err(card->dev, "ASoC: DAI link has no CODECs\n"); + return -EINVAL; + } + + return 0; +} + +static int soc_init_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link) +{ + int i, ret; + + ret = snd_soc_init_multicodec(card, link); + if (ret) { + dev_err(card->dev, "ASoC: failed to init multicodec\n"); + return ret; + } + + for (i = 0; i < link->num_codecs; i++) { + /* + * Codec must be specified by 1 of name or OF node, + * not both or neither. + */ + if (!!link->codecs[i].name == + !!link->codecs[i].of_node) { + dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n", + link->name); + return -EINVAL; + } + /* Codec DAI name must be specified */ + if (!link->codecs[i].dai_name) { + dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n", + link->name); + return -EINVAL; + } + } + + /* + * Platform may be specified by either name or OF node, but + * can be left unspecified, and a dummy platform will be used. + */ + if (link->platform_name && link->platform_of_node) { + dev_err(card->dev, + "ASoC: Both platform name/of_node are set for %s\n", + link->name); + return -EINVAL; + } + + /* + * CPU device may be specified by either name or OF node, but + * can be left unspecified, and will be matched based on DAI + * name alone.. + */ + if (link->cpu_name && link->cpu_of_node) { + dev_err(card->dev, + "ASoC: Neither/both cpu name/of_node are set for %s\n", + link->name); + return -EINVAL; + } + /* + * At least one of CPU DAI name or CPU device name/node must be + * specified + */ + if (!link->cpu_dai_name && + !(link->cpu_name || link->cpu_of_node)) { + dev_err(card->dev, + "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", + link->name); + return -EINVAL; + } + + return 0; +} + +/** + * snd_soc_add_dai_link - Add a DAI link dynamically + * @card: The ASoC card to which the DAI link is added + * @dai_link: The new DAI link to add + * + * This function adds a DAI link to the ASoC card's link list. + * + * Note: Topology can use this API to add DAI links when probing the + * topology component. And machine drivers can still define static + * DAI links in dai_link array. + */ +int snd_soc_add_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link) +{ + if (dai_link->dobj.type + && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) { + dev_err(card->dev, "Invalid dai link type %d\n", + dai_link->dobj.type); + return -EINVAL; + } + + lockdep_assert_held(&client_mutex); + /* Notify the machine driver for extra initialization + * on the link created by topology. + */ + if (dai_link->dobj.type && card->add_dai_link) + card->add_dai_link(card, dai_link); + + list_add_tail(&dai_link->list, &card->dai_link_list); + card->num_dai_links++; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_add_dai_link); + +/** + * snd_soc_remove_dai_link - Remove a DAI link from the list + * @card: The ASoC card that owns the link + * @dai_link: The DAI link to remove + * + * This function removes a DAI link from the ASoC card's link list. + * + * For DAI links previously added by topology, topology should + * remove them by using the dobj embedded in the link. + */ +void snd_soc_remove_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link) +{ + struct snd_soc_dai_link *link, *_link; + + if (dai_link->dobj.type + && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) { + dev_err(card->dev, "Invalid dai link type %d\n", + dai_link->dobj.type); + return; + } + + lockdep_assert_held(&client_mutex); + /* Notify the machine driver for extra destruction + * on the link created by topology. + */ + if (dai_link->dobj.type && card->remove_dai_link) + card->remove_dai_link(card, dai_link); + + list_for_each_entry_safe(link, _link, &card->dai_link_list, list) { + if (link == dai_link) { + list_del(&link->list); + card->num_dai_links--; + return; + } + } } +EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link); static void soc_set_name_prefix(struct snd_soc_card *card, struct snd_soc_component *component) @@ -1220,10 +1473,10 @@ static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd, return 0; } -static int soc_probe_link_components(struct snd_soc_card *card, int num, +static int soc_probe_link_components(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd, int order) { - struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; int i, ret; @@ -1283,35 +1536,35 @@ static int soc_link_dai_widgets(struct snd_soc_card *card, { struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dapm_widget *play_w, *capture_w; + struct snd_soc_dapm_widget *sink, *source; int ret; if (rtd->num_codecs > 1) dev_warn(card->dev, "ASoC: Multiple codecs not supported yet\n"); /* link the DAI widgets */ - play_w = codec_dai->playback_widget; - capture_w = cpu_dai->capture_widget; - if (play_w && capture_w) { + sink = codec_dai->playback_widget; + source = cpu_dai->capture_widget; + if (sink && source) { ret = snd_soc_dapm_new_pcm(card, dai_link->params, - dai_link->num_params, capture_w, - play_w); + dai_link->num_params, + source, sink); if (ret != 0) { dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", - play_w->name, capture_w->name, ret); + sink->name, source->name, ret); return ret; } } - play_w = cpu_dai->playback_widget; - capture_w = codec_dai->capture_widget; - if (play_w && capture_w) { + sink = cpu_dai->playback_widget; + source = codec_dai->capture_widget; + if (sink && source) { ret = snd_soc_dapm_new_pcm(card, dai_link->params, - dai_link->num_params, capture_w, - play_w); + dai_link->num_params, + source, sink); if (ret != 0) { dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", - play_w->name, capture_w->name, ret); + sink->name, source->name, ret); return ret; } } @@ -1319,15 +1572,15 @@ static int soc_link_dai_widgets(struct snd_soc_card *card, return 0; } -static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) +static int soc_probe_link_dais(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd, int order) { - struct snd_soc_dai_link *dai_link = &card->dai_link[num]; - struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; + struct snd_soc_dai_link *dai_link = rtd->dai_link; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int i, ret; dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n", - card->name, num, order); + card->name, rtd->num, order); /* set default power off timeout */ rtd->pmdown_time = pmdown_time; @@ -1372,7 +1625,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) if (cpu_dai->driver->compress_new) { /*create compress_device"*/ - ret = cpu_dai->driver->compress_new(rtd, num); + ret = cpu_dai->driver->compress_new(rtd, rtd->num); if (ret < 0) { dev_err(card->dev, "ASoC: can't create compress %s\n", dai_link->stream_name); @@ -1382,7 +1635,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) if (!dai_link->params) { /* create the pcm */ - ret = soc_new_pcm(rtd, num); + ret = soc_new_pcm(rtd, rtd->num); if (ret < 0) { dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", dai_link->stream_name, ret); @@ -1552,6 +1805,8 @@ EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt); static int snd_soc_instantiate_card(struct snd_soc_card *card) { struct snd_soc_codec *codec; + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dai_link *dai_link; int ret, i, order; mutex_lock(&client_mutex); @@ -1559,7 +1814,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) /* bind DAIs */ for (i = 0; i < card->num_links; i++) { - ret = soc_bind_dai_link(card, i); + ret = soc_bind_dai_link(card, &card->dai_link[i]); if (ret != 0) goto base_error; } @@ -1571,6 +1826,10 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) goto base_error; } + /* add predefined DAI links to the list */ + for (i = 0; i < card->num_links; i++) + snd_soc_add_dai_link(card, card->dai_link+i); + /* initialize the register cache for each available codec */ list_for_each_entry(codec, &codec_list, list) { if (codec->cache_init) @@ -1624,8 +1883,8 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) /* probe all components used by DAI links on this card */ for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; order++) { - for (i = 0; i < card->num_links; i++) { - ret = soc_probe_link_components(card, i, order); + list_for_each_entry(rtd, &card->rtd_list, list) { + ret = soc_probe_link_components(card, rtd, order); if (ret < 0) { dev_err(card->dev, "ASoC: failed to instantiate card %d\n", @@ -1635,11 +1894,26 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) } } + /* Find new DAI links added during probing components and bind them. + * Components with topology may bring new DAIs and DAI links. + */ + list_for_each_entry(dai_link, &card->dai_link_list, list) { + if (soc_is_dai_link_bound(card, dai_link)) + continue; + + ret = soc_init_dai_link(card, dai_link); + if (ret) + goto probe_dai_err; + ret = soc_bind_dai_link(card, dai_link); + if (ret) + goto probe_dai_err; + } + /* probe all DAI links on this card */ for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; order++) { - for (i = 0; i < card->num_links; i++) { - ret = soc_probe_link_dais(card, i, order); + list_for_each_entry(rtd, &card->rtd_list, list) { + ret = soc_probe_link_dais(card, rtd, order); if (ret < 0) { dev_err(card->dev, "ASoC: failed to instantiate card %d\n", @@ -1733,6 +2007,7 @@ card_probe_error: snd_card_free(card->snd_card); base_error: + soc_remove_pcm_runtimes(card); mutex_unlock(&card->mutex); mutex_unlock(&client_mutex); @@ -1763,13 +2038,12 @@ static int soc_probe(struct platform_device *pdev) static int soc_cleanup_card_resources(struct snd_soc_card *card) { + struct snd_soc_pcm_runtime *rtd; int i; /* make sure any delayed work runs */ - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; + list_for_each_entry(rtd, &card->rtd_list, list) flush_delayed_work(&rtd->delayed_work); - } /* remove auxiliary devices */ for (i = 0; i < card->num_aux_devs; i++) @@ -1777,6 +2051,7 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card) /* remove and free each DAI */ soc_remove_dai_links(card); + soc_remove_pcm_runtimes(card); soc_cleanup_card_debugfs(card); @@ -1803,29 +2078,26 @@ static int soc_remove(struct platform_device *pdev) int snd_soc_poweroff(struct device *dev) { struct snd_soc_card *card = dev_get_drvdata(dev); - int i; + struct snd_soc_pcm_runtime *rtd; if (!card->instantiated) return 0; /* Flush out pmdown_time work - we actually do want to run it * now, we're shutting down so no imminent restart. */ - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; + list_for_each_entry(rtd, &card->rtd_list, list) flush_delayed_work(&rtd->delayed_work); - } snd_soc_dapm_shutdown(card); /* deactivate pins to sleep state */ - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; + list_for_each_entry(rtd, &card->rtd_list, list) { struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int j; + int i; pinctrl_pm_select_sleep_state(cpu_dai->dev); - for (j = 0; j < rtd->num_codecs; j++) { - struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; pinctrl_pm_select_sleep_state(codec_dai->dev); } } @@ -2301,33 +2573,6 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, } EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); -static int snd_soc_init_multicodec(struct snd_soc_card *card, - struct snd_soc_dai_link *dai_link) -{ - /* Legacy codec/codec_dai link is a single entry in multicodec */ - if (dai_link->codec_name || dai_link->codec_of_node || - dai_link->codec_dai_name) { - dai_link->num_codecs = 1; - - dai_link->codecs = devm_kzalloc(card->dev, - sizeof(struct snd_soc_dai_link_component), - GFP_KERNEL); - if (!dai_link->codecs) - return -ENOMEM; - - dai_link->codecs[0].name = dai_link->codec_name; - dai_link->codecs[0].of_node = dai_link->codec_of_node; - dai_link->codecs[0].dai_name = dai_link->codec_dai_name; - } - - if (!dai_link->codecs) { - dev_err(card->dev, "ASoC: DAI link has no CODECs\n"); - return -EINVAL; - } - - return 0; -} - /** * snd_soc_register_card - Register a card with the ASoC core * @@ -2336,7 +2581,8 @@ static int snd_soc_init_multicodec(struct snd_soc_card *card, */ int snd_soc_register_card(struct snd_soc_card *card) { - int i, j, ret; + int i, ret; + struct snd_soc_pcm_runtime *rtd; if (!card->name || !card->dev) return -EINVAL; @@ -2344,63 +2590,11 @@ int snd_soc_register_card(struct snd_soc_card *card) for (i = 0; i < card->num_links; i++) { struct snd_soc_dai_link *link = &card->dai_link[i]; - ret = snd_soc_init_multicodec(card, link); + ret = soc_init_dai_link(card, link); if (ret) { - dev_err(card->dev, "ASoC: failed to init multicodec\n"); - return ret; - } - - for (j = 0; j < link->num_codecs; j++) { - /* - * Codec must be specified by 1 of name or OF node, - * not both or neither. - */ - if (!!link->codecs[j].name == - !!link->codecs[j].of_node) { - dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n", - link->name); - return -EINVAL; - } - /* Codec DAI name must be specified */ - if (!link->codecs[j].dai_name) { - dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n", - link->name); - return -EINVAL; - } - } - - /* - * Platform may be specified by either name or OF node, but - * can be left unspecified, and a dummy platform will be used. - */ - if (link->platform_name && link->platform_of_node) { - dev_err(card->dev, - "ASoC: Both platform name/of_node are set for %s\n", + dev_err(card->dev, "ASoC: failed to init link %s\n", link->name); - return -EINVAL; - } - - /* - * CPU device may be specified by either name or OF node, but - * can be left unspecified, and will be matched based on DAI - * name alone.. - */ - if (link->cpu_name && link->cpu_of_node) { - dev_err(card->dev, - "ASoC: Neither/both cpu name/of_node are set for %s\n", - link->name); - return -EINVAL; - } - /* - * At least one of CPU DAI name or CPU device name/node must be - * specified - */ - if (!link->cpu_dai_name && - !(link->cpu_name || link->cpu_of_node)) { - dev_err(card->dev, - "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", - link->name); - return -EINVAL; + return ret; } } @@ -2408,25 +2602,18 @@ int snd_soc_register_card(struct snd_soc_card *card) snd_soc_initialize_card_lists(card); - card->rtd = devm_kzalloc(card->dev, + INIT_LIST_HEAD(&card->dai_link_list); + card->num_dai_links = 0; + + INIT_LIST_HEAD(&card->rtd_list); + card->num_rtd = 0; + + card->rtd_aux = devm_kzalloc(card->dev, sizeof(struct snd_soc_pcm_runtime) * - (card->num_links + card->num_aux_devs), + card->num_aux_devs, GFP_KERNEL); - if (card->rtd == NULL) + if (card->rtd_aux == NULL) return -ENOMEM; - card->num_rtd = 0; - card->rtd_aux = &card->rtd[card->num_links]; - - for (i = 0; i < card->num_links; i++) { - card->rtd[i].card = card; - card->rtd[i].dai_link = &card->dai_link[i]; - card->rtd[i].codec_dais = devm_kzalloc(card->dev, - sizeof(struct snd_soc_dai *) * - (card->rtd[i].dai_link->num_codecs), - GFP_KERNEL); - if (card->rtd[i].codec_dais == NULL) - return -ENOMEM; - } for (i = 0; i < card->num_aux_devs; i++) card->rtd_aux[i].card = card; @@ -2442,8 +2629,7 @@ int snd_soc_register_card(struct snd_soc_card *card) return ret; /* deactivate pins to sleep state */ - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; + list_for_each_entry(rtd, &card->rtd_list, list) { struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int j; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 016eba10b1ec..5a2812fa8946 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1300,7 +1300,7 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) static int dapm_always_on_check_power(struct snd_soc_dapm_widget *w) { - return 1; + return w->connected; } static int dapm_seq_compare(struct snd_soc_dapm_widget *a, @@ -2293,6 +2293,12 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) kfree(w); } +void snd_soc_dapm_reset_cache(struct snd_soc_dapm_context *dapm) +{ + dapm->path_sink_cache.widget = NULL; + dapm->path_source_cache.widget = NULL; +} + /* free all dapm widgets and resources */ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) { @@ -2303,6 +2309,7 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) continue; snd_soc_dapm_free_widget(w); } + snd_soc_dapm_reset_cache(dapm); } static struct snd_soc_dapm_widget *dapm_find_widget( @@ -3351,6 +3358,11 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, w->is_ep = SND_SOC_DAPM_EP_SOURCE; w->power_check = dapm_always_on_check_power; break; + case snd_soc_dapm_sink: + w->is_ep = SND_SOC_DAPM_EP_SINK; + w->power_check = dapm_always_on_check_power; + break; + case snd_soc_dapm_mux: case snd_soc_dapm_demux: case snd_soc_dapm_switch: @@ -3893,13 +3905,10 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) { - struct snd_soc_pcm_runtime *rtd = card->rtd; - int i; + struct snd_soc_pcm_runtime *rtd; /* for each BE DAI link... */ - for (i = 0; i < card->num_rtd; i++) { - rtd = &card->rtd[i]; - + list_for_each_entry(rtd, &card->rtd_list, list) { /* * dynamic FE links have no fixed DAI mapping. * CODEC<->CODEC links have no direct connection. diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index ecd38e52285a..a513a34a51d2 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -404,7 +404,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx); /** * snd_soc_put_volsw_sx - double mixer set callback * @kcontrol: mixer control - * @uinfo: control element information + * @ucontrol: control element information * * Callback to set the value of a double mixer control that spans 2 registers. * @@ -779,11 +779,11 @@ int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag, switch (op_flag) { case SNDRV_CTL_TLV_OP_READ: if (params->get) - ret = params->get(tlv, count); + ret = params->get(kcontrol, tlv, count); break; case SNDRV_CTL_TLV_OP_WRITE: if (params->put) - ret = params->put(tlv, count); + ret = params->put(kcontrol, tlv, count); break; } return ret; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index c86dc96e8986..2a2ca2209656 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1213,11 +1213,10 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, struct snd_soc_dapm_widget *widget, int stream) { struct snd_soc_pcm_runtime *be; - int i, j; + int i; if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - for (i = 0; i < card->num_links; i++) { - be = &card->rtd[i]; + list_for_each_entry(be, &card->rtd_list, list) { if (!be->dai_link->no_pcm) continue; @@ -1225,16 +1224,15 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, if (be->cpu_dai->playback_widget == widget) return be; - for (j = 0; j < be->num_codecs; j++) { - struct snd_soc_dai *dai = be->codec_dais[j]; + for (i = 0; i < be->num_codecs; i++) { + struct snd_soc_dai *dai = be->codec_dais[i]; if (dai->playback_widget == widget) return be; } } } else { - for (i = 0; i < card->num_links; i++) { - be = &card->rtd[i]; + list_for_each_entry(be, &card->rtd_list, list) { if (!be->dai_link->no_pcm) continue; @@ -1242,8 +1240,8 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, if (be->cpu_dai->capture_widget == widget) return be; - for (j = 0; j < be->num_codecs; j++) { - struct snd_soc_dai *dai = be->codec_dais[j]; + for (i = 0; i < be->num_codecs; i++) { + struct snd_soc_dai *dai = be->codec_dais[i]; if (dai->capture_widget == widget) return be; } @@ -1616,6 +1614,56 @@ static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe, snd_pcm_stream_unlock_irq(substream); } +static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, + int stream) +{ + struct snd_soc_dpcm *dpcm; + struct snd_soc_pcm_runtime *fe = fe_substream->private_data; + struct snd_soc_dai *fe_cpu_dai = fe->cpu_dai; + int err; + + /* apply symmetry for FE */ + if (soc_pcm_has_symmetry(fe_substream)) + fe_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; + + /* Symmetry only applies if we've got an active stream. */ + if (fe_cpu_dai->active) { + err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai); + if (err < 0) + return err; + } + + /* apply symmetry for BE */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_pcm_substream *be_substream = + snd_soc_dpcm_get_substream(be, stream); + struct snd_soc_pcm_runtime *rtd = be_substream->private_data; + int i; + + if (soc_pcm_has_symmetry(be_substream)) + be_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; + + /* Symmetry only applies if we've got an active stream. */ + if (rtd->cpu_dai->active) { + err = soc_pcm_apply_symmetry(be_substream, rtd->cpu_dai); + if (err < 0) + return err; + } + + for (i = 0; i < rtd->num_codecs; i++) { + if (rtd->codec_dais[i]->active) { + err = soc_pcm_apply_symmetry(be_substream, + rtd->codec_dais[i]); + if (err < 0) + return err; + } + } + } + + return 0; +} + static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) { struct snd_soc_pcm_runtime *fe = fe_substream->private_data; @@ -1644,6 +1692,13 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) dpcm_set_fe_runtime(fe_substream); snd_pcm_limit_hw_rates(runtime); + ret = dpcm_apply_symmetry(fe_substream, stream); + if (ret < 0) { + dev_err(fe->dev, "ASoC: failed to apply dpcm symmetry %d\n", + ret); + goto unwind; + } + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); return 0; @@ -2115,7 +2170,8 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) continue; if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && - (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) continue; dev_dbg(be->dev, "ASoC: prepare BE %s\n", @@ -2343,12 +2399,12 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream) */ int soc_dpcm_runtime_update(struct snd_soc_card *card) { - int i, old, new, paths; + struct snd_soc_pcm_runtime *fe; + int old, new, paths; mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); - for (i = 0; i < card->num_rtd; i++) { + list_for_each_entry(fe, &card->rtd_list, list) { struct snd_soc_dapm_widget_list *list; - struct snd_soc_pcm_runtime *fe = &card->rtd[i]; /* make sure link is FE */ if (!fe->dai_link->dynamic) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 8d7ec80af51b..6963ba20991c 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -531,7 +531,7 @@ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr, /* TLV bytes controls need standard kcontrol info handler, * TLV callback and extended put/get handlers. */ - k->info = snd_soc_bytes_info; + k->info = snd_soc_bytes_info_ext; k->tlv.c = snd_soc_bytes_tlv_callback; ext_ops = tplg->bytes_ext_ops; @@ -1805,6 +1805,7 @@ void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm, snd_soc_tplg_widget_remove(w); snd_soc_dapm_free_widget(w); } + snd_soc_dapm_reset_cache(dapm); } EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove_all); diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c index 843f037a317d..5c2bc53f0a9b 100644 --- a/sound/soc/sti/uniperif_player.c +++ b/sound/soc/sti/uniperif_player.c @@ -669,6 +669,7 @@ static int uni_player_startup(struct snd_pcm_substream *substream, { struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); struct uniperif *player = priv->dai_data.uni; + player->substream = substream; player->clk_adj = 0; @@ -950,6 +951,8 @@ static void uni_player_shutdown(struct snd_pcm_substream *substream, if (player->state != UNIPERIF_STATE_STOPPED) /* Stop the player */ uni_player_stop(player); + + player->substream = NULL; } static int uni_player_parse_dt_clk_glue(struct platform_device *pdev, @@ -989,7 +992,7 @@ static int uni_player_parse_dt(struct platform_device *pdev, if (!info) return -ENOMEM; - if (of_property_read_u32(pnode, "version", &player->ver) || + if (of_property_read_u32(pnode, "st,version", &player->ver) || player->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) { dev_err(dev, "Unknown uniperipheral version "); return -EINVAL; @@ -998,13 +1001,13 @@ static int uni_player_parse_dt(struct platform_device *pdev, if (player->ver >= SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) info->underflow_enabled = 1; - if (of_property_read_u32(pnode, "uniperiph-id", &info->id)) { + if (of_property_read_u32(pnode, "st,uniperiph-id", &info->id)) { dev_err(dev, "uniperipheral id not defined"); return -EINVAL; } /* Read the device mode property */ - if (of_property_read_string(pnode, "mode", &mode)) { + if (of_property_read_string(pnode, "st,mode", &mode)) { dev_err(dev, "uniperipheral mode not defined"); return -EINVAL; } diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c index f791239a3087..8a0eb2050169 100644 --- a/sound/soc/sti/uniperif_reader.c +++ b/sound/soc/sti/uniperif_reader.c @@ -316,7 +316,7 @@ static int uni_reader_parse_dt(struct platform_device *pdev, if (!info) return -ENOMEM; - if (of_property_read_u32(node, "version", &reader->ver) || + if (of_property_read_u32(node, "st,version", &reader->ver) || reader->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) { dev_err(&pdev->dev, "Unknown uniperipheral version "); return -EINVAL; @@ -346,7 +346,6 @@ int uni_reader_init(struct platform_device *pdev, reader->hw = &uni_reader_pcm_hw; reader->dai_ops = &uni_reader_dai_ops; - dev_err(reader->dev, "%s: enter\n", __func__); ret = uni_reader_parse_dt(pdev, reader); if (ret < 0) { dev_err(reader->dev, "Failed to parse DeviceTree"); diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index bcbf4da168b6..44f170c73b06 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -2,6 +2,7 @@ * Copyright 2014 Emilio López <emilio@elopez.com.ar> * Copyright 2014 Jon Smirl <jonsmirl@gmail.com> * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com> + * Copyright 2015 Adam Sampson <ats@offog.org> * * Based on the Allwinner SDK driver, released under the GPL. * @@ -27,6 +28,7 @@ #include <linux/of_address.h> #include <linux/clk.h> #include <linux/regmap.h> +#include <linux/gpio/consumer.h> #include <sound/core.h> #include <sound/pcm.h> @@ -69,6 +71,7 @@ /* Codec ADC register offsets and bit fields */ #define SUN4I_CODEC_ADC_FIFOC (0x1c) +#define SUN4I_CODEC_ADC_FIFOC_ADC_FS (29) #define SUN4I_CODEC_ADC_FIFOC_EN_AD (28) #define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE (24) #define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (8) @@ -101,17 +104,14 @@ struct sun4i_codec { struct regmap *regmap; struct clk *clk_apb; struct clk *clk_module; + struct gpio_desc *gpio_pa; + struct snd_dmaengine_dai_dma_data capture_dma_data; struct snd_dmaengine_dai_dma_data playback_dma_data; }; static void sun4i_codec_start_playback(struct sun4i_codec *scodec) { - /* - * FIXME: according to the BSP, we might need to drive a PA - * GPIO high here on some boards - */ - /* Flush TX FIFO */ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH), @@ -125,37 +125,50 @@ static void sun4i_codec_start_playback(struct sun4i_codec *scodec) static void sun4i_codec_stop_playback(struct sun4i_codec *scodec) { - /* - * FIXME: according to the BSP, we might need to drive a PA - * GPIO low here on some boards - */ - /* Disable DAC DRQ */ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN), 0); } +static void sun4i_codec_start_capture(struct sun4i_codec *scodec) +{ + /* Enable ADC DRQ */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, + BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), + BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN)); +} + +static void sun4i_codec_stop_capture(struct sun4i_codec *scodec) +{ + /* Disable ADC DRQ */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, + BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0); +} + static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); - if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) - return -ENOTSUPP; - switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - sun4i_codec_start_playback(scodec); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + sun4i_codec_start_playback(scodec); + else + sun4i_codec_start_capture(scodec); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - sun4i_codec_stop_playback(scodec); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + sun4i_codec_stop_playback(scodec); + else + sun4i_codec_stop_capture(scodec); break; default: @@ -165,15 +178,54 @@ static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static int sun4i_codec_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); - u32 val; - if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) - return -ENOTSUPP; + + /* Flush RX FIFO */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, + BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH), + BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH)); + + + /* Set RX FIFO trigger level */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, + 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL, + 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL); + + /* + * FIXME: Undocumented in the datasheet, but + * Allwinner's code mentions that it is related + * related to microphone gain + */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL, + 0x3 << 25, + 0x1 << 25); + + if (of_device_is_compatible(scodec->dev->of_node, + "allwinner,sun7i-a20-codec")) + /* FIXME: Undocumented bits */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_TUNE, + 0x3 << 8, + 0x1 << 8); + + /* Fill most significant bits with valid data MSB */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, + BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE), + BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE)); + + return 0; +} + +static int sun4i_codec_prepare_playback(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); + u32 val; /* Flush the TX FIFO */ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, @@ -202,6 +254,15 @@ static int sun4i_codec_prepare(struct snd_pcm_substream *substream, 0); return 0; +}; + +static int sun4i_codec_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return sun4i_codec_prepare_playback(substream, dai); + + return sun4i_codec_prepare_capture(substream, dai); } static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params) @@ -276,30 +337,32 @@ static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params) } } -static int sun4i_codec_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) +static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec, + struct snd_pcm_hw_params *params, + unsigned int hwrate) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); - unsigned long clk_freq; - int ret, hwrate; - u32 val; - - if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) - return -ENOTSUPP; + /* Set ADC sample rate */ + regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, + 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS, + hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS); - clk_freq = sun4i_codec_get_mod_freq(params); - if (!clk_freq) - return -EINVAL; + /* Set the number of channels we want to use */ + if (params_channels(params) == 1) + regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, + BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), + BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN)); + else + regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC, + BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0); - ret = clk_set_rate(scodec->clk_module, clk_freq); - if (ret) - return ret; + return 0; +} - hwrate = sun4i_codec_get_hw_rate(params); - if (hwrate < 0) - return hwrate; +static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec, + struct snd_pcm_hw_params *params, + unsigned int hwrate) +{ + u32 val; /* Set DAC sample rate */ regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC, @@ -344,6 +407,35 @@ static int sun4i_codec_hw_params(struct snd_pcm_substream *substream, return 0; } +static int sun4i_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); + unsigned long clk_freq; + int ret, hwrate; + + clk_freq = sun4i_codec_get_mod_freq(params); + if (!clk_freq) + return -EINVAL; + + ret = clk_set_rate(scodec->clk_module, clk_freq); + if (ret) + return ret; + + hwrate = sun4i_codec_get_hw_rate(params); + if (hwrate < 0) + return hwrate; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return sun4i_codec_hw_params_playback(scodec, params, + hwrate); + + return sun4i_codec_hw_params_capture(scodec, params, + hwrate); +} + static int sun4i_codec_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -394,6 +486,20 @@ static struct snd_soc_dai_driver sun4i_codec_dai = { SNDRV_PCM_FMTBIT_S32_LE, .sig_bits = 24, }, + .capture = { + .stream_name = "Codec Capture", + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 192000, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000 | + SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .sig_bits = 24, + }, }; /*** Codec ***/ @@ -404,7 +510,7 @@ static const struct snd_kcontrol_new sun4i_codec_pa_mute = static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1); static const struct snd_kcontrol_new sun4i_codec_widgets[] = { - SOC_SINGLE_TLV("PA Volume", SUN4I_CODEC_DAC_ACTL, + SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0, sun4i_codec_pa_volume_scale), }; @@ -428,12 +534,23 @@ static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = { SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0), }; -static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = { +static const struct snd_soc_dapm_widget sun4i_codec_codec_dapm_widgets[] = { + /* Digital parts of the ADCs */ + SND_SOC_DAPM_SUPPLY("ADC", SUN4I_CODEC_ADC_FIFOC, + SUN4I_CODEC_ADC_FIFOC_EN_AD, 0, + NULL, 0), + /* Digital parts of the DACs */ SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC, SUN4I_CODEC_DAC_DPC_EN_DA, 0, NULL, 0), + /* Analog parts of the ADCs */ + SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL, + SUN4I_CODEC_ADC_ACTL_ADC_L_EN, 0), + SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL, + SUN4I_CODEC_ADC_ACTL_ADC_R_EN, 0), + /* Analog parts of the DACs */ SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_DACAENL, 0), @@ -452,23 +569,35 @@ static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0), - /* Pre-Amplifier */ - SND_SOC_DAPM_MIXER("Pre-Amplifier", SUN4I_CODEC_ADC_ACTL, + /* VMIC */ + SND_SOC_DAPM_SUPPLY("VMIC", SUN4I_CODEC_ADC_ACTL, + SUN4I_CODEC_ADC_ACTL_VMICEN, 0, NULL, 0), + + /* Mic Pre-Amplifiers */ + SND_SOC_DAPM_PGA("MIC1 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL, + SUN4I_CODEC_ADC_ACTL_PREG1EN, 0, NULL, 0), + + /* Power Amplifier */ + SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL, SUN4I_CODEC_ADC_ACTL_PA_EN, 0, sun4i_codec_pa_mixer_controls, ARRAY_SIZE(sun4i_codec_pa_mixer_controls)), - SND_SOC_DAPM_SWITCH("Pre-Amplifier Mute", SND_SOC_NOPM, 0, 0, + SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0, &sun4i_codec_pa_mute), + SND_SOC_DAPM_INPUT("Mic1"), + SND_SOC_DAPM_OUTPUT("HP Right"), SND_SOC_DAPM_OUTPUT("HP Left"), }; -static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = { - /* Left DAC Routes */ +static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = { + /* Left ADC / DAC Routes */ + { "Left ADC", NULL, "ADC" }, { "Left DAC", NULL, "DAC" }, - /* Right DAC Routes */ + /* Right ADC / DAC Routes */ + { "Right ADC", NULL, "ADC" }, { "Right DAC", NULL, "DAC" }, /* Right Mixer Routes */ @@ -480,25 +609,31 @@ static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = { { "Left Mixer", NULL, "Mixer Enable" }, { "Left Mixer", "Left DAC Playback Switch", "Left DAC" }, - /* Pre-Amplifier Mixer Routes */ - { "Pre-Amplifier", "Mixer Playback Switch", "Left Mixer" }, - { "Pre-Amplifier", "Mixer Playback Switch", "Right Mixer" }, - { "Pre-Amplifier", "DAC Playback Switch", "Left DAC" }, - { "Pre-Amplifier", "DAC Playback Switch", "Right DAC" }, - - /* PA -> HP path */ - { "Pre-Amplifier Mute", "Switch", "Pre-Amplifier" }, - { "HP Right", NULL, "Pre-Amplifier Mute" }, - { "HP Left", NULL, "Pre-Amplifier Mute" }, + /* Power Amplifier Routes */ + { "Power Amplifier", "Mixer Playback Switch", "Left Mixer" }, + { "Power Amplifier", "Mixer Playback Switch", "Right Mixer" }, + { "Power Amplifier", "DAC Playback Switch", "Left DAC" }, + { "Power Amplifier", "DAC Playback Switch", "Right DAC" }, + + /* Headphone Output Routes */ + { "Power Amplifier Mute", "Switch", "Power Amplifier" }, + { "HP Right", NULL, "Power Amplifier Mute" }, + { "HP Left", NULL, "Power Amplifier Mute" }, + + /* Mic1 Routes */ + { "Left ADC", NULL, "MIC1 Pre-Amplifier" }, + { "Right ADC", NULL, "MIC1 Pre-Amplifier" }, + { "MIC1 Pre-Amplifier", NULL, "Mic1"}, + { "Mic1", NULL, "VMIC" }, }; static struct snd_soc_codec_driver sun4i_codec_codec = { .controls = sun4i_codec_widgets, .num_controls = ARRAY_SIZE(sun4i_codec_widgets), - .dapm_widgets = sun4i_codec_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_dapm_widgets), - .dapm_routes = sun4i_codec_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(sun4i_codec_dapm_routes), + .dapm_widgets = sun4i_codec_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets), + .dapm_routes = sun4i_codec_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes), }; static const struct snd_soc_component_driver sun4i_codec_component = { @@ -515,7 +650,7 @@ static int sun4i_codec_dai_probe(struct snd_soc_dai *dai) struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card); snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data, - NULL); + &scodec->capture_dma_data); return 0; } @@ -531,6 +666,14 @@ static struct snd_soc_dai_driver dummy_cpu_dai = { .formats = SUN4I_CODEC_FORMATS, .sig_bits = 24, }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SUN4I_CODEC_RATES, + .formats = SUN4I_CODEC_FORMATS, + .sig_bits = 24, + }, }; static const struct regmap_config sun4i_codec_regmap_config = { @@ -568,6 +711,27 @@ static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev, return link; }; +static int sun4i_codec_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(w->dapm->card); + + if (scodec->gpio_pa) + gpiod_set_value_cansleep(scodec->gpio_pa, + !!SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static const struct snd_soc_dapm_widget sun4i_codec_card_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event), +}; + +static const struct snd_soc_dapm_route sun4i_codec_card_dapm_routes[] = { + { "Speaker", NULL, "HP Right" }, + { "Speaker", NULL, "HP Left" }, +}; + static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) { struct snd_soc_card *card; @@ -582,6 +746,10 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev) card->dev = dev; card->name = "sun4i-codec"; + card->dapm_widgets = sun4i_codec_card_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(sun4i_codec_card_dapm_widgets); + card->dapm_routes = sun4i_codec_card_dapm_routes; + card->num_dapm_routes = ARRAY_SIZE(sun4i_codec_card_dapm_routes); return card; }; @@ -633,11 +801,25 @@ static int sun4i_codec_probe(struct platform_device *pdev) return -EINVAL; } + scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa", + GPIOD_OUT_LOW); + if (IS_ERR(scodec->gpio_pa)) { + ret = PTR_ERR(scodec->gpio_pa); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to get pa gpio: %d\n", ret); + return ret; + } + /* DMA configuration for TX FIFO */ scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA; scodec->playback_dma_data.maxburst = 4; scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + /* DMA configuration for RX FIFO */ + scodec->capture_dma_data.addr = res->start + SUN4I_CODEC_ADC_RXDATA; + scodec->capture_dma_data.maxburst = 4; + scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec, &sun4i_codec_dai, 1); if (ret) { diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index 21604009bc1a..e485278e027a 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -199,7 +199,8 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd) static int tegra_wm8903_remove(struct snd_soc_card *card) { - struct snd_soc_pcm_runtime *rtd = &(card->rtd[0]); + struct snd_soc_pcm_runtime *rtd = + snd_soc_get_pcm_runtime(card, card->dai_link[0].name); struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_codec *codec = codec_dai->codec; struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); |