diff options
Diffstat (limited to 'sound')
53 files changed, 3501 insertions, 889 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index e2828e101433..2ae9619443d1 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -45,6 +45,7 @@ source "sound/soc/nuc900/Kconfig" source "sound/soc/omap/Kconfig" source "sound/soc/kirkwood/Kconfig" source "sound/soc/intel/Kconfig" +source "sound/soc/mediatek/Kconfig" source "sound/soc/mxs/Kconfig" source "sound/soc/pxa/Kconfig" source "sound/soc/qcom/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index a0e1ee6b507d..e189903fabf4 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_SND_SOC) += dwc/ obj-$(CONFIG_SND_SOC) += fsl/ obj-$(CONFIG_SND_SOC) += jz4740/ obj-$(CONFIG_SND_SOC) += intel/ +obj-$(CONFIG_SND_SOC) += mediatek/ obj-$(CONFIG_SND_SOC) += mxs/ obj-$(CONFIG_SND_SOC) += nuc900/ obj-$(CONFIG_SND_SOC) += omap/ diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index c3152072d682..1489cd461aec 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -9,21 +9,32 @@ config SND_ATMEL_SOC if SND_ATMEL_SOC config SND_ATMEL_SOC_PDC - bool + tristate + default m if SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=m + default y if SND_ATMEL_SOC_SSC_PDC=y || (SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=y) + +config SND_ATMEL_SOC_SSC_PDC + tristate config SND_ATMEL_SOC_DMA - bool + tristate select SND_SOC_GENERIC_DMAENGINE_PCM + default m if SND_ATMEL_SOC_SSC_DMA=m && SND_ATMEL_SOC_SSC=m + default y if SND_ATMEL_SOC_SSC_DMA=y || (SND_ATMEL_SOC_SSC_DMA=m && SND_ATMEL_SOC_SSC=y) + +config SND_ATMEL_SOC_SSC_DMA + tristate config SND_ATMEL_SOC_SSC tristate + default y if SND_ATMEL_SOC_SSC_DMA=y || SND_ATMEL_SOC_SSC_PDC=y + default m if SND_ATMEL_SOC_SSC_DMA=m || SND_ATMEL_SOC_SSC_PDC=m config SND_AT91_SOC_SAM9G20_WM8731 tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board" depends on ARCH_AT91 || COMPILE_TEST depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI - select SND_ATMEL_SOC_PDC - select SND_ATMEL_SOC_SSC + select SND_ATMEL_SOC_SSC_PDC select SND_SOC_WM8731 help Say Y if you want to add support for SoC audio on WM8731-based @@ -33,8 +44,7 @@ config SND_ATMEL_SOC_WM8904 tristate "Atmel ASoC driver for boards using WM8904 codec" depends on ARCH_AT91 || COMPILE_TEST depends on ATMEL_SSC && I2C - select SND_ATMEL_SOC_SSC - select SND_ATMEL_SOC_DMA + select SND_ATMEL_SOC_SSC_DMA select SND_SOC_WM8904 help Say Y if you want to add support for Atmel ASoC driver for boards using @@ -44,8 +54,7 @@ config SND_AT91_SOC_SAM9X5_WM8731 tristate "SoC Audio support for WM8731-based at91sam9x5 board" depends on ARCH_AT91 || COMPILE_TEST depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI - select SND_ATMEL_SOC_SSC - select SND_ATMEL_SOC_DMA + select SND_ATMEL_SOC_SSC_DMA select SND_SOC_WM8731 help Say Y if you want to add support for audio SoC on an diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index 4fa7ac91f972..b327e5cc8de3 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -1,8 +1,10 @@ # AT91 Platform Support -snd-soc-atmel-pcm-$(CONFIG_SND_ATMEL_SOC_PDC) := atmel-pcm-pdc.o -snd-soc-atmel-pcm-$(CONFIG_SND_ATMEL_SOC_DMA) += atmel-pcm-dma.o -snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o $(snd-soc-atmel-pcm-y) +snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o +snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o +snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o +obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o +obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o # AT91 Machine Support diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9b36011a814e..efaafce8ba38 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -509,6 +509,11 @@ config SND_SOC_RL6231 default m if SND_SOC_RT5670=m default m if SND_SOC_RT5677=m +config SND_SOC_RL6347A + tristate + default y if SND_SOC_RT286=y + default m if SND_SOC_RT286=m + config SND_SOC_RT286 tristate depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 3dcf5ac85e89..cf160d972cb3 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -77,6 +77,7 @@ snd-soc-pcm512x-objs := pcm512x.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o snd-soc-rl6231-objs := rl6231.o +snd-soc-rl6347a-objs := rl6347a.o snd-soc-rt286-objs := rt286.o snd-soc-rt5631-objs := rt5631.o snd-soc-rt5640-objs := rt5640.o @@ -263,6 +264,7 @@ 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 obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o +obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 679f0a0f7039..78268f0514e9 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -27,7 +27,7 @@ #include "max98090.h" /* Allows for sparsely populated register maps */ -static struct reg_default max98090_reg[] = { +static const struct reg_default max98090_reg[] = { { 0x00, 0x00 }, /* 00 Software Reset */ { 0x03, 0x04 }, /* 03 Interrupt Masks */ { 0x04, 0x00 }, /* 04 System Clock Quick */ @@ -2704,7 +2704,7 @@ static const struct of_device_id max98090_of_match[] = { MODULE_DEVICE_TABLE(of, max98090_of_match); #ifdef CONFIG_ACPI -static struct acpi_device_id max98090_acpi_match[] = { +static const struct acpi_device_id max98090_acpi_match[] = { { "193C9890", MAX98090 }, { } }; diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index 62dda2488f14..b74118e019fb 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -341,6 +341,7 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec); int i = get_coeff(priv->mclk, params_rate(hw_params)); + int srate; if (i < 0) return i; @@ -370,53 +371,16 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream, BIT(0) | BIT(1), 0); } - switch (params_rate(hw_params)) { - case 16000: - snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, - get_srate(params_rate(hw_params))); - snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, - coeff_div[i].pllnl); - snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, - coeff_div[i].pllnh); - snd_soc_update_bits(codec, ML26124_PLLML, 0xff, - coeff_div[i].pllml); - snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, - coeff_div[i].pllmh); - snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, - coeff_div[i].plldiv); - break; - case 32000: - snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, - get_srate(params_rate(hw_params))); - snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, - coeff_div[i].pllnl); - snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, - coeff_div[i].pllnh); - snd_soc_update_bits(codec, ML26124_PLLML, 0xff, - coeff_div[i].pllml); - snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, - coeff_div[i].pllmh); - snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, - coeff_div[i].plldiv); - break; - case 48000: - snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, - get_srate(params_rate(hw_params))); - snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, - coeff_div[i].pllnl); - snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, - coeff_div[i].pllnh); - snd_soc_update_bits(codec, ML26124_PLLML, 0xff, - coeff_div[i].pllml); - snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, - coeff_div[i].pllmh); - snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, - coeff_div[i].plldiv); - break; - default: - pr_err("%s:this rate is no support for ml26124\n", __func__); - return -EINVAL; - } + srate = get_srate(params_rate(hw_params)); + if (srate < 0) + return srate; + + snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, srate); + snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, coeff_div[i].pllnl); + snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, coeff_div[i].pllnh); + snd_soc_update_bits(codec, ML26124_PLLML, 0xff, coeff_div[i].pllml); + snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, coeff_div[i].pllmh); + snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, coeff_div[i].plldiv); return 0; } diff --git a/sound/soc/codecs/rl6347a.c b/sound/soc/codecs/rl6347a.c new file mode 100644 index 000000000000..91d5166bd3a1 --- /dev/null +++ b/sound/soc/codecs/rl6347a.c @@ -0,0 +1,128 @@ +/* + * rl6347a.c - RL6347A class device shared support + * + * Copyright 2015 Realtek Semiconductor Corp. + * + * Author: Oder Chiou <oder_chiou@realtek.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/i2c.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/dmi.h> +#include <linux/acpi.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/jack.h> +#include <linux/workqueue.h> +#include <sound/hda_verbs.h> + +#include "rl6347a.h" + +int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value) +{ + struct i2c_client *client = context; + struct rl6347a_priv *rl6347a = i2c_get_clientdata(client); + u8 data[4]; + int ret, i; + + /* handle index registers */ + if (reg <= 0xff) { + rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg); + for (i = 0; i < rl6347a->index_cache_size; i++) { + if (reg == rl6347a->index_cache[i].reg) { + rl6347a->index_cache[i].def = value; + break; + } + + } + reg = RL6347A_PROC_COEF; + } + + data[0] = (reg >> 24) & 0xff; + data[1] = (reg >> 16) & 0xff; + /* + * 4 bit VID: reg should be 0 + * 12 bit VID: value should be 0 + * So we use an OR operator to handle it rather than use if condition. + */ + data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff); + data[3] = value & 0xff; + + ret = i2c_master_send(client, data, 4); + + if (ret == 4) + return 0; + else + pr_err("ret=%d\n", ret); + if (ret < 0) + return ret; + else + return -EIO; +} +EXPORT_SYMBOL_GPL(rl6347a_hw_write); + +int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value) +{ + struct i2c_client *client = context; + struct i2c_msg xfer[2]; + int ret; + __be32 be_reg; + unsigned int index, vid, buf = 0x0; + + /* handle index registers */ + if (reg <= 0xff) { + rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg); + reg = RL6347A_PROC_COEF; + } + + reg = reg | 0x80000; + vid = (reg >> 8) & 0xfff; + + if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) { + index = (reg >> 8) & 0xf; + reg = (reg & ~0xf0f) | index; + } + be_reg = cpu_to_be32(reg); + + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 4; + xfer[0].buf = (u8 *)&be_reg; + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 4; + xfer[1].buf = (u8 *)&buf; + + ret = i2c_transfer(client->adapter, xfer, 2); + if (ret < 0) + return ret; + else if (ret != 2) + return -EIO; + + *value = be32_to_cpu(buf); + + return 0; +} +EXPORT_SYMBOL_GPL(rl6347a_hw_read); + +MODULE_DESCRIPTION("RL6347A class device shared support"); +MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rl6347a.h b/sound/soc/codecs/rl6347a.h new file mode 100644 index 000000000000..1cb56e50b7f3 --- /dev/null +++ b/sound/soc/codecs/rl6347a.h @@ -0,0 +1,32 @@ +/* + * rl6347a.h - RL6347A class device shared support + * + * Copyright 2015 Realtek Semiconductor Corp. + * + * Author: Oder Chiou <oder_chiou@realtek.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 __RL6347A_H__ +#define __RL6347A_H__ + +#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D) + +#define RL6347A_VENDOR_REGISTERS 0x20 + +#define RL6347A_COEF_INDEX\ + VERB_CMD(AC_VERB_SET_COEF_INDEX, RL6347A_VENDOR_REGISTERS, 0) +#define RL6347A_PROC_COEF\ + VERB_CMD(AC_VERB_SET_PROC_COEF, RL6347A_VENDOR_REGISTERS, 0) + +struct rl6347a_priv { + struct reg_default *index_cache; + int index_cache_size; +}; + +int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value); +int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value); + +#endif /* __RL6347A_H__ */ diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index c6cca0639e0d..5c43e263b2c1 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -31,12 +31,15 @@ #include <sound/rt286.h> #include <sound/hda_verbs.h> +#include "rl6347a.h" #include "rt286.h" #define RT286_VENDOR_ID 0x10ec0286 #define RT288_VENDOR_ID 0x10ec0288 struct rt286_priv { + struct reg_default *index_cache; + int index_cache_size; struct regmap *regmap; struct snd_soc_codec *codec; struct rt286_platform_data pdata; @@ -45,7 +48,6 @@ struct rt286_priv { struct delayed_work jack_detect_work; int sys_clk; int clk_id; - struct reg_default *index_cache; }; static struct reg_default rt286_index_def[] = { @@ -185,94 +187,6 @@ static bool rt286_readable_register(struct device *dev, unsigned int reg) } } -static int rt286_hw_write(void *context, unsigned int reg, unsigned int value) -{ - struct i2c_client *client = context; - struct rt286_priv *rt286 = i2c_get_clientdata(client); - u8 data[4]; - int ret, i; - - /* handle index registers */ - if (reg <= 0xff) { - rt286_hw_write(client, RT286_COEF_INDEX, reg); - for (i = 0; i < INDEX_CACHE_SIZE; i++) { - if (reg == rt286->index_cache[i].reg) { - rt286->index_cache[i].def = value; - break; - } - - } - reg = RT286_PROC_COEF; - } - - data[0] = (reg >> 24) & 0xff; - data[1] = (reg >> 16) & 0xff; - /* - * 4 bit VID: reg should be 0 - * 12 bit VID: value should be 0 - * So we use an OR operator to handle it rather than use if condition. - */ - data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff); - data[3] = value & 0xff; - - ret = i2c_master_send(client, data, 4); - - if (ret == 4) - return 0; - else - pr_err("ret=%d\n", ret); - if (ret < 0) - return ret; - else - return -EIO; -} - -static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value) -{ - struct i2c_client *client = context; - struct i2c_msg xfer[2]; - int ret; - __be32 be_reg; - unsigned int index, vid, buf = 0x0; - - /* handle index registers */ - if (reg <= 0xff) { - rt286_hw_write(client, RT286_COEF_INDEX, reg); - reg = RT286_PROC_COEF; - } - - reg = reg | 0x80000; - vid = (reg >> 8) & 0xfff; - - if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) { - index = (reg >> 8) & 0xf; - reg = (reg & ~0xf0f) | index; - } - be_reg = cpu_to_be32(reg); - - /* Write register */ - xfer[0].addr = client->addr; - xfer[0].flags = 0; - xfer[0].len = 4; - xfer[0].buf = (u8 *)&be_reg; - - /* Read data */ - xfer[1].addr = client->addr; - xfer[1].flags = I2C_M_RD; - xfer[1].len = 4; - xfer[1].buf = (u8 *)&buf; - - ret = i2c_transfer(client->adapter, xfer, 2); - if (ret < 0) - return ret; - else if (ret != 2) - return -EIO; - - *value = be32_to_cpu(buf); - - return 0; -} - #ifdef CONFIG_PM static void rt286_index_sync(struct snd_soc_codec *codec) { @@ -1174,8 +1088,8 @@ static const struct regmap_config rt286_regmap = { .max_register = 0x02370100, .volatile_reg = rt286_volatile_register, .readable_reg = rt286_readable_register, - .reg_write = rt286_hw_write, - .reg_read = rt286_hw_read, + .reg_write = rl6347a_hw_write, + .reg_read = rl6347a_hw_read, .cache_type = REGCACHE_RBTREE, .reg_defaults = rt286_reg, .num_reg_defaults = ARRAY_SIZE(rt286_reg), @@ -1248,6 +1162,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c, } rt286->index_cache = rt286_index_def; + rt286->index_cache_size = INDEX_CACHE_SIZE; rt286->i2c = i2c; i2c_set_clientdata(i2c, rt286); diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index f40752a6c242..9bc78e57513d 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -51,7 +51,7 @@ static const struct regmap_range_cfg rt5640_ranges[] = { .window_len = 0x1, }, }; -static struct reg_default init_list[] = { +static const struct reg_default init_list[] = { {RT5640_PR_BASE + 0x3d, 0x3600}, {RT5640_PR_BASE + 0x12, 0x0aa8}, {RT5640_PR_BASE + 0x14, 0x0aaa}, @@ -59,7 +59,6 @@ static struct reg_default init_list[] = { {RT5640_PR_BASE + 0x21, 0xe0e0}, {RT5640_PR_BASE + 0x23, 0x1804}, }; -#define RT5640_INIT_REG_LEN ARRAY_SIZE(init_list) static const struct reg_default rt5640_reg[] = { { 0x00, 0x000e }, @@ -2122,7 +2121,7 @@ MODULE_DEVICE_TABLE(of, rt5640_of_match); #endif #ifdef CONFIG_ACPI -static struct acpi_device_id rt5640_acpi_match[] = { +static const struct acpi_device_id rt5640_acpi_match[] = { { "INT33CA", 0 }, { "10EC5640", 0 }, { "10EC5642", 0 }, diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index d5f0f5680d3b..9ce311e088fc 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -349,6 +349,7 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg) case RT5645_TDM_CTRL_1: case RT5645_TDM_CTRL_2: case RT5645_TDM_CTRL_3: + case RT5650_TDM_CTRL_4: case RT5645_GLB_CLK: case RT5645_PLL_CTRL1: case RT5645_PLL_CTRL2: @@ -1705,15 +1706,6 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { SND_SOC_DAPM_MUX("RT5645 IF1 ADC Mux", SND_SOC_NOPM, 0, 0, &rt5645_if1_adc_in_mux), - SND_SOC_DAPM_MUX("RT5650 IF1 ADC1 Swap Mux", SND_SOC_NOPM, - 0, 0, &rt5650_if1_adc1_in_mux), - SND_SOC_DAPM_MUX("RT5650 IF1 ADC2 Swap Mux", SND_SOC_NOPM, - 0, 0, &rt5650_if1_adc2_in_mux), - SND_SOC_DAPM_MUX("RT5650 IF1 ADC3 Swap Mux", SND_SOC_NOPM, - 0, 0, &rt5650_if1_adc3_in_mux), - SND_SOC_DAPM_MUX("RT5650 IF1 ADC Mux", SND_SOC_NOPM, - 0, 0, &rt5650_if1_adc_in_mux), - SND_SOC_DAPM_MUX("IF2 ADC Mux", SND_SOC_NOPM, 0, 0, &rt5645_if2_adc_in_mux), @@ -1732,14 +1724,6 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { &rt5645_if1_dac2_tdm_sel_mux), SND_SOC_DAPM_MUX("RT5645 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0, &rt5645_if1_dac3_tdm_sel_mux), - SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0, - &rt5650_if1_dac0_tdm_sel_mux), - SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0, - &rt5650_if1_dac1_tdm_sel_mux), - SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0, - &rt5650_if1_dac2_tdm_sel_mux), - SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0, - &rt5650_if1_dac3_tdm_sel_mux), SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -1881,6 +1865,24 @@ static const struct snd_soc_dapm_widget rt5650_specific_dapm_widgets[] = { 0, 0, &rt5650_a_dac2_l_mux), SND_SOC_DAPM_MUX("A DAC2 R Mux", SND_SOC_NOPM, 0, 0, &rt5650_a_dac2_r_mux), + + SND_SOC_DAPM_MUX("RT5650 IF1 ADC1 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5650_if1_adc1_in_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 ADC2 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5650_if1_adc2_in_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 ADC3 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5650_if1_adc3_in_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 ADC Mux", SND_SOC_NOPM, + 0, 0, &rt5650_if1_adc_in_mux), + + SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0, + &rt5650_if1_dac0_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0, + &rt5650_if1_dac1_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0, + &rt5650_if1_dac2_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0, + &rt5650_if1_dac3_tdm_sel_mux), }; static const struct snd_soc_dapm_route rt5645_dapm_routes[] = { @@ -2761,6 +2763,7 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec, struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); if (enable) { + snd_soc_dapm_mutex_lock(&codec->dapm); snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm, "ADC L power"); snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm, @@ -2770,6 +2773,8 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec, snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm, "Mic Det Power"); snd_soc_dapm_sync_unlocked(&codec->dapm); + snd_soc_dapm_mutex_unlock(&codec->dapm); + snd_soc_update_bits(codec, RT5645_INT_IRQ_ST, 0x8, 0x8); snd_soc_update_bits(codec, @@ -2780,6 +2785,8 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec, } else { snd_soc_update_bits(codec, RT5650_4BTN_IL_CMD2, 0x8000, 0x0); snd_soc_update_bits(codec, RT5645_INT_IRQ_ST, 0x8, 0x0); + + snd_soc_dapm_mutex_lock(&codec->dapm); snd_soc_dapm_disable_pin_unlocked(&codec->dapm, "ADC L power"); snd_soc_dapm_disable_pin_unlocked(&codec->dapm, @@ -2790,6 +2797,7 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec, snd_soc_dapm_disable_pin_unlocked(&codec->dapm, "Mic Det Power"); snd_soc_dapm_sync_unlocked(&codec->dapm); + snd_soc_dapm_mutex_unlock(&codec->dapm); } } @@ -2937,17 +2945,11 @@ static int rt5645_irq_detection(struct rt5645_priv *rt5645) switch (rt5645->pdata.jd_mode) { case 0: /* Not using rt5645 JD */ - if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) { - gpio_state = gpio_get_value(rt5645->pdata.hp_det_gpio); - dev_dbg(rt5645->codec->dev, "gpio = %d(%d)\n", - rt5645->pdata.hp_det_gpio, gpio_state); - } - if ((rt5645->pdata.gpio_hp_det_active_high && gpio_state) || - (!rt5645->pdata.gpio_hp_det_active_high && - !gpio_state)) { - report = rt5645_jack_detect(rt5645->codec, 1); - } else { - report = rt5645_jack_detect(rt5645->codec, 0); + if (rt5645->gpiod_hp_det) { + gpio_state = gpiod_get_value(rt5645->gpiod_hp_det); + dev_dbg(rt5645->codec->dev, "gpio_state = %d\n", + gpio_state); + report = rt5645_jack_detect(rt5645->codec, gpio_state); } snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE); @@ -3230,6 +3232,20 @@ static struct dmi_system_id dmi_platform_intel_braswell[] = { { } }; +static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev) +{ + rt5645->pdata.in2_diff = device_property_read_bool(dev, + "realtek,in2-differential"); + device_property_read_u32(dev, + "realtek,dmic1-data-pin", &rt5645->pdata.dmic1_data_pin); + device_property_read_u32(dev, + "realtek,dmic2-data-pin", &rt5645->pdata.dmic2_data_pin); + device_property_read_u32(dev, + "realtek,jd-mode", &rt5645->pdata.jd_mode); + + return 0; +} + static int rt5645_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -3237,7 +3253,6 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, struct rt5645_priv *rt5645; int ret; unsigned int val; - struct gpio_desc *gpiod; rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv), GFP_KERNEL); @@ -3247,22 +3262,19 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, rt5645->i2c = i2c; i2c_set_clientdata(i2c, rt5645); - if (pdata) { + if (pdata) rt5645->pdata = *pdata; - } else { - if (dmi_check_system(dmi_platform_intel_braswell)) { - rt5645->pdata = *rt5645_pdata; - gpiod = devm_gpiod_get_index(&i2c->dev, "rt5645", 0); + else if (dmi_check_system(dmi_platform_intel_braswell)) + rt5645->pdata = *rt5645_pdata; + else + rt5645_parse_dt(rt5645, &i2c->dev); - if (IS_ERR(gpiod) || gpiod_direction_input(gpiod)) { - rt5645->pdata.hp_det_gpio = -1; - dev_err(&i2c->dev, "failed to initialize gpiod\n"); - } else { - rt5645->pdata.hp_det_gpio = desc_to_gpio(gpiod); - rt5645->pdata.gpio_hp_det_active_high - = !gpiod_is_active_low(gpiod); - } - } + rt5645->gpiod_hp_det = devm_gpiod_get_optional(&i2c->dev, "hp-detect", + GPIOD_IN); + + if (IS_ERR(rt5645->gpiod_hp_det)) { + dev_err(&i2c->dev, "failed to initialize gpiod\n"); + return PTR_ERR(rt5645->gpiod_hp_det); } rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap); @@ -3426,16 +3438,6 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); } - if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) { - ret = gpio_request(rt5645->pdata.hp_det_gpio, "rt5645"); - if (ret) - dev_err(&i2c->dev, "Fail gpio_request hp_det_gpio\n"); - - ret = gpio_direction_input(rt5645->pdata.hp_det_gpio); - if (ret) - dev_err(&i2c->dev, "Fail gpio_direction hp_det_gpio\n"); - } - return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645, rt5645_dai, ARRAY_SIZE(rt5645_dai)); } @@ -3449,9 +3451,6 @@ static int rt5645_i2c_remove(struct i2c_client *i2c) cancel_delayed_work_sync(&rt5645->jack_detect_work); - if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) - gpio_free(rt5645->pdata.hp_det_gpio); - snd_soc_unregister_codec(&i2c->dev); return 0; diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index 9ec4e899795d..0353a6a273ab 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -2182,6 +2182,7 @@ struct rt5645_priv { struct rt5645_platform_data pdata; struct regmap *regmap; struct i2c_client *i2c; + struct gpio_desc *gpiod_hp_det; struct snd_soc_jack *hp_jack; struct snd_soc_jack *mic_jack; struct snd_soc_jack *btn_jack; diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 840dd6d0003a..a9123d414178 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -51,12 +51,11 @@ static const struct regmap_range_cfg rt5670_ranges[] = { .window_len = 0x1, }, }; -static struct reg_default init_list[] = { +static const struct reg_default init_list[] = { { RT5670_PR_BASE + 0x14, 0x9a8a }, { RT5670_PR_BASE + 0x38, 0x3ba1 }, { RT5670_PR_BASE + 0x3d, 0x3640 }, }; -#define RT5670_INIT_REG_LEN ARRAY_SIZE(init_list) static const struct reg_default rt5670_reg[] = { { 0x00, 0x0000 }, @@ -2809,7 +2808,7 @@ static const struct i2c_device_id rt5670_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id); #ifdef CONFIG_ACPI -static struct acpi_device_id rt5670_acpi_match[] = { +static const struct acpi_device_id rt5670_acpi_match[] = { { "10EC5670", 0}, { }, }; diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 891e2c529df3..4f25a7d0efa2 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -45,7 +45,7 @@ static struct reg_default tas2552_reg_defs[] = { {TAS2552_OUTPUT_DATA, 0xc0}, {TAS2552_PDM_CFG, 0x01}, {TAS2552_PGA_GAIN, 0x00}, - {TAS2552_BOOST_PT_CTRL, 0x0f}, + {TAS2552_BOOST_APT_CTRL, 0x0f}, {TAS2552_RESERVED_0D, 0xbe}, {TAS2552_LIMIT_RATE_HYS, 0x08}, {TAS2552_CFG_2, 0xef}, @@ -77,7 +77,9 @@ struct tas2552_data { struct gpio_desc *enable_gpio; unsigned char regs[TAS2552_VBAT_DATA]; unsigned int pll_clkin; + int pll_clk_id; unsigned int pdm_clk; + int pdm_clk_id; unsigned int dai_fmt; unsigned int tdm_delay; @@ -143,31 +145,105 @@ static const struct snd_soc_dapm_route tas2552_audio_map[] = { }; #ifdef CONFIG_PM -static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown) +static void tas2552_sw_shutdown(struct tas2552_data *tas2552, int sw_shutdown) { u8 cfg1_reg = 0; - if (!tas_data->codec) + if (!tas2552->codec) return; if (sw_shutdown) cfg1_reg = TAS2552_SWS; - snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1, TAS2552_SWS, + snd_soc_update_bits(tas2552->codec, TAS2552_CFG_1, TAS2552_SWS, cfg1_reg); } #endif +static int tas2552_setup_pll(struct snd_soc_codec *codec, + struct snd_pcm_hw_params *params) +{ + struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); + bool bypass_pll = false; + unsigned int pll_clk = params_rate(params) * 512; + unsigned int pll_clkin = tas2552->pll_clkin; + u8 pll_enable; + + if (!pll_clkin) { + if (tas2552->pll_clk_id != TAS2552_PLL_CLKIN_BCLK) + return -EINVAL; + + pll_clkin = snd_soc_params_to_bclk(params); + pll_clkin += tas2552->tdm_delay; + } + + pll_enable = snd_soc_read(codec, TAS2552_CFG_2) & TAS2552_PLL_ENABLE; + snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0); + + if (pll_clkin == pll_clk) + bypass_pll = true; + + if (bypass_pll) { + /* By pass the PLL configuration */ + snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2, + TAS2552_PLL_BYPASS, TAS2552_PLL_BYPASS); + } else { + /* Fill in the PLL control registers for J & D + * pll_clk = (.5 * pll_clkin * J.D) / 2^p + * Need to fill in J and D here based on incoming freq + */ + unsigned int d; + u8 j; + u8 pll_sel = (tas2552->pll_clk_id << 3) & TAS2552_PLL_SRC_MASK; + u8 p = snd_soc_read(codec, TAS2552_PLL_CTRL_1); + + p = (p >> 7); + +recalc: + j = (pll_clk * 2 * (1 << p)) / pll_clkin; + d = (pll_clk * 2 * (1 << p)) % pll_clkin; + d /= (pll_clkin / 10000); + + if (d && (pll_clkin < 512000 || pll_clkin > 9200000)) { + if (tas2552->pll_clk_id == TAS2552_PLL_CLKIN_BCLK) { + pll_clkin = 1800000; + pll_sel = (TAS2552_PLL_CLKIN_1_8_FIXED << 3) & + TAS2552_PLL_SRC_MASK; + } else { + pll_clkin = snd_soc_params_to_bclk(params); + pll_clkin += tas2552->tdm_delay; + pll_sel = (TAS2552_PLL_CLKIN_BCLK << 3) & + TAS2552_PLL_SRC_MASK; + } + goto recalc; + } + + snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK, + pll_sel); + + snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1, + TAS2552_PLL_J_MASK, j); + /* Will clear the PLL_BYPASS bit */ + snd_soc_write(codec, TAS2552_PLL_CTRL_2, + TAS2552_PLL_D_UPPER(d)); + snd_soc_write(codec, TAS2552_PLL_CTRL_3, + TAS2552_PLL_D_LOWER(d)); + } + + /* Restore PLL status */ + snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, + pll_enable); + + return 0; +} + static int tas2552_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 tas2552_data *tas2552 = dev_get_drvdata(codec->dev); - int sample_rate, pll_clk; - int d; int cpf; - u8 p, j; u8 ser_ctrl1_reg, wclk_rate; switch (params_width(params)) { @@ -245,49 +321,7 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream, snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK, wclk_rate); - if (!tas2552->pll_clkin) - return -EINVAL; - - snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0); - - if (tas2552->pll_clkin == TAS2552_245MHZ_CLK || - tas2552->pll_clkin == TAS2552_225MHZ_CLK) { - /* By pass the PLL configuration */ - snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2, - TAS2552_PLL_BYPASS_MASK, - TAS2552_PLL_BYPASS); - } else { - /* Fill in the PLL control registers for J & D - * PLL_CLK = (.5 * freq * J.D) / 2^p - * Need to fill in J and D here based on incoming freq - */ - p = snd_soc_read(codec, TAS2552_PLL_CTRL_1); - p = (p >> 7); - sample_rate = params_rate(params); - - if (sample_rate == 48000) - pll_clk = TAS2552_245MHZ_CLK; - else if (sample_rate == 44100) - pll_clk = TAS2552_225MHZ_CLK; - else { - dev_vdbg(codec->dev, "Substream sample rate is not found %i\n", - params_rate(params)); - return -EINVAL; - } - - j = (pll_clk * 2 * (1 << p)) / tas2552->pll_clkin; - d = (pll_clk * 2 * (1 << p)) % tas2552->pll_clkin; - - snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1, - TAS2552_PLL_J_MASK, j); - snd_soc_write(codec, TAS2552_PLL_CTRL_2, - (d >> 7) & TAS2552_PLL_D_UPPER_MASK); - snd_soc_write(codec, TAS2552_PLL_CTRL_3, - d & TAS2552_PLL_D_LOWER_MASK); - - } - - return 0; + return tas2552_setup_pll(codec, params); } #define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \ @@ -370,12 +404,21 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, switch (clk_id) { case TAS2552_PLL_CLKIN_MCLK: - case TAS2552_PLL_CLKIN_BCLK: case TAS2552_PLL_CLKIN_IVCLKIN: + if (freq < 512000 || freq > 24576000) { + /* out of range PLL_CLKIN, fall back to use BCLK */ + dev_warn(codec->dev, "Out of range PLL_CLKIN: %u\n", + freq); + clk_id = TAS2552_PLL_CLKIN_BCLK; + freq = 0; + } + /* fall through */ + case TAS2552_PLL_CLKIN_BCLK: case TAS2552_PLL_CLKIN_1_8_FIXED: mask = TAS2552_PLL_SRC_MASK; val = (clk_id << 3) & mask; /* bit 4:5 in the register */ reg = TAS2552_CFG_1; + tas2552->pll_clk_id = clk_id; tas2552->pll_clkin = freq; break; case TAS2552_PDM_CLK_PLL: @@ -385,6 +428,7 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, mask = TAS2552_PDM_CLK_SEL_MASK; val = (clk_id >> 1) & mask; /* bit 0:1 in the register */ reg = TAS2552_PDM_CFG; + tas2552->pdm_clk_id = clk_id; tas2552->pdm_clk = freq; break; default: @@ -509,9 +553,20 @@ static struct snd_soc_dai_driver tas2552_dai[] = { */ static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 0); +static const char * const tas2552_din_source_select[] = { + "Muted", + "Left", + "Right", + "Left + Right average", +}; +static SOC_ENUM_SINGLE_DECL(tas2552_din_source_enum, + TAS2552_CFG_3, 3, + tas2552_din_source_select); + static const struct snd_kcontrol_new tas2552_snd_controls[] = { SOC_SINGLE_TLV("Speaker Driver Playback Volume", TAS2552_PGA_GAIN, 0, 0x1f, 0, dac_tlv), + SOC_ENUM("DIN source", tas2552_din_source_enum), }; static int tas2552_codec_probe(struct snd_soc_codec *codec) @@ -543,13 +598,14 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE); snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL | TAS2552_DIN_SRC_SEL_AVG_L_R); - snd_soc_write(codec, TAS2552_DOUT, TAS2552_PDM_DATA_I); - snd_soc_write(codec, TAS2552_OUTPUT_DATA, TAS2552_PDM_DATA_V_I | 0x8); - snd_soc_write(codec, TAS2552_BOOST_PT_CTRL, TAS2552_APT_DELAY_200 | - TAS2552_APT_THRESH_2_1_7); + snd_soc_write(codec, TAS2552_OUTPUT_DATA, + TAS2552_PDM_DATA_SEL_V_I | + TAS2552_R_DATA_OUT(TAS2552_DATA_OUT_V_DATA)); + snd_soc_write(codec, TAS2552_BOOST_APT_CTRL, TAS2552_APT_DELAY_200 | + TAS2552_APT_THRESH_20_17); - snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN | - TAS2552_APT_EN | TAS2552_LIM_EN); + snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN | + TAS2552_LIM_EN); return 0; @@ -647,13 +703,10 @@ static int tas2552_probe(struct i2c_client *client, if (data == NULL) return -ENOMEM; - data->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(data->enable_gpio)) { - if (PTR_ERR(data->enable_gpio) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - data->enable_gpio = NULL;; - } + data->enable_gpio = devm_gpiod_get_optional(dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(data->enable_gpio)) + return PTR_ERR(data->enable_gpio); data->tas2552_client = client; data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config); @@ -695,6 +748,7 @@ static int tas2552_probe(struct i2c_client *client, static int tas2552_i2c_remove(struct i2c_client *client) { snd_soc_unregister_codec(&client->dev); + pm_runtime_disable(&client->dev); return 0; } diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h index bbb820495516..5746f8fd0afd 100644 --- a/sound/soc/codecs/tas2552.h +++ b/sound/soc/codecs/tas2552.h @@ -19,7 +19,7 @@ #define __TAS2552_H__ /* Register Address Map */ -#define TAS2552_DEVICE_STATUS 0x00 +#define TAS2552_DEVICE_STATUS 0x00 #define TAS2552_CFG_1 0x01 #define TAS2552_CFG_2 0x02 #define TAS2552_CFG_3 0x03 @@ -33,13 +33,13 @@ #define TAS2552_BTIP 0x0b #define TAS2552_BTS_CTRL 0x0c #define TAS2552_RESERVED_0D 0x0d -#define TAS2552_LIMIT_RATE_HYS 0x0e -#define TAS2552_LIMIT_RELEASE 0x0f -#define TAS2552_LIMIT_INT_COUNT 0x10 +#define TAS2552_LIMIT_RATE_HYS 0x0e +#define TAS2552_LIMIT_RELEASE 0x0f +#define TAS2552_LIMIT_INT_COUNT 0x10 #define TAS2552_PDM_CFG 0x11 #define TAS2552_PGA_GAIN 0x12 -#define TAS2552_EDGE_RATE_CTRL 0x13 -#define TAS2552_BOOST_PT_CTRL 0x14 +#define TAS2552_EDGE_RATE_CTRL 0x13 +#define TAS2552_BOOST_APT_CTRL 0x14 #define TAS2552_VER_NUM 0x16 #define TAS2552_VBAT_DATA 0x19 #define TAS2552_MAX_REG 0x20 @@ -103,10 +103,21 @@ #define TAS2552_WCLKDIR (1 << 7) /* OUTPUT_DATA register */ -#define TAS2552_PDM_DATA_I 0x00 -#define TAS2552_PDM_DATA_V (1 << 6) -#define TAS2552_PDM_DATA_I_V (1 << 7) -#define TAS2552_PDM_DATA_V_I (0x11 << 6) +#define TAS2552_DATA_OUT_I_DATA (0x0) +#define TAS2552_DATA_OUT_V_DATA (0x1) +#define TAS2552_DATA_OUT_VBAT_DATA (0x2) +#define TAS2552_DATA_OUT_VBOOST_DATA (0x3) +#define TAS2552_DATA_OUT_PGA_GAIN (0x4) +#define TAS2552_DATA_OUT_IV_DATA (0x5) +#define TAS2552_DATA_OUT_VBAT_VBOOST_GAIN (0x6) +#define TAS2552_DATA_OUT_DISABLED (0x7) +#define TAS2552_L_DATA_OUT(x) ((x) << 0) +#define TAS2552_R_DATA_OUT(x) ((x) << 3) +#define TAS2552_PDM_DATA_SEL_I (0x0 << 6) +#define TAS2552_PDM_DATA_SEL_V (0x1 << 6) +#define TAS2552_PDM_DATA_SEL_I_V (0x2 << 6) +#define TAS2552_PDM_DATA_SEL_V_I (0x3 << 6) +#define TAS2552_PDM_DATA_SEL_MASK TAS2552_PDM_DATA_SEL_V_I /* PDM CFG Register */ #define TAS2552_PDM_CLK_SEL_PLL (0x0 << 0) @@ -116,24 +127,20 @@ #define TAS2552_PDM_CLK_SEL_MASK TAS2552_PDM_CLK_SEL_MCLK #define TAS2552_PDM_DATA_ES (1 << 2) -/* Boost pass-through register */ -#define TAS2552_APT_DELAY_50 0x00 -#define TAS2552_APT_DELAY_75 (1 << 1) -#define TAS2552_APT_DELAY_125 (1 << 2) -#define TAS2552_APT_DELAY_200 (1 << 3) - -#define TAS2552_APT_THRESH_2_5 0x00 -#define TAS2552_APT_THRESH_1_7 (1 << 3) -#define TAS2552_APT_THRESH_1_4_1_1 (1 << 4) -#define TAS2552_APT_THRESH_2_1_7 (0x11 << 2) +/* Boost Auto-pass through register */ +#define TAS2552_APT_DELAY_50 (0x0 << 0) +#define TAS2552_APT_DELAY_75 (0x1 << 0) +#define TAS2552_APT_DELAY_125 (0x2 << 0) +#define TAS2552_APT_DELAY_200 (0x3 << 0) +#define TAS2552_APT_THRESH_05_02 (0x0 << 2) +#define TAS2552_APT_THRESH_10_07 (0x1 << 2) +#define TAS2552_APT_THRESH_14_11 (0x2 << 2) +#define TAS2552_APT_THRESH_20_17 (0x3 << 2) /* PLL Control Register */ -#define TAS2552_245MHZ_CLK 24576000 -#define TAS2552_225MHZ_CLK 22579200 -#define TAS2552_PLL_J_MASK 0x7f -#define TAS2552_PLL_D_UPPER_MASK 0x3f -#define TAS2552_PLL_D_LOWER_MASK 0xff -#define TAS2552_PLL_BYPASS_MASK 0x80 -#define TAS2552_PLL_BYPASS 0x80 +#define TAS2552_PLL_J_MASK 0x7f +#define TAS2552_PLL_D_UPPER(x) (((x) >> 8) & 0x3f) +#define TAS2552_PLL_D_LOWER(x) ((x) & 0xff) +#define TAS2552_PLL_BYPASS (1 << 7) #endif diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index d8959e31853d..c5ec519d34be 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1876,8 +1876,8 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec); int ret; - ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 2); - if (ret != 0) + ret = wm_adsp2_codec_probe(&priv->core.adsp[0], codec); + if (ret) return ret; arizona_init_spk(codec); @@ -1894,6 +1894,8 @@ static int wm5102_codec_remove(struct snd_soc_codec *codec) { struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec); + wm_adsp2_codec_remove(&priv->core.adsp[0], codec); + priv->core.arizona->dapm = NULL; return 0; diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 14a7739d6c09..5f032a37b61f 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -1600,7 +1600,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec); - int ret; + int i, ret; priv->core.arizona->dapm = dapm; @@ -1608,9 +1608,11 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) arizona_init_gpio(codec); arizona_init_mono(codec); - ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 8); - if (ret != 0) - return ret; + for (i = 0; i < WM5110_NUM_ADSP; ++i) { + ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec); + if (ret) + return ret; + } snd_soc_dapm_disable_pin(dapm, "HAPTICS"); @@ -1620,6 +1622,10 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) static int wm5110_codec_remove(struct snd_soc_codec *codec) { struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec); + int i; + + for (i = 0; i < WM5110_NUM_ADSP; ++i) + wm_adsp2_codec_remove(&priv->core.adsp[i], codec); priv->core.arizona->dapm = NULL; diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 8c5b9df3e542..43ea8ae5f871 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -113,6 +113,15 @@ static struct { { 7, 1152 }, }; +static struct { + int value; + int ratio; +} bclk_ratios[WM8523_NUM_RATES] = { + { 2, 32 }, + { 3, 64 }, + { 4, 128 }, +}; + static int wm8523_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -162,6 +171,23 @@ static int wm8523_hw_params(struct snd_pcm_substream *substream, aifctrl2 &= ~WM8523_SR_MASK; aifctrl2 |= lrclk_ratios[i].value; + if (aifctrl1 & WM8523_AIF_MSTR) { + /* Find a fs->bclk ratio */ + for (i = 0; i < ARRAY_SIZE(bclk_ratios); i++) + if (params_width(params) * 2 <= bclk_ratios[i].ratio) + break; + + if (i == ARRAY_SIZE(bclk_ratios)) { + dev_err(codec->dev, + "No matching BCLK/fs ratio for word length %d\n", + params_width(params)); + return -EINVAL; + } + + aifctrl2 &= ~WM8523_BCLKDIV_MASK; + aifctrl2 |= bclk_ratios[i].value << WM8523_BCLKDIV_SHIFT; + } + aifctrl1 &= ~WM8523_WL_MASK; switch (params_width(params)) { case 16: diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 09ff01f2fc1e..b34623786e35 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -125,18 +125,6 @@ static const struct snd_soc_dapm_route wm8741_dapm_routes[] = { { "VOUTRN", NULL, "DACR" }, }; -static struct { - int value; - int ratio; -} lrclk_ratios[WM8741_NUM_RATES] = { - { 1, 128 }, - { 2, 192 }, - { 3, 256 }, - { 4, 384 }, - { 5, 512 }, - { 6, 768 }, -}; - static const unsigned int rates_11289[] = { 44100, 88200, }; @@ -209,25 +197,16 @@ static const struct snd_pcm_hw_constraint_list constraints_36864 = { .list = rates_36864, }; - static int wm8741_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); - /* The set of sample rates that can be supported depends on the - * MCLK supplied to the CODEC - enforce this. - */ - if (!wm8741->sysclk) { - dev_err(codec->dev, - "No MCLK configured, call set_sysclk() on init\n"); - return -EINVAL; - } - - snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - wm8741->sysclk_constraints); + if (wm8741->sysclk) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + wm8741->sysclk_constraints); return 0; } @@ -241,17 +220,24 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream, u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1FC; int i; - /* Find a supported LRCLK ratio */ - for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) { - if (wm8741->sysclk / params_rate(params) == - lrclk_ratios[i].ratio) + /* The set of sample rates that can be supported depends on the + * MCLK supplied to the CODEC - enforce this. + */ + if (!wm8741->sysclk) { + dev_err(codec->dev, + "No MCLK configured, call set_sysclk() on init or in hw_params\n"); + return -EINVAL; + } + + /* Find a supported LRCLK rate */ + for (i = 0; i < wm8741->sysclk_constraints->count; i++) { + if (wm8741->sysclk_constraints->list[i] == params_rate(params)) break; } - /* Should never happen, should be handled by constraints */ - if (i == ARRAY_SIZE(lrclk_ratios)) { - dev_err(codec->dev, "MCLK/fs ratio %d unsupported\n", - wm8741->sysclk / params_rate(params)); + if (i == wm8741->sysclk_constraints->count) { + dev_err(codec->dev, "LRCLK %d unsupported with MCLK %d\n", + params_rate(params), wm8741->sysclk); return -EINVAL; } @@ -274,8 +260,8 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - dev_dbg(codec->dev, "wm8741_hw_params: bit size param = %d", - params_width(params)); + dev_dbg(codec->dev, "wm8741_hw_params: bit size param = %d, rate param = %d", + params_width(params), params_rate(params)); snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface); return 0; @@ -290,6 +276,11 @@ static int wm8741_set_dai_sysclk(struct snd_soc_dai *codec_dai, dev_dbg(codec->dev, "wm8741_set_dai_sysclk info: freq=%dHz\n", freq); switch (freq) { + case 0: + wm8741->sysclk_constraints = NULL; + wm8741->sysclk = freq; + return 0; + case 11289600: wm8741->sysclk_constraints = &constraints_11289; wm8741->sysclk = freq; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 761418f05d59..94c5c4681ce5 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -247,7 +247,7 @@ SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0), SOC_ENUM("ADC Polarity", wm8960_enum[0]), SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0), -SOC_ENUM("DAC Polarity", wm8960_enum[2]), +SOC_ENUM("DAC Polarity", wm8960_enum[1]), SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, wm8960_get_deemph, wm8960_put_deemph), diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 687c4dd7ec99..505b65f5734f 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -1930,7 +1930,7 @@ static int wm8995_set_dai_sysclk(struct snd_soc_dai *dai, dai->id + 1, freq); break; case WM8995_SYSCLK_MCLK2: - wm8995->sysclk[dai->id] = WM8995_SYSCLK_MCLK1; + wm8995->sysclk[dai->id] = WM8995_SYSCLK_MCLK2; wm8995->mclk[1] = freq; dev_dbg(dai->dev, "AIF%d using MCLK2 at %uHz\n", dai->id + 1, freq); diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index b62ffd0c133e..f9f90b0f5db4 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -23,6 +23,7 @@ #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/workqueue.h> +#include <linux/debugfs.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -248,6 +249,175 @@ struct wm_coeff_ctl { unsigned int flags; }; +#ifdef CONFIG_DEBUG_FS +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, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wm_adsp *dsp = file->private_data; + ssize_t ret; + + mutex_lock(&dsp->debugfs_lock); + + if (!dsp->wmfw_file_name || !dsp->running) + ret = 0; + else + ret = simple_read_from_buffer(user_buf, count, ppos, + dsp->wmfw_file_name, + strlen(dsp->wmfw_file_name)); + + mutex_unlock(&dsp->debugfs_lock); + return ret; +} + +static ssize_t wm_adsp_debugfs_bin_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wm_adsp *dsp = file->private_data; + ssize_t ret; + + mutex_lock(&dsp->debugfs_lock); + + if (!dsp->bin_file_name || !dsp->running) + ret = 0; + else + ret = simple_read_from_buffer(user_buf, count, ppos, + dsp->bin_file_name, + strlen(dsp->bin_file_name)); + + mutex_unlock(&dsp->debugfs_lock); + return ret; +} + +static const struct { + const char *name; + const struct file_operations fops; +} wm_adsp_debugfs_fops[] = { + { + .name = "wmfw_file_name", + .fops = { + .open = simple_open, + .read = wm_adsp_debugfs_wmfw_read, + }, + }, + { + .name = "bin_file_name", + .fops = { + .open = simple_open, + .read = wm_adsp_debugfs_bin_read, + }, + }, +}; + +static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, + struct snd_soc_codec *codec) +{ + struct dentry *root = NULL; + char *root_name; + int i; + + if (!codec->component.debugfs_root) { + adsp_err(dsp, "No codec debugfs root\n"); + goto err; + } + + root_name = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!root_name) + goto err; + + snprintf(root_name, PAGE_SIZE, "dsp%d", dsp->num); + root = debugfs_create_dir(root_name, codec->component.debugfs_root); + kfree(root_name); + + if (!root) + goto err; + + if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running)) + goto err; + + if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id)) + goto err; + + if (!debugfs_create_x32("fw_version", S_IRUGO, root, + &dsp->fw_id_version)) + goto err; + + for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) { + if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name, + S_IRUGO, root, dsp, + &wm_adsp_debugfs_fops[i].fops)) + goto err; + } + + dsp->debugfs_root = root; + return; + +err: + debugfs_remove_recursive(root); + adsp_err(dsp, "Failed to create debugfs\n"); +} + +static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) +{ + wm_adsp_debugfs_clear(dsp); + debugfs_remove_recursive(dsp->debugfs_root); +} +#else +static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, + struct snd_soc_codec *codec) +{ +} + +static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) +{ +} + +static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, + const char *s) +{ +} + +static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, + const char *s) +{ +} + +static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) +{ +} +#endif + static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -298,7 +468,6 @@ const struct snd_kcontrol_new wm_adsp1_fw_controls[] = { }; EXPORT_SYMBOL_GPL(wm_adsp1_fw_controls); -#if IS_ENABLED(CONFIG_SND_SOC_ARIZONA) static const struct soc_enum wm_adsp2_rate_enum[] = { SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1, ARIZONA_DSP1_RATE_SHIFT, 0xf, @@ -318,22 +487,28 @@ static const struct soc_enum wm_adsp2_rate_enum[] = { arizona_rate_text, arizona_rate_val), }; -const struct snd_kcontrol_new wm_adsp2_fw_controls[] = { - SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], - wm_adsp_fw_get, wm_adsp_fw_put), - SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]), - SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], - wm_adsp_fw_get, wm_adsp_fw_put), - SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]), - SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], - wm_adsp_fw_get, wm_adsp_fw_put), - SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]), - SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], - wm_adsp_fw_get, wm_adsp_fw_put), - SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]), +static const struct snd_kcontrol_new wm_adsp2_fw_controls[4][2] = { + { + SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]), + }, + { + SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]), + }, + { + SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]), + }, + { + SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]), + }, }; -EXPORT_SYMBOL_GPL(wm_adsp2_fw_controls); -#endif static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, int type) @@ -1128,6 +1303,8 @@ static int wm_adsp_load(struct wm_adsp *dsp) adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", file, regions, pos - firmware->size); + wm_adsp_debugfs_save_wmfwname(dsp, file); + out_fw: regmap_async_complete(regmap); wm_adsp_buf_free(&buf_list); @@ -1345,11 +1522,12 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp) n_algs = be32_to_cpu(adsp2_id.n_algs); dsp->fw_id = be32_to_cpu(adsp2_id.fw.id); + dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver); adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", dsp->fw_id, - (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16, - (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8, - be32_to_cpu(adsp2_id.fw.ver) & 0xff, + (dsp->fw_id_version & 0xff0000) >> 16, + (dsp->fw_id_version & 0xff00) >> 8, + dsp->fw_id_version & 0xff, n_algs); alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, @@ -1625,6 +1803,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", file, blocks, pos - firmware->size); + wm_adsp_debugfs_save_binname(dsp, file); + out_fw: regmap_async_complete(regmap); release_firmware(firmware); @@ -1638,6 +1818,9 @@ int wm_adsp1_init(struct wm_adsp *dsp) { INIT_LIST_HEAD(&dsp->alg_regions); +#ifdef CONFIG_DEBUG_FS + mutex_init(&dsp->debugfs_lock); +#endif return 0; } EXPORT_SYMBOL_GPL(wm_adsp1_init); @@ -1896,6 +2079,10 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, /* Log firmware state, it can be useful for analysis */ wm_adsp2_show_fw_status(dsp); + wm_adsp_debugfs_clear(dsp); + + dsp->fw_id = 0; + dsp->fw_id_version = 0; dsp->running = false; regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, @@ -1933,6 +2120,24 @@ err: } EXPORT_SYMBOL_GPL(wm_adsp2_event); +int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec) +{ + wm_adsp2_init_debugfs(dsp, codec); + + return snd_soc_add_codec_controls(codec, + wm_adsp2_fw_controls[dsp->num - 1], + ARRAY_SIZE(wm_adsp2_fw_controls[0])); +} +EXPORT_SYMBOL_GPL(wm_adsp2_codec_probe); + +int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec) +{ + wm_adsp2_cleanup_debugfs(dsp); + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp2_codec_remove); + int wm_adsp2_init(struct wm_adsp *dsp) { int ret; @@ -1952,6 +2157,9 @@ 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 return 0; } EXPORT_SYMBOL_GPL(wm_adsp2_init); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 0e5f07c35d50..5042cbd39e54 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -46,17 +46,26 @@ struct wm_adsp { struct list_head alg_regions; int fw_id; + int fw_id_version; const struct wm_adsp_region *mem; int num_mems; int fw; int fw_ver; - bool running; + u32 running; struct list_head ctl_list; struct work_struct boot_work; + +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_root; + struct mutex debugfs_lock; + char *wmfw_file_name; + char *bin_file_name; +#endif + }; #define WM_ADSP1(wname, num) \ @@ -75,10 +84,11 @@ struct wm_adsp { WM_ADSP2_E(wname, num, wm_adsp2_early_event) extern const struct snd_kcontrol_new wm_adsp1_fw_controls[]; -extern const struct snd_kcontrol_new wm_adsp2_fw_controls[]; int wm_adsp1_init(struct wm_adsp *dsp); int wm_adsp2_init(struct wm_adsp *dsp); +int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec); +int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec); int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index d79349434a9a..b960e626dad9 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -686,6 +686,8 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, if (mcasp->serial_dir[i] == TX_MODE && tx_ser < max_active_serializers) { mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AXR(i)); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), + DISMOD_LOW, DISMOD_MASK); tx_ser++; } else if (mcasp->serial_dir[i] == RX_MODE && rx_ser < max_active_serializers) { @@ -1565,6 +1567,49 @@ static int davinci_mcasp_init_ch_constraints(struct davinci_mcasp *mcasp) return ret; } +enum { + PCM_EDMA, + PCM_SDMA, +}; +static const char *sdma_prefix = "ti,omap"; + +static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp) +{ + struct dma_chan *chan; + const char *tmp; + int ret = PCM_EDMA; + + if (!mcasp->dev->of_node) + return PCM_EDMA; + + tmp = mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data; + chan = dma_request_slave_channel_reason(mcasp->dev, tmp); + if (IS_ERR(chan)) { + if (PTR_ERR(chan) != -EPROBE_DEFER) + dev_err(mcasp->dev, + "Can't verify DMA configuration (%ld)\n", + PTR_ERR(chan)); + return PTR_ERR(chan); + } + BUG_ON(!chan->device || !chan->device->dev); + + if (chan->device->dev->of_node) + ret = of_property_read_string(chan->device->dev->of_node, + "compatible", &tmp); + else + dev_dbg(mcasp->dev, "DMA controller has no of-node\n"); + + dma_release_channel(chan); + if (ret) + return ret; + + dev_dbg(mcasp->dev, "DMA controller compatible = \"%s\"\n", tmp); + if (!strncmp(tmp, sdma_prefix, strlen(sdma_prefix))) + return PCM_SDMA; + + return PCM_EDMA; +} + static int davinci_mcasp_probe(struct platform_device *pdev) { struct snd_dmaengine_dai_dma_data *dma_data; @@ -1763,27 +1808,34 @@ static int davinci_mcasp_probe(struct platform_device *pdev) if (ret != 0) goto err; - switch (mcasp->version) { + ret = davinci_mcasp_get_dma_type(mcasp); + switch (ret) { + case PCM_EDMA: #if IS_BUILTIN(CONFIG_SND_EDMA_SOC) || \ (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \ IS_MODULE(CONFIG_SND_EDMA_SOC)) - case MCASP_VERSION_1: - case MCASP_VERSION_2: - case MCASP_VERSION_3: ret = edma_pcm_platform_register(&pdev->dev); - break; +#else + dev_err(&pdev->dev, "Missing SND_EDMA_SOC\n"); + ret = -EINVAL; + goto err; #endif + break; + case PCM_SDMA: #if IS_BUILTIN(CONFIG_SND_OMAP_SOC) || \ (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \ IS_MODULE(CONFIG_SND_OMAP_SOC)) - case MCASP_VERSION_4: ret = omap_pcm_platform_register(&pdev->dev); - break; +#else + dev_err(&pdev->dev, "Missing SND_SDMA_SOC\n"); + ret = -EINVAL; + goto err; #endif + break; default: - dev_err(&pdev->dev, "Invalid McASP version: %d\n", - mcasp->version); - ret = -EINVAL; + dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret); + case -EPROBE_DEFER: + goto err; break; } diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h index 79dc511180bf..a3be108a8c17 100644 --- a/sound/soc/davinci/davinci-mcasp.h +++ b/sound/soc/davinci/davinci-mcasp.h @@ -215,7 +215,10 @@ * DAVINCI_MCASP_XRSRCTL_BASE_REG - Serializer Control Register Bits */ #define MODE(val) (val) -#define DISMOD (val)(val<<2) +#define DISMOD_3STATE (0x0) +#define DISMOD_LOW (0x2 << 2) +#define DISMOD_HIGH (0x3 << 2) +#define DISMOD_MASK DISMOD_HIGH #define TXSTATE BIT(4) #define RXSTATE BIT(5) #define SRMOD_MASK 3 diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c index cd146d4fa805..b38b98cae855 100644 --- a/sound/soc/fsl/imx-wm8962.c +++ b/sound/soc/fsl/imx-wm8962.c @@ -190,7 +190,7 @@ static int imx_wm8962_probe(struct platform_device *pdev) dev_err(&pdev->dev, "audmux internal port setup failed\n"); return ret; } - imx_audmux_v2_configure_port(ext_port, + ret = imx_audmux_v2_configure_port(ext_port, IMX_AUDMUX_V2_PTCR_SYN, IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); if (ret) { diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index c87e58504a62..d5554939146e 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -26,6 +26,7 @@ struct simple_card_data { struct simple_dai_props { struct asoc_simple_dai cpu_dai; struct asoc_simple_dai codec_dai; + unsigned int mclk_fs; } *dai_props; unsigned int mclk_fs; int gpio_hp_det; @@ -76,11 +77,18 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - unsigned int mclk; + struct simple_dai_props *dai_props = + &priv->dai_props[rtd - rtd->card->rtd]; + unsigned int mclk, mclk_fs = 0; int ret = 0; - if (priv->mclk_fs) { - mclk = params_rate(params) * priv->mclk_fs; + if (priv->mclk_fs) + mclk_fs = priv->mclk_fs; + else if (dai_props->mclk_fs) + mclk_fs = dai_props->mclk_fs; + + if (mclk_fs) { + mclk = params_rate(params) * mclk_fs; ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, SND_SOC_CLOCK_IN); } @@ -313,6 +321,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, char prop[128]; char *prefix = ""; int ret, cpu_args; + u32 val; /* For single DAI link & old style of DT node */ if (is_top_level_node) @@ -338,6 +347,9 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, if (ret < 0) goto dai_link_of_err; + if (!of_property_read_u32(node, "mclk-fs", &val)) + dai_props->mclk_fs = val; + ret = asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai, &dai_link->cpu_of_node, &dai_link->cpu_dai_name, diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 791953ffbc41..f3060a4ca040 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -112,7 +112,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH config SND_SOC_INTEL_CHT_BSW_RT5645_MACH tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec" - depends on X86_INTEL_LPSS + depends on X86_INTEL_LPSS && I2C select SND_SOC_RT5645 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI @@ -123,7 +123,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5645_MACH config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with MAX98090 & TI codec" - depends on X86_INTEL_LPSS + depends on X86_INTEL_LPSS && I2C select SND_SOC_MAX98090 select SND_SOC_TS3A227E select SND_SST_MFLD_PLATFORM diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index 61e240935451..31e9b9ecbb8a 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -1401,36 +1401,32 @@ static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w, down_read(&card->controls_rwsem); list_for_each_entry(kctl, &card->controls, list) { - idx = strstr(kctl->id.name, " "); + idx = strchr(kctl->id.name, ' '); if (idx == NULL) continue; - index = strlen(kctl->id.name) - strlen(idx); + index = idx - (char*)kctl->id.name; + if (strncmp(kctl->id.name, w->name, index)) + continue; - if (strstr(kctl->id.name, "Volume") && - !strncmp(kctl->id.name, w->name, index)) + if (strstr(kctl->id.name, "Volume")) ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN); - else if (strstr(kctl->id.name, "params") && - !strncmp(kctl->id.name, w->name, index)) + else if (strstr(kctl->id.name, "params")) ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO); else if (strstr(kctl->id.name, "Switch") && - !strncmp(kctl->id.name, w->name, index) && strstr(kctl->id.name, "Gain")) { struct sst_gain_mixer_control *mc = (void *)kctl->private_value; mc->w = w; - } else if (strstr(kctl->id.name, "interleaver") && - !strncmp(kctl->id.name, w->name, index)) { + } else if (strstr(kctl->id.name, "interleaver")) { struct sst_enum *e = (void *)kctl->private_value; e->w = w; - } else if (strstr(kctl->id.name, "deinterleaver") && - !strncmp(kctl->id.name, w->name, index)) { - + } else if (strstr(kctl->id.name, "deinterleaver")) { struct sst_enum *e = (void *)kctl->private_value; e->w = w; diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c index 96c2e420cce6..a4b458e77089 100644 --- a/sound/soc/intel/atom/sst/sst.c +++ b/sound/soc/intel/atom/sst/sst.c @@ -368,8 +368,8 @@ static inline void sst_restore_shim64(struct intel_sst_drv *ctx, * initialize by FW or driver when firmware is loaded */ spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); - sst_shim_write64(shim, SST_IMRX, shim_regs->imrx), - sst_shim_write64(shim, SST_CSR, shim_regs->csr), + sst_shim_write64(shim, SST_IMRX, shim_regs->imrx); + sst_shim_write64(shim, SST_CSR, shim_regs->csr); spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); } diff --git a/sound/soc/intel/atom/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c index 7b50a9d17ec1..620da1d1b9e3 100644 --- a/sound/soc/intel/atom/sst/sst_drv_interface.c +++ b/sound/soc/intel/atom/sst/sst_drv_interface.c @@ -533,7 +533,7 @@ static inline int sst_calc_tstamp(struct intel_sst_drv *ctx, info->buffer_ptr = pointer_samples / substream->runtime->channels; - info->pcm_delay = delay_frames / substream->runtime->channels; + info->pcm_delay = delay_frames; dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n", info->buffer_ptr, info->pcm_delay); return 0; diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index 1be079423d1e..d604ee80eda4 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -101,6 +101,33 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream, return 0; } +static int cht_ti_jack_event(struct notifier_block *nb, + unsigned long event, void *data) +{ + + struct snd_soc_jack *jack = (struct snd_soc_jack *)data; + struct snd_soc_dai *codec_dai = jack->card->rtd->codec_dai; + struct snd_soc_codec *codec = codec_dai->codec; + + if (event & SND_JACK_MICROPHONE) { + + snd_soc_dapm_force_enable_pin(&codec->dapm, "SHDN"); + snd_soc_dapm_force_enable_pin(&codec->dapm, "MICBIAS"); + snd_soc_dapm_sync(&codec->dapm); + } else { + + snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS"); + snd_soc_dapm_disable_pin(&codec->dapm, "SHDN"); + snd_soc_dapm_sync(&codec->dapm); + } + + return 0; +} + +static struct notifier_block cht_jack_nb = { + .notifier_call = cht_ti_jack_event, +}; + static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) { int ret; @@ -130,6 +157,9 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) return ret; } + if (ctx->ts3a227e_present) + snd_soc_jack_notifier_register(jack, &cht_jack_nb); + return ret; } diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c index 42f293f9c6e2..67b6d3d52f57 100644 --- a/sound/soc/intel/common/sst-acpi.c +++ b/sound/soc/intel/common/sst-acpi.c @@ -263,7 +263,7 @@ static struct sst_acpi_desc sst_acpi_baytrail_desc = { .resindex_dma_base = -1, }; -static struct acpi_device_id sst_acpi_match[] = { +static const struct acpi_device_id sst_acpi_match[] = { { "INT33C8", (unsigned long)&sst_acpi_haswell_desc }, { "INT3438", (unsigned long)&sst_acpi_broadwell_desc }, { "80860F28", (unsigned long)&sst_acpi_baytrail_desc }, diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig new file mode 100644 index 000000000000..15c04e2eae34 --- /dev/null +++ b/sound/soc/mediatek/Kconfig @@ -0,0 +1,30 @@ +config SND_SOC_MEDIATEK + tristate "ASoC support for Mediatek chip" + depends on ARCH_MEDIATEK + help + This adds ASoC platform driver support for Mediatek chip + that can be used with other codecs. + Select Y if you have such device. + Ex: MT8173 + +config SND_SOC_MT8173_MAX98090 + tristate "ASoC Audio driver for MT8173 with MAX98090 codec" + depends on SND_SOC_MEDIATEK + select SND_SOC_MAX98090 + help + This adds ASoC driver for Mediatek MT8173 boards + with the MAX98090 audio codec. + Select Y if you have such device. + If unsure select "N". + +config SND_SOC_MT8173_RT5650_RT5676 + tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs" + depends on SND_SOC_MEDIATEK + select SND_SOC_RT5645 + select SND_SOC_RT5677 + help + This adds ASoC driver for Mediatek MT8173 boards + with the RT5650 and RT5676 codecs. + Select Y if you have such device. + If unsure select "N". + diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile new file mode 100644 index 000000000000..75effbec438d --- /dev/null +++ b/sound/soc/mediatek/Makefile @@ -0,0 +1,5 @@ +# MTK Platform Support +obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o +# Machine support +obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o +obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173-max98090.c new file mode 100644 index 000000000000..4d44b5803e55 --- /dev/null +++ b/sound/soc/mediatek/mt8173-max98090.c @@ -0,0 +1,213 @@ +/* + * mt8173-max98090.c -- MT8173 MAX98090 ALSA SoC machine driver + * + * Copyright (c) 2015 MediaTek Inc. + * Author: Koro Chen <koro.chen@mediatek.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 and + * only 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 <sound/soc.h> +#include <sound/jack.h> +#include <linux/gpio.h> +#include "../codecs/max98090.h" + +static struct snd_soc_jack mt8173_max98090_jack; + +static struct snd_soc_jack_pin mt8173_max98090_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static const struct snd_soc_dapm_widget mt8173_max98090_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route mt8173_max98090_routes[] = { + {"Speaker", NULL, "SPKL"}, + {"Speaker", NULL, "SPKR"}, + {"DMICL", NULL, "Int Mic"}, + {"Headphone", NULL, "HPL"}, + {"Headphone", NULL, "HPR"}, + {"Headset Mic", NULL, "MICBIAS"}, + {"IN34", NULL, "Headset Mic"}, +}; + +static const struct snd_kcontrol_new mt8173_max98090_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static int mt8173_max98090_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; + + return snd_soc_dai_set_sysclk(codec_dai, 0, params_rate(params) * 256, + SND_SOC_CLOCK_IN); +} + +static struct snd_soc_ops mt8173_max98090_ops = { + .hw_params = mt8173_max98090_hw_params, +}; + +static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec; + + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE, + &mt8173_max98090_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't snd_soc_jack_new %d\n", ret); + return ret; + } + + ret = snd_soc_jack_add_pins(&mt8173_max98090_jack, + ARRAY_SIZE(mt8173_max98090_jack_pins), + mt8173_max98090_jack_pins); + if (ret) { + dev_err(card->dev, "Can't snd_soc_jack_add_pins %d\n", ret); + return ret; + } + + return max98090_mic_detect(codec, &mt8173_max98090_jack); +} + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link mt8173_max98090_dais[] = { + /* Front End DAI links */ + { + .name = "MAX98090 Playback", + .stream_name = "MAX98090 Playback", + .cpu_dai_name = "DL1", + .platform_name = "11220000.mt8173-afe-pcm", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + }, + { + .name = "MAX98090 Capture", + .stream_name = "MAX98090 Capture", + .cpu_dai_name = "VUL", + .platform_name = "11220000.mt8173-afe-pcm", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + }, + /* Back End DAI links */ + { + .name = "Codec", + .cpu_dai_name = "I2S", + .platform_name = "11220000.mt8173-afe-pcm", + .no_pcm = 1, + .codec_dai_name = "HiFi", + .init = mt8173_max98090_init, + .ops = &mt8173_max98090_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_card mt8173_max98090_card = { + .name = "mt8173-max98090", + .dai_link = mt8173_max98090_dais, + .num_links = ARRAY_SIZE(mt8173_max98090_dais), + .controls = mt8173_max98090_controls, + .num_controls = ARRAY_SIZE(mt8173_max98090_controls), + .dapm_widgets = mt8173_max98090_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8173_max98090_widgets), + .dapm_routes = mt8173_max98090_routes, + .num_dapm_routes = ARRAY_SIZE(mt8173_max98090_routes), +}; + +static int mt8173_max98090_dev_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt8173_max98090_card; + struct device_node *codec_node; + int ret, i; + + codec_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,audio-codec", 0); + if (!codec_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + for (i = 0; i < card->num_links; i++) { + if (mt8173_max98090_dais[i].codec_name) + continue; + mt8173_max98090_dais[i].codec_of_node = codec_node; + } + card->dev = &pdev->dev; + + ret = snd_soc_register_card(card); + if (ret) + dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + __func__, ret); + return ret; +} + +static int mt8173_max98090_dev_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + return 0; +} + +static const struct of_device_id mt8173_max98090_dt_match[] = { + { .compatible = "mediatek,mt8173-max98090", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8173_max98090_dt_match); + +static struct platform_driver mt8173_max98090_driver = { + .driver = { + .name = "mt8173-max98090", + .owner = THIS_MODULE, + .of_match_table = mt8173_max98090_dt_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, + .probe = mt8173_max98090_dev_probe, + .remove = mt8173_max98090_dev_remove, +}; + +module_platform_driver(mt8173_max98090_driver); + +/* Module information */ +MODULE_DESCRIPTION("MT8173 MAX98090 ALSA SoC machine driver"); +MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mt8173-max98090"); + diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c new file mode 100644 index 000000000000..094055323059 --- /dev/null +++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c @@ -0,0 +1,278 @@ +/* + * mt8173-rt5650-rt5676.c -- MT8173 machine driver with RT5650/5676 codecs + * + * Copyright (c) 2015 MediaTek Inc. + * Author: Koro Chen <koro.chen@mediatek.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 and + * only 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/gpio.h> +#include <linux/of_gpio.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include "../codecs/rt5645.h" +#include "../codecs/rt5677.h" + +#define MCLK_FOR_CODECS 12288000 + +static const struct snd_soc_dapm_widget mt8173_rt5650_rt5676_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route mt8173_rt5650_rt5676_routes[] = { + {"Speaker", NULL, "SPOL"}, + {"Speaker", NULL, "SPOR"}, + {"Speaker", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */ + {"Sub DMIC L1", NULL, "Int Mic"}, /* DMIC from 5676 */ + {"Sub DMIC R1", NULL, "Int Mic"}, + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Headphone", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */ + {"Headset Mic", NULL, "micbias1"}, + {"Headset Mic", NULL, "micbias2"}, + {"IN1P", NULL, "Headset Mic"}, + {"IN1N", NULL, "Headset Mic"}, + {"Sub AIF2RX", NULL, "Headset Mic"}, /* IF2 DAC from 5650 */ +}; + +static const struct snd_kcontrol_new mt8173_rt5650_rt5676_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int i, ret; + + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; + + /* pll from mclk 12.288M */ + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, + params_rate(params) * 512); + if (ret) + return ret; + + /* sysclk from pll */ + ret = snd_soc_dai_set_sysclk(codec_dai, 1, + params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + return 0; +} + +static struct snd_soc_ops mt8173_rt5650_rt5676_ops = { + .hw_params = mt8173_rt5650_rt5676_hw_params, +}; + +static struct snd_soc_jack mt8173_rt5650_rt5676_jack; + +static int mt8173_rt5650_rt5676_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec_dais[0]->codec; + struct snd_soc_codec *codec_sub = runtime->codec_dais[1]->codec; + int ret; + + rt5645_sel_asrc_clk_src(codec, + RT5645_DA_STEREO_FILTER | + RT5645_AD_STEREO_FILTER, + RT5645_CLK_SEL_I2S1_ASRC); + rt5677_sel_asrc_clk_src(codec_sub, + RT5677_DA_STEREO_FILTER | + RT5677_AD_STEREO1_FILTER, + RT5677_CLK_SEL_I2S1_ASRC); + rt5677_sel_asrc_clk_src(codec_sub, + RT5677_AD_STEREO2_FILTER | + RT5677_I2S2_SOURCE, + RT5677_CLK_SEL_I2S2_ASRC); + + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &mt8173_rt5650_rt5676_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't new Headset Jack %d\n", ret); + return ret; + } + + return rt5645_set_jack_detect(codec, + &mt8173_rt5650_rt5676_jack, + &mt8173_rt5650_rt5676_jack, + &mt8173_rt5650_rt5676_jack); +} + +static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = { + { + .dai_name = "rt5645-aif1", + }, + { + .dai_name = "rt5677-aif1", + }, +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { + /* Front End DAI links */ + { + .name = "rt5650_rt5676 Playback", + .stream_name = "rt5650_rt5676 Playback", + .cpu_dai_name = "DL1", + .platform_name = "11220000.mt8173-afe-pcm", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + }, + { + .name = "rt5650_rt5676 Capture", + .stream_name = "rt5650_rt5676 Capture", + .cpu_dai_name = "VUL", + .platform_name = "11220000.mt8173-afe-pcm", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + }, + + /* Back End DAI links */ + { + .name = "Codec", + .cpu_dai_name = "I2S", + .platform_name = "11220000.mt8173-afe-pcm", + .no_pcm = 1, + .codecs = mt8173_rt5650_rt5676_codecs, + .num_codecs = 2, + .init = mt8173_rt5650_rt5676_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &mt8173_rt5650_rt5676_ops, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */ + .name = "rt5650_rt5676 intercodec", + .stream_name = "rt5650_rt5676 intercodec", + .cpu_dai_name = "snd-soc-dummy-dai", + .platform_name = "snd-soc-dummy", + .no_pcm = 1, + .codec_dai_name = "rt5677-aif2", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, + }, + +}; + +static struct snd_soc_codec_conf mt8173_rt5650_rt5676_codec_conf[] = { + { + .name_prefix = "Sub", + }, +}; + +static struct snd_soc_card mt8173_rt5650_rt5676_card = { + .name = "mtk-rt5650-rt5676", + .dai_link = mt8173_rt5650_rt5676_dais, + .num_links = ARRAY_SIZE(mt8173_rt5650_rt5676_dais), + .codec_conf = mt8173_rt5650_rt5676_codec_conf, + .num_configs = ARRAY_SIZE(mt8173_rt5650_rt5676_codec_conf), + .controls = mt8173_rt5650_rt5676_controls, + .num_controls = ARRAY_SIZE(mt8173_rt5650_rt5676_controls), + .dapm_widgets = mt8173_rt5650_rt5676_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5676_widgets), + .dapm_routes = mt8173_rt5650_rt5676_routes, + .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5676_routes), +}; + +static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt8173_rt5650_rt5676_card; + int ret; + + mt8173_rt5650_rt5676_codecs[0].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0); + if (!mt8173_rt5650_rt5676_codecs[0].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + mt8173_rt5650_rt5676_codecs[1].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1); + if (!mt8173_rt5650_rt5676_codecs[1].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + mt8173_rt5650_rt5676_codec_conf[0].of_node = + mt8173_rt5650_rt5676_codecs[1].of_node; + + mt8173_rt5650_rt5676_dais[3].codec_of_node = + mt8173_rt5650_rt5676_codecs[1].of_node; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + + ret = snd_soc_register_card(card); + if (ret) + dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + __func__, ret); + return ret; +} + +static int mt8173_rt5650_rt5676_dev_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + return 0; +} + +static const struct of_device_id mt8173_rt5650_rt5676_dt_match[] = { + { .compatible = "mediatek,mt8173-rt5650-rt5676", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5676_dt_match); + +static struct platform_driver mt8173_rt5650_rt5676_driver = { + .driver = { + .name = "mtk-rt5650-rt5676", + .owner = THIS_MODULE, + .of_match_table = mt8173_rt5650_rt5676_dt_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, + .probe = mt8173_rt5650_rt5676_dev_probe, + .remove = mt8173_rt5650_rt5676_dev_remove, +}; + +module_platform_driver(mt8173_rt5650_rt5676_driver); + +/* Module information */ +MODULE_DESCRIPTION("MT8173 RT5650 and RT5676 SoC machine driver"); +MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mtk-rt5650-rt5676"); + diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h new file mode 100644 index 000000000000..a88b17511fdf --- /dev/null +++ b/sound/soc/mediatek/mtk-afe-common.h @@ -0,0 +1,109 @@ +/* + * mtk_afe_common.h -- Mediatek audio driver common definitions + * + * Copyright (c) 2015 MediaTek Inc. + * Author: Koro Chen <koro.chen@mediatek.com> + * Sascha Hauer <s.hauer@pengutronix.de> + * Hidalgo Huang <hidalgo.huang@mediatek.com> + * Ir Lian <ir.lian@mediatek.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 and + * only 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. + */ + +#ifndef _MTK_AFE_COMMON_H_ +#define _MTK_AFE_COMMON_H_ + +#include <linux/clk.h> +#include <linux/regmap.h> + +enum { + MTK_AFE_MEMIF_DL1, + MTK_AFE_MEMIF_DL2, + MTK_AFE_MEMIF_VUL, + MTK_AFE_MEMIF_DAI, + MTK_AFE_MEMIF_AWB, + MTK_AFE_MEMIF_MOD_DAI, + MTK_AFE_MEMIF_HDMI, + MTK_AFE_MEMIF_NUM, + MTK_AFE_IO_MOD_PCM1 = MTK_AFE_MEMIF_NUM, + MTK_AFE_IO_MOD_PCM2, + MTK_AFE_IO_PMIC, + MTK_AFE_IO_I2S, + MTK_AFE_IO_2ND_I2S, + MTK_AFE_IO_HW_GAIN1, + MTK_AFE_IO_HW_GAIN2, + MTK_AFE_IO_MRG_O, + MTK_AFE_IO_MRG_I, + MTK_AFE_IO_DAIBT, + MTK_AFE_IO_HDMI, +}; + +enum { + MTK_AFE_IRQ_1, + MTK_AFE_IRQ_2, + MTK_AFE_IRQ_3, + MTK_AFE_IRQ_4, + MTK_AFE_IRQ_5, + MTK_AFE_IRQ_6, + MTK_AFE_IRQ_7, + MTK_AFE_IRQ_8, + MTK_AFE_IRQ_NUM, +}; + +enum { + MTK_CLK_INFRASYS_AUD, + MTK_CLK_TOP_PDN_AUD, + MTK_CLK_TOP_PDN_AUD_BUS, + MTK_CLK_I2S0_M, + MTK_CLK_I2S1_M, + MTK_CLK_I2S2_M, + MTK_CLK_I2S3_M, + MTK_CLK_I2S3_B, + MTK_CLK_BCK0, + MTK_CLK_BCK1, + MTK_CLK_NUM +}; + +struct mtk_afe; +struct snd_pcm_substream; + +struct mtk_afe_memif_data { + int id; + const char *name; + int reg_ofs_base; + int reg_ofs_cur; + int fs_shift; + int mono_shift; + int enable_shift; + int irq_reg_cnt; + int irq_cnt_shift; + int irq_en_shift; + int irq_fs_shift; + int irq_clr_shift; +}; + +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; +}; + +struct mtk_afe { + /* address for ioremap audio hardware register */ + void __iomem *base_addr; + struct device *dev; + struct regmap *regmap; + struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM]; + struct clk *clocks[MTK_CLK_NUM]; +}; +#endif diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c new file mode 100644 index 000000000000..cc228db5fb76 --- /dev/null +++ b/sound/soc/mediatek/mtk-afe-pcm.c @@ -0,0 +1,1233 @@ +/* + * Mediatek ALSA SoC AFE platform driver + * + * Copyright (c) 2015 MediaTek Inc. + * Author: Koro Chen <koro.chen@mediatek.com> + * Sascha Hauer <s.hauer@pengutronix.de> + * Hidalgo Huang <hidalgo.huang@mediatek.com> + * Ir Lian <ir.lian@mediatek.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 and + * only 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/delay.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/pm_runtime.h> +#include <sound/soc.h> +#include "mtk-afe-common.h" + +/***************************************************************************** + * R E G I S T E R D E F I N I T I O N + *****************************************************************************/ +#define AUDIO_TOP_CON0 0x0000 +#define AUDIO_TOP_CON1 0x0004 +#define AFE_DAC_CON0 0x0010 +#define AFE_DAC_CON1 0x0014 +#define AFE_I2S_CON1 0x0034 +#define AFE_I2S_CON2 0x0038 +#define AFE_CONN_24BIT 0x006c + +#define AFE_CONN1 0x0024 +#define AFE_CONN2 0x0028 +#define AFE_CONN7 0x0460 +#define AFE_CONN8 0x0464 +#define AFE_HDMI_CONN0 0x0390 + +/* Memory interface */ +#define AFE_DL1_BASE 0x0040 +#define AFE_DL1_CUR 0x0044 +#define AFE_DL2_BASE 0x0050 +#define AFE_DL2_CUR 0x0054 +#define AFE_AWB_BASE 0x0070 +#define AFE_AWB_CUR 0x007c +#define AFE_VUL_BASE 0x0080 +#define AFE_VUL_CUR 0x008c +#define AFE_DAI_BASE 0x0090 +#define AFE_DAI_CUR 0x009c +#define AFE_MOD_PCM_BASE 0x0330 +#define AFE_MOD_PCM_CUR 0x033c +#define AFE_HDMI_OUT_BASE 0x0374 +#define AFE_HDMI_OUT_CUR 0x0378 + +#define AFE_ADDA2_TOP_CON0 0x0600 + +#define AFE_HDMI_OUT_CON0 0x0370 + +#define AFE_IRQ_MCU_CON 0x03a0 +#define AFE_IRQ_STATUS 0x03a4 +#define AFE_IRQ_CLR 0x03a8 +#define AFE_IRQ_CNT1 0x03ac +#define AFE_IRQ_CNT2 0x03b0 +#define AFE_IRQ_MCU_EN 0x03b4 +#define AFE_IRQ_CNT5 0x03bc +#define AFE_IRQ_CNT7 0x03dc + +#define AFE_TDM_CON1 0x0548 +#define AFE_TDM_CON2 0x054c + +#define AFE_BASE_END_OFFSET 8 +#define AFE_IRQ_STATUS_BITS 0xff + +/* AUDIO_TOP_CON0 (0x0000) */ +#define AUD_TCON0_PDN_SPDF (0x1 << 21) +#define AUD_TCON0_PDN_HDMI (0x1 << 20) +#define AUD_TCON0_PDN_24M (0x1 << 9) +#define AUD_TCON0_PDN_22M (0x1 << 8) +#define AUD_TCON0_PDN_AFE (0x1 << 2) + +/* AFE_I2S_CON1 (0x0034) */ +#define AFE_I2S_CON1_LOW_JITTER_CLK (0x1 << 12) +#define AFE_I2S_CON1_RATE(x) (((x) & 0xf) << 8) +#define AFE_I2S_CON1_FORMAT_I2S (0x1 << 3) +#define AFE_I2S_CON1_EN (0x1 << 0) + +/* AFE_I2S_CON2 (0x0038) */ +#define AFE_I2S_CON2_LOW_JITTER_CLK (0x1 << 12) +#define AFE_I2S_CON2_RATE(x) (((x) & 0xf) << 8) +#define AFE_I2S_CON2_FORMAT_I2S (0x1 << 3) +#define AFE_I2S_CON2_EN (0x1 << 0) + +/* AFE_CONN_24BIT (0x006c) */ +#define AFE_CONN_24BIT_O04 (0x1 << 4) +#define AFE_CONN_24BIT_O03 (0x1 << 3) + +/* AFE_HDMI_CONN0 (0x0390) */ +#define AFE_HDMI_CONN0_O37_I37 (0x7 << 21) +#define AFE_HDMI_CONN0_O36_I36 (0x6 << 18) +#define AFE_HDMI_CONN0_O35_I33 (0x3 << 15) +#define AFE_HDMI_CONN0_O34_I32 (0x2 << 12) +#define AFE_HDMI_CONN0_O33_I35 (0x5 << 9) +#define AFE_HDMI_CONN0_O32_I34 (0x4 << 6) +#define AFE_HDMI_CONN0_O31_I31 (0x1 << 3) +#define AFE_HDMI_CONN0_O30_I30 (0x0 << 0) + +/* AFE_TDM_CON1 (0x0548) */ +#define AFE_TDM_CON1_LRCK_WIDTH(x) (((x) - 1) << 24) +#define AFE_TDM_CON1_32_BCK_CYCLES (0x2 << 12) +#define AFE_TDM_CON1_WLEN_32BIT (0x2 << 8) +#define AFE_TDM_CON1_MSB_ALIGNED (0x1 << 4) +#define AFE_TDM_CON1_1_BCK_DELAY (0x1 << 3) +#define AFE_TDM_CON1_BCK_INV (0x1 << 1) +#define AFE_TDM_CON1_EN (0x1 << 0) + +enum afe_tdm_ch_start { + AFE_TDM_CH_START_O30_O31 = 0, + AFE_TDM_CH_START_O32_O33, + AFE_TDM_CH_START_O34_O35, + AFE_TDM_CH_START_O36_O37, + AFE_TDM_CH_ZERO, +}; + +static const struct snd_pcm_hardware mtk_afe_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .buffer_bytes_max = 256 * 1024, + .period_bytes_min = 512, + .period_bytes_max = 128 * 1024, + .periods_min = 2, + .periods_max = 256, + .fifo_size = 0, +}; + +static snd_pcm_uframes_t mtk_afe_pcm_pointer + (struct snd_pcm_substream *substream) +{ + 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]; + + return bytes_to_frames(substream->runtime, memif->hw_ptr); +} + +static const struct snd_pcm_ops mtk_afe_pcm_ops = { + .ioctl = snd_pcm_lib_ioctl, + .pointer = mtk_afe_pcm_pointer, +}; + +static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + size_t size; + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + + size = mtk_afe_hardware.buffer_bytes_max; + + return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + card->dev, size, size); +} + +static void mtk_afe_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static const struct snd_soc_platform_driver mtk_afe_pcm_platform = { + .ops = &mtk_afe_pcm_ops, + .pcm_new = mtk_afe_pcm_new, + .pcm_free = mtk_afe_pcm_free, +}; + +struct mtk_afe_rate { + unsigned int rate; + unsigned int regvalue; +}; + +static const struct mtk_afe_rate mtk_afe_i2s_rates[] = { + { .rate = 8000, .regvalue = 0 }, + { .rate = 11025, .regvalue = 1 }, + { .rate = 12000, .regvalue = 2 }, + { .rate = 16000, .regvalue = 4 }, + { .rate = 22050, .regvalue = 5 }, + { .rate = 24000, .regvalue = 6 }, + { .rate = 32000, .regvalue = 8 }, + { .rate = 44100, .regvalue = 9 }, + { .rate = 48000, .regvalue = 10 }, + { .rate = 88000, .regvalue = 11 }, + { .rate = 96000, .regvalue = 12 }, + { .rate = 174000, .regvalue = 13 }, + { .rate = 192000, .regvalue = 14 }, +}; + +static int mtk_afe_i2s_fs(unsigned int sample_rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mtk_afe_i2s_rates); i++) + if (mtk_afe_i2s_rates[i].rate == sample_rate) + return mtk_afe_i2s_rates[i].regvalue; + + return -EINVAL; +} + +static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate) +{ + unsigned int val; + int fs = mtk_afe_i2s_fs(rate); + + if (fs < 0) + return -EINVAL; + + /* from external ADC */ + regmap_update_bits(afe->regmap, AFE_ADDA2_TOP_CON0, 0x1, 0x1); + + /* set input */ + val = AFE_I2S_CON2_LOW_JITTER_CLK | + AFE_I2S_CON2_RATE(fs) | + AFE_I2S_CON2_FORMAT_I2S; + + regmap_update_bits(afe->regmap, AFE_I2S_CON2, ~AFE_I2S_CON2_EN, val); + + /* set output */ + val = AFE_I2S_CON1_LOW_JITTER_CLK | + AFE_I2S_CON1_RATE(fs) | + AFE_I2S_CON1_FORMAT_I2S; + + regmap_update_bits(afe->regmap, AFE_I2S_CON1, ~AFE_I2S_CON1_EN, val); + return 0; +} + +static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable) +{ + unsigned int val; + + regmap_read(afe->regmap, AFE_I2S_CON2, &val); + if (!!(val & AFE_I2S_CON2_EN) == enable) + return; /* must skip soft reset */ + + /* I2S soft reset begin */ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0x4); + + /* input */ + regmap_update_bits(afe->regmap, AFE_I2S_CON2, 0x1, enable); + + /* output */ + regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable); + + /* I2S soft reset end */ + udelay(1); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0); +} + +static int mtk_afe_dais_enable_clks(struct mtk_afe *afe, + struct clk *m_ck, struct clk *b_ck) +{ + int ret; + + if (m_ck) { + ret = clk_prepare_enable(m_ck); + if (ret) { + 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) { + ret = clk_prepare_enable(b_ck); + if (ret) { + dev_err(afe->dev, "Failed to enable b_ck\n"); + return ret; + } + } + return 0; +} + +static int mtk_afe_dais_set_clks(struct mtk_afe *afe, + struct clk *m_ck, unsigned int mck_rate, + struct clk *b_ck, unsigned int bck_rate) +{ + int ret; + + if (m_ck) { + ret = clk_set_rate(m_ck, mck_rate); + if (ret) { + dev_err(afe->dev, "Failed to set m_ck rate\n"); + return ret; + } + } + + if (b_ck) { + ret = clk_set_rate(b_ck, bck_rate); + if (ret) { + dev_err(afe->dev, "Failed to set b_ck rate\n"); + return ret; + } + } + return 0; +} + +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); + clk_disable_unprepare(m_ck); + } + if (b_ck) + clk_disable_unprepare(b_ck); +} + +static int mtk_afe_i2s_startup(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); + + if (dai->active) + return 0; + + mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); + return 0; +} + +static void mtk_afe_i2s_shutdown(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); + + if (dai->active) + return; + + mtk_afe_set_i2s_enable(afe, false); + 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, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime * const runtime = substream->runtime; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + int ret; + + mtk_afe_dais_set_clks(afe, + afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256, + NULL, 0); + /* config I2S */ + ret = mtk_afe_set_i2s(afe, substream->runtime->rate); + if (ret) + return ret; + + mtk_afe_set_i2s_enable(afe, true); + + return 0; +} + +static int mtk_afe_hdmi_startup(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); + + if (dai->active) + return 0; + + mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S3_M], + afe->clocks[MTK_CLK_I2S3_B]); + return 0; +} + +static void mtk_afe_hdmi_shutdown(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); + + if (dai->active) + return; + + 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, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime * const runtime = substream->runtime; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + unsigned int val; + + mtk_afe_dais_set_clks(afe, + afe->clocks[MTK_CLK_I2S3_M], runtime->rate * 128, + afe->clocks[MTK_CLK_I2S3_B], + runtime->rate * runtime->channels * 32); + + val = AFE_TDM_CON1_BCK_INV | + AFE_TDM_CON1_1_BCK_DELAY | + AFE_TDM_CON1_MSB_ALIGNED | /* I2S mode */ + AFE_TDM_CON1_WLEN_32BIT | + AFE_TDM_CON1_32_BCK_CYCLES | + AFE_TDM_CON1_LRCK_WIDTH(32); + regmap_update_bits(afe->regmap, AFE_TDM_CON1, ~AFE_TDM_CON1_EN, val); + + /* set tdm2 config */ + switch (runtime->channels) { + case 1: + case 2: + val = AFE_TDM_CH_START_O30_O31; + val |= (AFE_TDM_CH_ZERO << 4); + val |= (AFE_TDM_CH_ZERO << 8); + val |= (AFE_TDM_CH_ZERO << 12); + break; + case 3: + case 4: + val = AFE_TDM_CH_START_O30_O31; + val |= (AFE_TDM_CH_START_O32_O33 << 4); + val |= (AFE_TDM_CH_ZERO << 8); + val |= (AFE_TDM_CH_ZERO << 12); + break; + case 5: + case 6: + val = AFE_TDM_CH_START_O30_O31; + val |= (AFE_TDM_CH_START_O32_O33 << 4); + val |= (AFE_TDM_CH_START_O34_O35 << 8); + val |= (AFE_TDM_CH_ZERO << 12); + break; + case 7: + case 8: + val = AFE_TDM_CH_START_O30_O31; + val |= (AFE_TDM_CH_START_O32_O33 << 4); + val |= (AFE_TDM_CH_START_O34_O35 << 8); + val |= (AFE_TDM_CH_START_O36_O37 << 12); + break; + default: + val = 0; + } + regmap_update_bits(afe->regmap, AFE_TDM_CON2, 0x0000ffff, val); + + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, + 0x000000f0, runtime->channels << 4); + return 0; +} + +static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd, + 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); + + dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF, 0); + + /* set connections: O30~O37: L/R/LS/RS/C/LFE/CH7/CH8 */ + regmap_write(afe->regmap, AFE_HDMI_CONN0, + AFE_HDMI_CONN0_O30_I30 | AFE_HDMI_CONN0_O31_I31 | + AFE_HDMI_CONN0_O32_I34 | AFE_HDMI_CONN0_O33_I35 | + AFE_HDMI_CONN0_O34_I32 | AFE_HDMI_CONN0_O35_I33 | + AFE_HDMI_CONN0_O36_I36 | AFE_HDMI_CONN0_O37_I37); + + /* enable Out control */ + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0x1); + + /* enable tdm */ + regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0x1); + + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + /* disable tdm */ + regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0); + + /* disable Out control */ + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0); + + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF, + AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF); + + return 0; + default: + return -EINVAL; + } +} + +static int mtk_afe_dais_startup(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); + struct snd_pcm_runtime *runtime = substream->runtime; + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + int ret; + + memif->substream = substream; + + snd_soc_set_runtime_hwparams(substream, &mtk_afe_hardware); + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n"); + return ret; +} + +static void mtk_afe_dais_shutdown(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); + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + + memif->substream = NULL; +} + +static int mtk_afe_dais_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 mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + int ret; + + dev_dbg(afe->dev, + "%s period = %u, rate= %u, channels=%u\n", + __func__, params_period_size(params), params_rate(params), + params_channels(params)); + + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (ret < 0) + return ret; + + memif->phys_buf_addr = substream->runtime->dma_addr; + memif->buffer_size = substream->runtime->dma_bytes; + memif->hw_ptr = 0; + + /* start */ + regmap_write(afe->regmap, + memif->data->reg_ofs_base, memif->phys_buf_addr); + /* end */ + regmap_write(afe->regmap, + memif->data->reg_ofs_base + AFE_BASE_END_OFFSET, + memif->phys_buf_addr + memif->buffer_size - 1); + + /* set channel */ + if (memif->data->mono_shift >= 0) { + unsigned int mono = (params_channels(params) == 1) ? 1 : 0; + + regmap_update_bits(afe->regmap, AFE_DAC_CON1, + 1 << memif->data->mono_shift, + mono << memif->data->mono_shift); + } + + /* set rate */ + if (memif->data->fs_shift < 0) + return 0; + if (memif->data->id == MTK_AFE_MEMIF_DAI || + memif->data->id == MTK_AFE_MEMIF_MOD_DAI) { + unsigned int val; + + switch (params_rate(params)) { + case 8000: + val = 0; + break; + case 16000: + val = 1; + break; + case 32000: + val = 2; + break; + default: + return -EINVAL; + } + + if (memif->data->id == MTK_AFE_MEMIF_DAI) + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + 0x3 << memif->data->fs_shift, + val << memif->data->fs_shift); + else + regmap_update_bits(afe->regmap, AFE_DAC_CON1, + 0x3 << memif->data->fs_shift, + val << memif->data->fs_shift); + + } else { + int fs = mtk_afe_i2s_fs(params_rate(params)); + + if (fs < 0) + return -EINVAL; + + regmap_update_bits(afe->regmap, AFE_DAC_CON1, + 0xf << memif->data->fs_shift, + fs << memif->data->fs_shift); + } + + return 0; +} + +static int mtk_afe_dais_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + 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) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime * const runtime = substream->runtime; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + unsigned int counter = runtime->period_size; + + dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (memif->data->enable_shift >= 0) + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + 1 << memif->data->enable_shift, + 1 << memif->data->enable_shift); + + /* set irq counter */ + regmap_update_bits(afe->regmap, + memif->data->irq_reg_cnt, + 0x3ffff << memif->data->irq_cnt_shift, + counter << memif->data->irq_cnt_shift); + + /* set irq fs */ + if (memif->data->irq_fs_shift >= 0) { + int fs = mtk_afe_i2s_fs(runtime->rate); + + if (fs < 0) + return -EINVAL; + + regmap_update_bits(afe->regmap, + AFE_IRQ_MCU_CON, + 0xf << memif->data->irq_fs_shift, + fs << memif->data->irq_fs_shift); + } + /* enable interrupt */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON, + 1 << memif->data->irq_en_shift, + 1 << memif->data->irq_en_shift); + + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (memif->data->enable_shift >= 0) + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + 1 << memif->data->enable_shift, 0); + /* disable interrupt */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON, + 1 << memif->data->irq_en_shift, + 0 << memif->data->irq_en_shift); + /* 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; + } +} + +/* FE DAIs */ +static const struct snd_soc_dai_ops mtk_afe_dai_ops = { + .startup = mtk_afe_dais_startup, + .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, +}; + +/* BE DAIs */ +static const struct snd_soc_dai_ops mtk_afe_i2s_ops = { + .startup = mtk_afe_i2s_startup, + .shutdown = mtk_afe_i2s_shutdown, + .prepare = mtk_afe_i2s_prepare, +}; + +static const struct snd_soc_dai_ops mtk_afe_hdmi_ops = { + .startup = mtk_afe_hdmi_startup, + .shutdown = mtk_afe_hdmi_shutdown, + .prepare = mtk_afe_hdmi_prepare, + .trigger = mtk_afe_hdmi_trigger, + +}; + +static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = { + /* FE DAIs: memory intefaces to CPU */ + { + .name = "DL1", /* downlink 1 */ + .id = MTK_AFE_MEMIF_DL1, + .playback = { + .stream_name = "DL1", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_dai_ops, + }, { + .name = "VUL", /* voice uplink */ + .id = MTK_AFE_MEMIF_VUL, + .capture = { + .stream_name = "VUL", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_dai_ops, + }, { + /* BE DAIs */ + .name = "I2S", + .id = MTK_AFE_IO_I2S, + .playback = { + .stream_name = "I2S Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "I2S Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_i2s_ops, + .symmetric_rates = 1, + }, +}; + +static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = { + /* FE DAIs */ + { + .name = "HDMI", + .id = MTK_AFE_MEMIF_HDMI, + .playback = { + .stream_name = "HDMI", + .channels_min = 2, + .channels_max = 8, + .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, + }, + .ops = &mtk_afe_dai_ops, + }, { + /* BE DAIs */ + .name = "HDMIO", + .id = MTK_AFE_IO_HDMI, + .playback = { + .stream_name = "HDMIO Playback", + .channels_min = 2, + .channels_max = 8, + .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, + }, + .ops = &mtk_afe_hdmi_ops, + }, +}; + +static const struct snd_kcontrol_new mtk_afe_o03_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN1, 21, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_afe_o04_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN2, 6, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_afe_o09_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_afe_o10_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0), +}; + +static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = { + /* Backend DAIs */ + SND_SOC_DAPM_AIF_IN("I2S Capture", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S Playback", NULL, 0, SND_SOC_NOPM, 0, 0), + + /* inter-connections */ + SND_SOC_DAPM_MIXER("I05", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I06", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0, + mtk_afe_o03_mix, ARRAY_SIZE(mtk_afe_o03_mix)), + SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0, + mtk_afe_o04_mix, ARRAY_SIZE(mtk_afe_o04_mix)), + SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0, + mtk_afe_o09_mix, ARRAY_SIZE(mtk_afe_o09_mix)), + SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0, + mtk_afe_o10_mix, ARRAY_SIZE(mtk_afe_o10_mix)), +}; + +static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = { + {"I05", NULL, "DL1"}, + {"I06", NULL, "DL1"}, + {"I2S Playback", NULL, "O03"}, + {"I2S Playback", NULL, "O04"}, + {"VUL", NULL, "O09"}, + {"VUL", NULL, "O10"}, + {"I17", NULL, "I2S Capture"}, + {"I18", NULL, "I2S Capture"}, + { "O03", "I05 Switch", "I05" }, + { "O04", "I06 Switch", "I06" }, + { "O09", "I17 Switch", "I17" }, + { "O10", "I18 Switch", "I18" }, +}; + +static const struct snd_soc_dapm_widget mtk_afe_hdmi_widgets[] = { + /* Backend DAIs */ + SND_SOC_DAPM_AIF_OUT("HDMIO Playback", NULL, 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = { + {"HDMIO Playback", NULL, "HDMI"}, +}; + +static const struct snd_soc_component_driver mtk_afe_pcm_dai_component = { + .name = "mtk-afe-pcm-dai", + .dapm_widgets = mtk_afe_pcm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mtk_afe_pcm_widgets), + .dapm_routes = mtk_afe_pcm_routes, + .num_dapm_routes = ARRAY_SIZE(mtk_afe_pcm_routes), +}; + +static const struct snd_soc_component_driver mtk_afe_hdmi_dai_component = { + .name = "mtk-afe-hdmi-dai", + .dapm_widgets = mtk_afe_hdmi_widgets, + .num_dapm_widgets = ARRAY_SIZE(mtk_afe_hdmi_widgets), + .dapm_routes = mtk_afe_hdmi_routes, + .num_dapm_routes = ARRAY_SIZE(mtk_afe_hdmi_routes), +}; + +static const char *aud_clks[MTK_CLK_NUM] = { + [MTK_CLK_INFRASYS_AUD] = "infra_sys_audio_clk", + [MTK_CLK_TOP_PDN_AUD] = "top_pdn_audio", + [MTK_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus", + [MTK_CLK_I2S0_M] = "i2s0_m", + [MTK_CLK_I2S1_M] = "i2s1_m", + [MTK_CLK_I2S2_M] = "i2s2_m", + [MTK_CLK_I2S3_M] = "i2s3_m", + [MTK_CLK_I2S3_B] = "i2s3_b", + [MTK_CLK_BCK0] = "bck0", + [MTK_CLK_BCK1] = "bck1", +}; + +static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { + { + .name = "DL1", + .id = MTK_AFE_MEMIF_DL1, + .reg_ofs_base = AFE_DL1_BASE, + .reg_ofs_cur = AFE_DL1_CUR, + .fs_shift = 0, + .mono_shift = 21, + .enable_shift = 1, + .irq_reg_cnt = AFE_IRQ_CNT1, + .irq_cnt_shift = 0, + .irq_en_shift = 0, + .irq_fs_shift = 4, + .irq_clr_shift = 0, + }, { + .name = "DL2", + .id = MTK_AFE_MEMIF_DL2, + .reg_ofs_base = AFE_DL2_BASE, + .reg_ofs_cur = AFE_DL2_CUR, + .fs_shift = 4, + .mono_shift = 22, + .enable_shift = 2, + .irq_reg_cnt = AFE_IRQ_CNT1, + .irq_cnt_shift = 20, + .irq_en_shift = 2, + .irq_fs_shift = 16, + .irq_clr_shift = 2, + }, { + .name = "VUL", + .id = MTK_AFE_MEMIF_VUL, + .reg_ofs_base = AFE_VUL_BASE, + .reg_ofs_cur = AFE_VUL_CUR, + .fs_shift = 16, + .mono_shift = 27, + .enable_shift = 3, + .irq_reg_cnt = AFE_IRQ_CNT2, + .irq_cnt_shift = 0, + .irq_en_shift = 1, + .irq_fs_shift = 8, + .irq_clr_shift = 1, + }, { + .name = "DAI", + .id = MTK_AFE_MEMIF_DAI, + .reg_ofs_base = AFE_DAI_BASE, + .reg_ofs_cur = AFE_DAI_CUR, + .fs_shift = 24, + .mono_shift = -1, + .enable_shift = 4, + .irq_reg_cnt = AFE_IRQ_CNT2, + .irq_cnt_shift = 20, + .irq_en_shift = 3, + .irq_fs_shift = 20, + .irq_clr_shift = 3, + }, { + .name = "AWB", + .id = MTK_AFE_MEMIF_AWB, + .reg_ofs_base = AFE_AWB_BASE, + .reg_ofs_cur = AFE_AWB_CUR, + .fs_shift = 12, + .mono_shift = 24, + .enable_shift = 6, + .irq_reg_cnt = AFE_IRQ_CNT7, + .irq_cnt_shift = 0, + .irq_en_shift = 14, + .irq_fs_shift = 24, + .irq_clr_shift = 6, + }, { + .name = "MOD_DAI", + .id = MTK_AFE_MEMIF_MOD_DAI, + .reg_ofs_base = AFE_MOD_PCM_BASE, + .reg_ofs_cur = AFE_MOD_PCM_CUR, + .fs_shift = 30, + .mono_shift = 30, + .enable_shift = 7, + .irq_reg_cnt = AFE_IRQ_CNT2, + .irq_cnt_shift = 20, + .irq_en_shift = 3, + .irq_fs_shift = 20, + .irq_clr_shift = 3, + }, { + .name = "HDMI", + .id = MTK_AFE_MEMIF_HDMI, + .reg_ofs_base = AFE_HDMI_OUT_BASE, + .reg_ofs_cur = AFE_HDMI_OUT_CUR, + .fs_shift = -1, + .mono_shift = -1, + .enable_shift = -1, + .irq_reg_cnt = AFE_IRQ_CNT5, + .irq_cnt_shift = 0, + .irq_en_shift = 12, + .irq_fs_shift = -1, + .irq_clr_shift = 4, + }, +}; + +static const struct regmap_config mtk_afe_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = AFE_ADDA2_TOP_CON0, + .cache_type = REGCACHE_NONE, +}; + +static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id) +{ + struct mtk_afe *afe = dev_id; + unsigned int reg_value, hw_ptr; + int i, ret; + + ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, ®_value); + if (ret) { + dev_err(afe->dev, "%s irq status err\n", __func__); + reg_value = AFE_IRQ_STATUS_BITS; + goto err_irq; + } + + for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) { + struct mtk_afe_memif *memif = &afe->memif[i]; + + 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); + } + +err_irq: + /* clear irq */ + regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS); + + return IRQ_HANDLED; +} + +static int mtk_afe_runtime_suspend(struct device *dev) +{ + struct mtk_afe *afe = dev_get_drvdata(dev); + + /* disable AFE clk */ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE); + + clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]); + clk_disable_unprepare(afe->clocks[MTK_CLK_BCK1]); + clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]); + clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); + clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]); + return 0; +} + +static int mtk_afe_runtime_resume(struct device *dev) +{ + struct mtk_afe *afe = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_INFRASYS_AUD]); + if (ret) + return ret; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); + if (ret) + goto err_infra; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD]); + if (ret) + goto err_top_aud_bus; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK0]); + if (ret) + goto err_top_aud; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK1]); + if (ret) + goto err_bck0; + + /* enable AFE clk */ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, AUD_TCON0_PDN_AFE, 0); + + /* set O3/O4 16bits */ + regmap_update_bits(afe->regmap, AFE_CONN_24BIT, + AFE_CONN_24BIT_O03 | AFE_CONN_24BIT_O04, 0); + + /* unmask all IRQs */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN, 0xff, 0xff); + return 0; + +err_bck0: + clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]); +err_top_aud: + clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]); +err_top_aud_bus: + clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); +err_infra: + clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]); + return ret; +} + +static int mtk_afe_init_audio_clk(struct mtk_afe *afe) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(aud_clks); i++) { + afe->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]); + if (IS_ERR(afe->clocks[i])) { + dev_err(afe->dev, "%s devm_clk_get %s fail\n", + __func__, aud_clks[i]); + return PTR_ERR(afe->clocks[i]); + } + } + clk_set_rate(afe->clocks[MTK_CLK_BCK0], 22579200); /* 22M */ + clk_set_rate(afe->clocks[MTK_CLK_BCK1], 24576000); /* 24M */ + return 0; +} + +static int mtk_afe_pcm_dev_probe(struct platform_device *pdev) +{ + int ret, i; + unsigned int irq_id; + struct mtk_afe *afe; + struct resource *res; + + afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + if (!afe) + return -ENOMEM; + + afe->dev = &pdev->dev; + + irq_id = platform_get_irq(pdev, 0); + if (!irq_id) { + dev_err(afe->dev, "np %s no irq\n", afe->dev->of_node->name); + return -ENXIO; + } + ret = devm_request_irq(afe->dev, irq_id, mtk_afe_irq_handler, + 0, "Afe_ISR_Handle", (void *)afe); + if (ret) { + dev_err(afe->dev, "could not request_irq\n"); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + afe->base_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(afe->base_addr)) + return PTR_ERR(afe->base_addr); + + afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, + &mtk_afe_regmap_config); + if (IS_ERR(afe->regmap)) + return PTR_ERR(afe->regmap); + + /* initial audio related clock */ + ret = mtk_afe_init_audio_clk(afe); + if (ret) { + dev_err(afe->dev, "mtk_afe_init_audio_clk fail\n"); + return ret; + } + + for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) + afe->memif[i].data = &memif_data[i]; + + platform_set_drvdata(pdev, afe); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = mtk_afe_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform); + if (ret) + goto err_pm_disable; + + ret = snd_soc_register_component(&pdev->dev, + &mtk_afe_pcm_dai_component, + mtk_afe_pcm_dais, + ARRAY_SIZE(mtk_afe_pcm_dais)); + if (ret) + goto err_platform; + + ret = snd_soc_register_component(&pdev->dev, + &mtk_afe_hdmi_dai_component, + mtk_afe_hdmi_dais, + ARRAY_SIZE(mtk_afe_hdmi_dais)); + if (ret) + goto err_comp; + + dev_info(&pdev->dev, "MTK AFE driver initialized.\n"); + return 0; + +err_comp: + snd_soc_unregister_component(&pdev->dev); +err_platform: + snd_soc_unregister_platform(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); + return ret; +} + +static int mtk_afe_pcm_dev_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id mtk_afe_pcm_dt_match[] = { + { .compatible = "mediatek,mt8173-afe-pcm", }, + { } +}; +MODULE_DEVICE_TABLE(of, mtk_afe_pcm_dt_match); + +static const struct dev_pm_ops mtk_afe_pm_ops = { + SET_RUNTIME_PM_OPS(mtk_afe_runtime_suspend, mtk_afe_runtime_resume, + NULL) +}; + +static struct platform_driver mtk_afe_pcm_driver = { + .driver = { + .name = "mtk-afe-pcm", + .owner = THIS_MODULE, + .of_match_table = mtk_afe_pcm_dt_match, + .pm = &mtk_afe_pm_ops, + }, + .probe = mtk_afe_pcm_dev_probe, + .remove = mtk_afe_pcm_dev_remove, +}; + +module_platform_driver(mtk_afe_pcm_driver); + +MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver"); +MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index fded99362d39..3bebfb1d3a6f 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c @@ -245,6 +245,8 @@ static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = { static const struct snd_soc_dapm_route audio_map[] = { {"Ext Spk", NULL, "HPLOUT"}, {"Ext Spk", NULL, "HPROUT"}, + {"Ext Spk", NULL, "HPLCOM"}, + {"Ext Spk", NULL, "HPRCOM"}, {"Headphone Jack", NULL, "LLOUT"}, {"Headphone Jack", NULL, "RLOUT"}, {"FM Transmitter", NULL, "LLOUT"}, @@ -288,15 +290,8 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = rtd->card; struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card); - - struct snd_soc_dapm_context *dapm = &codec->dapm; int err; - /* Set up NC codec pins */ - snd_soc_dapm_nc_pin(dapm, "MIC3L"); - snd_soc_dapm_nc_pin(dapm, "MIC3R"); - snd_soc_dapm_nc_pin(dapm, "LINE1R"); - err = tpa6130a2_add_controls(codec); if (err < 0) { dev_err(card->dev, "Failed to add TPA6130A2 controls\n"); @@ -383,6 +378,7 @@ static struct snd_soc_card rx51_sound_card = { .num_aux_devs = ARRAY_SIZE(rx51_aux_dev), .codec_conf = rx51_codec_conf, .num_configs = ARRAY_SIZE(rx51_codec_conf), + .fully_routed = true, .controls = aic34_rx51_controls, .num_controls = ARRAY_SIZE(aic34_rx51_controls), diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 938144c59e2b..807fedfa1c76 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -32,3 +32,12 @@ config SND_SOC_STORM help Say Y or M if you want add support for SoC audio on the Qualcomm Technologies IPQ806X-based Storm board. + +config SND_SOC_APQ8016_SBC + tristate "SoC Audio support for APQ8016 SBC platforms" + depends on SND_SOC_QCOM && (ARCH_QCOM || COMPILE_TEST) + select SND_SOC_LPASS_APQ8016 + help + Support for Qualcomm Technologies LPASS audio block in + APQ8016 SOC-based systems. + Say Y if you want to use audio devices on MI2S. diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index ac7630833fe5..79e5c50a8f71 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -11,5 +11,7 @@ obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o # Machine snd-soc-storm-objs := storm.o +snd-soc-apq8016-sbc-objs := apq8016_sbc.o obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o +obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c new file mode 100644 index 000000000000..1efdf0088ecd --- /dev/null +++ b/sound/soc/qcom/apq8016_sbc.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2015 The Linux Foundation. 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 and + * only 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/device.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <dt-bindings/sound/apq8016-lpass.h> + +struct apq8016_sbc_data { + void __iomem *mic_iomux; + void __iomem *spkr_iomux; + struct snd_soc_dai_link dai_link[]; /* dynamically allocated */ +}; + +#define MIC_CTRL_QUA_WS_SLAVE_SEL_10 BIT(17) +#define MIC_CTRL_TLMM_SCLK_EN BIT(1) +#define SPKR_CTL_PRI_WS_SLAVE_SEL_11 (BIT(17) | BIT(16)) + +static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_card *card = rtd->card; + struct apq8016_sbc_data *pdata = snd_soc_card_get_drvdata(card); + int rval = 0; + + switch (cpu_dai->id) { + case MI2S_PRIMARY: + writel(readl(pdata->spkr_iomux) | SPKR_CTL_PRI_WS_SLAVE_SEL_11, + pdata->spkr_iomux); + break; + + case MI2S_QUATERNARY: + /* Configure the Quat MI2S to TLMM */ + writel(readl(pdata->mic_iomux) | MIC_CTRL_QUA_WS_SLAVE_SEL_10 | + MIC_CTRL_TLMM_SCLK_EN, + pdata->mic_iomux); + break; + + default: + dev_err(card->dev, "unsupported cpu dai configuration\n"); + rval = -EINVAL; + break; + + } + + return rval; +} + +static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card) +{ + struct device *dev = card->dev; + struct snd_soc_dai_link *link; + struct device_node *np, *codec, *cpu, *node = dev->of_node; + struct apq8016_sbc_data *data; + int ret, num_links; + + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(dev, "Error parsing card name: %d\n", ret); + return ERR_PTR(ret); + } + + /* Populate links */ + num_links = of_get_child_count(node); + + /* Allocate the private data and the DAI link array */ + data = devm_kzalloc(dev, sizeof(*data) + sizeof(*link) * num_links, + GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); + + card->dai_link = &data->dai_link[0]; + card->num_links = num_links; + + link = data->dai_link; + + for_each_child_of_node(node, np) { + cpu = of_get_child_by_name(np, "cpu"); + codec = of_get_child_by_name(np, "codec"); + + if (!cpu || !codec) { + dev_err(dev, "Can't find cpu/codec DT node\n"); + return ERR_PTR(-EINVAL); + } + + link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0); + if (!link->cpu_of_node) { + dev_err(card->dev, "error getting cpu phandle\n"); + return ERR_PTR(-EINVAL); + } + + link->codec_of_node = of_parse_phandle(codec, "sound-dai", 0); + if (!link->codec_of_node) { + dev_err(card->dev, "error getting codec phandle\n"); + return ERR_PTR(-EINVAL); + } + + ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name); + if (ret) { + dev_err(card->dev, "error getting cpu dai name\n"); + return ERR_PTR(ret); + } + + ret = snd_soc_of_get_dai_name(codec, &link->codec_dai_name); + if (ret) { + dev_err(card->dev, "error getting codec dai name\n"); + return ERR_PTR(ret); + } + + link->platform_of_node = link->cpu_of_node; + /* For now we only support playback */ + link->playback_only = true; + + ret = of_property_read_string(np, "link-name", &link->name); + if (ret) { + dev_err(card->dev, "error getting codec dai_link name\n"); + return ERR_PTR(ret); + } + + link->stream_name = link->name; + link->init = apq8016_sbc_dai_init; + link++; + } + + return data; +} + +static int apq8016_sbc_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct snd_soc_card *card; + struct apq8016_sbc_data *data; + struct resource *res; + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + card->dev = dev; + data = apq8016_sbc_parse_of(card); + if (IS_ERR(data)) { + dev_err(&pdev->dev, "Error resolving dai links: %ld\n", + PTR_ERR(data)); + return PTR_ERR(data); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mic-iomux"); + data->mic_iomux = devm_ioremap_resource(dev, res); + if (IS_ERR(data->mic_iomux)) + return PTR_ERR(data->mic_iomux); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spkr-iomux"); + data->spkr_iomux = devm_ioremap_resource(dev, res); + if (IS_ERR(data->spkr_iomux)) + return PTR_ERR(data->spkr_iomux); + + platform_set_drvdata(pdev, data); + snd_soc_card_set_drvdata(card, data); + + return devm_snd_soc_register_card(&pdev->dev, card); +} + +static const struct of_device_id apq8016_sbc_device_id[] = { + { .compatible = "qcom,apq8016-sbc-sndcard" }, + {}, +}; +MODULE_DEVICE_TABLE(of, apq8016_sbc_device_id); + +static struct platform_driver apq8016_sbc_platform_driver = { + .driver = { + .name = "qcom-apq8016-sbc", + .of_match_table = of_match_ptr(apq8016_sbc_device_id), + }, + .probe = apq8016_sbc_platform_probe, +}; +module_platform_driver(apq8016_sbc_platform_driver); + +MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org"); +MODULE_DESCRIPTION("APQ8016 ASoC Machine Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c index b8bd296190ad..2d833bffdba0 100644 --- a/sound/soc/qcom/storm.c +++ b/sound/soc/qcom/storm.c @@ -69,11 +69,6 @@ static struct snd_soc_dai_link storm_dai_link = { .ops = &storm_soc_ops, }; -static struct snd_soc_card storm_soc_card = { - .name = "ipq806x-storm", - .dev = NULL, -}; - static int storm_parse_of(struct snd_soc_card *card) { struct snd_soc_dai_link *dai_link = card->dai_link; @@ -99,14 +94,13 @@ static int storm_parse_of(struct snd_soc_card *card) static int storm_platform_probe(struct platform_device *pdev) { - struct snd_soc_card *card = &storm_soc_card; + struct snd_soc_card *card; int ret; - if (card->dev) { - dev_err(&pdev->dev, "%s() error, existing soundcard\n", - __func__); - return -ENODEV; - } + card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + card->dev = &pdev->dev; platform_set_drvdata(pdev, card); @@ -128,16 +122,12 @@ static int storm_platform_probe(struct platform_device *pdev) } ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret == -EPROBE_DEFER) { - card->dev = NULL; - return ret; - } else if (ret) { + if (ret) dev_err(&pdev->dev, "%s() error registering soundcard: %d\n", __func__, ret); - return ret; - } - return 0; + return ret; + } #ifdef CONFIG_OF diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index d460d2aa82ee..f1e5920654f6 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -137,15 +137,17 @@ char *rsnd_mod_name(struct rsnd_mod *mod) return mod->ops->name; } -struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod) +struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { if (!mod || !mod->ops || !mod->ops->dma_req) return NULL; - return mod->ops->dma_req(mod); + return mod->ops->dma_req(io, mod); } -int rsnd_mod_init(struct rsnd_mod *mod, +int rsnd_mod_init(struct rsnd_priv *priv, + struct rsnd_mod *mod, struct rsnd_mod_ops *ops, struct clk *clk, enum rsnd_mod_type type, @@ -160,6 +162,7 @@ int rsnd_mod_init(struct rsnd_mod *mod, mod->ops = ops; mod->type = type; mod->clk = clk; + mod->priv = priv; return ret; } @@ -170,10 +173,31 @@ void rsnd_mod_quit(struct rsnd_mod *mod) clk_unprepare(mod->clk); } -int rsnd_mod_is_working(struct rsnd_mod *mod) +void rsnd_mod_interrupt(struct rsnd_mod *mod, + void (*callback)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io)) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(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) { + + for (i = 0; i < RSND_MOD_MAX; i++) { + io = &rdai->playback; + if (mod == io->mod[i]) + callback(mod, io); + + io = &rdai->capture; + if (mod == io->mod[i]) + callback(mod, io); + } + } +} +int rsnd_io_is_working(struct rsnd_dai_stream *io) +{ /* see rsnd_dai_stream_init/quit() */ return !!io->substream; } @@ -181,10 +205,9 @@ int rsnd_mod_is_working(struct rsnd_mod *mod) /* * settting function */ -u32 rsnd_get_adinr(struct rsnd_mod *mod) +u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); u32 adinr = runtime->channels; @@ -207,26 +230,31 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod) /* * rsnd_dai functions */ -#define __rsnd_mod_call(mod, func, param...) \ +#define __rsnd_mod_call(mod, io, func, param...) \ ({ \ struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct device *dev = rsnd_priv_to_dev(priv); \ - u32 mask = (1 << __rsnd_mod_shift_##func) & ~(1 << 31); \ - u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func; \ + u32 mask = 0xF << __rsnd_mod_shift_##func; \ + u8 val = (mod->status >> __rsnd_mod_shift_##func) & 0xF; \ + u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ int ret = 0; \ - if ((mod->status & mask) == call) { \ - dev_dbg(dev, "%s[%d] %s\n", \ - rsnd_mod_name(mod), rsnd_mod_id(mod), #func); \ - ret = (mod)->ops->func(mod, param); \ - mod->status = (mod->status & ~mask) | (~call & mask); \ + int called = 0; \ + if (val == __rsnd_mod_call_##func) { \ + called = 1; \ + ret = (mod)->ops->func(mod, io, param); \ + mod->status = (mod->status & ~mask) + \ + (add << __rsnd_mod_shift_##func); \ } \ + dev_dbg(dev, "%s[%d] 0x%08x %s\n", \ + rsnd_mod_name(mod), rsnd_mod_id(mod), mod->status, \ + called ? #func : ""); \ ret; \ }) -#define rsnd_mod_call(mod, func, param...) \ +#define rsnd_mod_call(mod, io, func, param...) \ (!(mod) ? -ENODEV : \ !((mod)->ops->func) ? 0 : \ - __rsnd_mod_call(mod, func, param)) + __rsnd_mod_call(mod, io, func, param)) #define rsnd_dai_call(fn, io, param...) \ ({ \ @@ -236,7 +264,7 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod) mod = (io)->mod[i]; \ if (!mod) \ continue; \ - ret = rsnd_mod_call(mod, fn, param); \ + ret = rsnd_mod_call(mod, io, fn, param); \ if (ret < 0) \ break; \ } \ @@ -260,7 +288,6 @@ static int rsnd_dai_connect(struct rsnd_mod *mod, } io->mod[mod->type] = mod; - mod->io = io; return 0; } @@ -268,7 +295,6 @@ static int rsnd_dai_connect(struct rsnd_mod *mod, static void rsnd_dai_disconnect(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { - mod->io = NULL; io->mod[mod->type] = NULL; } @@ -302,7 +328,7 @@ int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional) return pos; } -void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte) +bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte) { io->byte_pos += byte; @@ -319,8 +345,24 @@ void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte) io->next_period_byte = io->byte_per_period; } - snd_pcm_period_elapsed(substream); + return true; } + + return false; +} + +void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io) +{ + struct snd_pcm_substream *substream = io->substream; + + /* + * this function should be called... + * + * - if rsnd_dai_pointer_update() returns true + * - without spin lock + */ + + snd_pcm_period_elapsed(substream); } static void rsnd_dai_stream_init(struct rsnd_dai_stream *io, @@ -834,16 +876,18 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, } if (change) - cfg->update(mod); + cfg->update(cfg->io, mod); return change; } static int __rsnd_kctrl_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, struct rsnd_kctrl_cfg *cfg, - void (*update)(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; @@ -872,6 +916,7 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod, cfg->update = update; cfg->card = card; cfg->kctrl = kctrl; + cfg->io = io; return 0; } @@ -882,36 +927,42 @@ void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg) } int rsnd_kctrl_new_m(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), struct rsnd_kctrl_cfg_m *_cfg, u32 max) { _cfg->cfg.max = max; _cfg->cfg.size = RSND_DVC_CHANNELS; _cfg->cfg.val = _cfg->val; - return __rsnd_kctrl_new(mod, rtd, name, &_cfg->cfg, update); + return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update); } int rsnd_kctrl_new_s(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), struct rsnd_kctrl_cfg_s *_cfg, u32 max) { _cfg->cfg.max = max; _cfg->cfg.size = 1; _cfg->cfg.val = &_cfg->val; - return __rsnd_kctrl_new(mod, rtd, name, &_cfg->cfg, update); + return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update); } int rsnd_kctrl_new_e(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, struct rsnd_kctrl_cfg_s *_cfg, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), const char * const *texts, u32 max) { @@ -919,7 +970,7 @@ int rsnd_kctrl_new_e(struct rsnd_mod *mod, _cfg->cfg.size = 1; _cfg->cfg.val = &_cfg->val; _cfg->cfg.texts = texts; - return __rsnd_kctrl_new(mod, rtd, name, &_cfg->cfg, update); + return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update); } /* diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 144308f15fb3..d306e298c63d 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -32,11 +32,12 @@ struct rsnd_dma_ctrl { /* * Audio DMAC */ -static void rsnd_dmaen_complete(void *data) +static void __rsnd_dmaen_complete(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - struct rsnd_dma *dma = (struct rsnd_dma *)data; - struct rsnd_mod *mod = rsnd_dma_to_mod(dma); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + bool elapsed = false; + unsigned long flags; /* * Renesas sound Gen1 needs 1 DMAC, @@ -49,23 +50,36 @@ static void rsnd_dmaen_complete(void *data) * rsnd_dai_pointer_update() will be called twice, * ant it will breaks io->byte_pos */ + spin_lock_irqsave(&priv->lock, flags); + + if (rsnd_io_is_working(io)) + elapsed = rsnd_dai_pointer_update(io, io->byte_per_period); - rsnd_dai_pointer_update(io, io->byte_per_period); + spin_unlock_irqrestore(&priv->lock, flags); + + if (elapsed) + rsnd_dai_period_elapsed(io); } -static void rsnd_dmaen_stop(struct rsnd_dma *dma) +static void rsnd_dmaen_complete(void *data) +{ + struct rsnd_mod *mod = data; + + rsnd_mod_interrupt(mod, __rsnd_dmaen_complete); +} + +static void rsnd_dmaen_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); dmaengine_terminate_all(dmaen->chan); } -static void rsnd_dmaen_start(struct rsnd_dma *dma) +static void rsnd_dmaen_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { 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 rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_substream *substream = io->substream; struct device *dev = rsnd_priv_to_dev(priv); struct dma_async_tx_descriptor *desc; @@ -84,7 +98,7 @@ static void rsnd_dmaen_start(struct rsnd_dma *dma) } desc->callback = rsnd_dmaen_complete; - desc->callback_param = dma; + desc->callback_param = mod; if (dmaengine_submit(desc) < 0) { dev_err(dev, "dmaengine_submit() fail\n"); @@ -115,7 +129,8 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, return chan; } -static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_mod *mod_from, +static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) { if ((!mod_from && !mod_to) || @@ -123,19 +138,19 @@ static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_mod *mod_from, return NULL; if (mod_from) - return rsnd_mod_dma_req(mod_from); + return rsnd_mod_dma_req(io, mod_from); else - return rsnd_mod_dma_req(mod_to); + return rsnd_mod_dma_req(io, mod_to); } -static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, +static int rsnd_dmaen_init(struct rsnd_dai_stream *io, + struct rsnd_dma *dma, int id, struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) { struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); + struct rsnd_priv *priv = rsnd_io_to_priv(io); struct device *dev = rsnd_priv_to_dev(priv); struct dma_slave_config cfg = {}; - struct rsnd_mod *mod = rsnd_dma_to_mod(dma); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int is_play = rsnd_io_is_play(io); int ret; @@ -145,7 +160,7 @@ static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, } if (dev->of_node) { - dmaen->chan = rsnd_dmaen_request_channel(mod_from, mod_to); + dmaen->chan = rsnd_dmaen_request_channel(io, mod_from, mod_to); } else { dma_cap_mask_t mask; @@ -177,7 +192,7 @@ static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, return 0; rsnd_dma_init_err: - rsnd_dma_quit(dma); + rsnd_dma_quit(io, dma); rsnd_dma_channel_err: /* @@ -189,7 +204,7 @@ rsnd_dma_channel_err: return -EAGAIN; } -static void rsnd_dmaen_quit(struct rsnd_dma *dma) +static void rsnd_dmaen_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); @@ -238,9 +253,9 @@ static const u8 gen2_id_table_cmd[] = { 0x38, /* SCU_CMD1 */ }; -static u32 rsnd_dmapp_get_id(struct rsnd_mod *mod) +static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); @@ -268,11 +283,12 @@ static u32 rsnd_dmapp_get_id(struct rsnd_mod *mod) return entry[id]; } -static u32 rsnd_dmapp_get_chcr(struct rsnd_mod *mod_from, +static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) { - return (rsnd_dmapp_get_id(mod_from) << 24) + - (rsnd_dmapp_get_id(mod_to) << 16); + return (rsnd_dmapp_get_id(io, mod_from) << 24) + + (rsnd_dmapp_get_id(io, mod_to) << 16); } #define rsnd_dmapp_addr(dmac, dma, reg) \ @@ -299,7 +315,7 @@ static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg) return ioread32(rsnd_dmapp_addr(dmac, dma, reg)); } -static void rsnd_dmapp_stop(struct rsnd_dma *dma) +static void rsnd_dmapp_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { int i; @@ -312,7 +328,7 @@ static void rsnd_dmapp_stop(struct rsnd_dma *dma) } } -static void rsnd_dmapp_start(struct rsnd_dma *dma) +static void rsnd_dmapp_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); @@ -321,19 +337,21 @@ static void rsnd_dmapp_start(struct rsnd_dma *dma) rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR); } -static int rsnd_dmapp_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, +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) { struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(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); dmapp->dmapp_id = dmac->dmapp_num; - dmapp->chcr = rsnd_dmapp_get_chcr(mod_from, mod_to) | PDMACHCR_DE; + dmapp->chcr = rsnd_dmapp_get_chcr(io, mod_from, mod_to) | PDMACHCR_DE; dmac->dmapp_num++; - rsnd_dmapp_stop(dma); + 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); @@ -386,12 +404,12 @@ static struct rsnd_dma_ops rsnd_dmapp_ops = { #define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i)) static dma_addr_t -rsnd_gen2_dma_addr(struct rsnd_priv *priv, +rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, struct rsnd_mod *mod, int is_play, int is_from) { + struct rsnd_priv *priv = rsnd_io_to_priv(io); struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI); phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU); int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod); @@ -438,7 +456,7 @@ rsnd_gen2_dma_addr(struct rsnd_priv *priv, dev_err(dev, "DVC is selected without SRC\n"); /* use SSIU or SSI ? */ - if (is_ssi && rsnd_ssi_use_busif(mod)) + if (is_ssi && rsnd_ssi_use_busif(io, mod)) is_ssi++; return (is_from) ? @@ -446,10 +464,12 @@ rsnd_gen2_dma_addr(struct rsnd_priv *priv, dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr; } -static dma_addr_t rsnd_dma_addr(struct rsnd_priv *priv, +static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io, struct rsnd_mod *mod, int is_play, int is_from) { + struct rsnd_priv *priv = rsnd_io_to_priv(io); + /* * gen1 uses default DMA addr */ @@ -459,17 +479,17 @@ static dma_addr_t rsnd_dma_addr(struct rsnd_priv *priv, if (!mod) return 0; - return rsnd_gen2_dma_addr(priv, mod, is_play, is_from); + return rsnd_gen2_dma_addr(io, mod, is_play, is_from); } #define MOD_MAX 4 /* MEM/SSI/SRC/DVC */ static void rsnd_dma_of_path(struct rsnd_dma *dma, + 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_dai_stream *io = rsnd_mod_to_io(this); struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); @@ -524,17 +544,17 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma, } } -void rsnd_dma_stop(struct rsnd_dma *dma) +void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { - dma->ops->stop(dma); + dma->ops->stop(io, dma); } -void rsnd_dma_start(struct rsnd_dma *dma) +void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { - dma->ops->start(dma); + dma->ops->start(io, dma); } -void rsnd_dma_quit(struct rsnd_dma *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); @@ -543,15 +563,14 @@ void rsnd_dma_quit(struct rsnd_dma *dma) if (!dmac) return; - dma->ops->quit(dma); + dma->ops->quit(io, dma); } -int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id) +int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id) { - struct rsnd_mod *mod = rsnd_dma_to_mod(dma); struct rsnd_mod *mod_from; struct rsnd_mod *mod_to; - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); int is_play = rsnd_io_is_play(io); @@ -564,10 +583,10 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id) if (!dmac) return -EAGAIN; - rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to); + rsnd_dma_of_path(dma, io, is_play, &mod_from, &mod_to); - dma->src_addr = rsnd_dma_addr(priv, mod_from, is_play, 1); - dma->dst_addr = rsnd_dma_addr(priv, mod_to, is_play, 0); + 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) @@ -579,7 +598,7 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id) if (rsnd_is_gen1(priv)) dma->ops = &rsnd_dmaen_ops; - return dma->ops->init(priv, dma, id, mod_from, mod_to); + return dma->ops->init(io, dma, id, mod_from, mod_to); } int rsnd_dma_probe(struct platform_device *pdev, diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index e5fcb062ad77..36fc020cbc18 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -63,7 +63,8 @@ static const char * const dvc_ramp_rate[] = { "0.125 dB/8192 steps", /* 10111 */ }; -static void rsnd_dvc_volume_update(struct rsnd_mod *mod) +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 val[RSND_DVC_CHANNELS]; @@ -120,6 +121,7 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod) } static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); @@ -134,9 +136,9 @@ static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, } static int rsnd_dvc_init(struct rsnd_mod *dvc_mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(dvc_mod); struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); struct device *dev = rsnd_priv_to_dev(priv); int dvc_id = rsnd_mod_id(dvc_mod); @@ -168,10 +170,10 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod, rsnd_mod_write(dvc_mod, DVC_DVUIR, 1); - rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod)); + rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod, io)); /* ch0/ch1 Volume */ - rsnd_dvc_volume_update(dvc_mod); + rsnd_dvc_volume_update(io, dvc_mod); rsnd_mod_write(dvc_mod, DVC_DVUIR, 0); @@ -181,6 +183,7 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod, } static int rsnd_dvc_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { rsnd_mod_hw_stop(mod); @@ -189,6 +192,7 @@ static int rsnd_dvc_quit(struct rsnd_mod *mod, } static int rsnd_dvc_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { rsnd_mod_write(mod, CMD_CTRL, 0x10); @@ -197,6 +201,7 @@ static int rsnd_dvc_start(struct rsnd_mod *mod, } 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); @@ -205,15 +210,15 @@ static int rsnd_dvc_stop(struct rsnd_mod *mod, } static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); int is_play = rsnd_io_is_play(io); int ret; /* Volume */ - ret = rsnd_kctrl_new_m(mod, rtd, + ret = rsnd_kctrl_new_m(mod, io, rtd, is_play ? "DVC Out Playback Volume" : "DVC In Capture Volume", rsnd_dvc_volume_update, @@ -222,7 +227,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, return ret; /* Mute */ - ret = rsnd_kctrl_new_m(mod, rtd, + ret = rsnd_kctrl_new_m(mod, io, rtd, is_play ? "DVC Out Mute Switch" : "DVC In Mute Switch", rsnd_dvc_volume_update, @@ -231,7 +236,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, return ret; /* Ramp */ - ret = rsnd_kctrl_new_s(mod, rtd, + ret = rsnd_kctrl_new_s(mod, io, rtd, is_play ? "DVC Out Ramp Switch" : "DVC In Ramp Switch", rsnd_dvc_volume_update, @@ -239,7 +244,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, if (ret < 0) return ret; - ret = rsnd_kctrl_new_e(mod, rtd, + ret = rsnd_kctrl_new_e(mod, io, rtd, is_play ? "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate", &dvc->rup, @@ -248,7 +253,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, if (ret < 0) return ret; - ret = rsnd_kctrl_new_e(mod, rtd, + ret = rsnd_kctrl_new_e(mod, io, rtd, is_play ? "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate", &dvc->rdown, @@ -261,7 +266,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, return 0; } -static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_mod *mod) +static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); @@ -366,7 +372,7 @@ int rsnd_dvc_probe(struct platform_device *pdev, dvc->info = &info->dvc_info[i]; - ret = rsnd_mod_init(&dvc->mod, &rsnd_dvc_ops, + ret = rsnd_mod_init(priv, &dvc->mod, &rsnd_dvc_ops, clk, RSND_MOD_DVC, i); if (ret) return ret; diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 03ff071d012f..09fcc54a8ee0 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -165,18 +165,18 @@ void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 data); void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data); -u32 rsnd_get_adinr(struct rsnd_mod *mod); +u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io); /* * R-Car DMA */ struct rsnd_dma; struct rsnd_dma_ops { - void (*start)(struct rsnd_dma *dma); - void (*stop)(struct rsnd_dma *dma); - int (*init)(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, + 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_dma *dma); + void (*quit)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); }; struct rsnd_dmaen { @@ -200,10 +200,10 @@ struct rsnd_dma { #define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en) #define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp) -void rsnd_dma_start(struct rsnd_dma *dma); -void rsnd_dma_stop(struct rsnd_dma *dma); -int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id); -void rsnd_dma_quit(struct rsnd_dma *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); @@ -224,25 +224,35 @@ enum rsnd_mod_type { struct rsnd_mod_ops { char *name; - struct dma_chan* (*dma_req)(struct rsnd_mod *mod); + struct dma_chan* (*dma_req)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod); int (*probe)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*remove)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*init)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*quit)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*start)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*stop)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*pcm_new)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd); int (*hw_params)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params); int (*fallback)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); }; @@ -252,32 +262,43 @@ struct rsnd_mod { enum rsnd_mod_type type; struct rsnd_mod_ops *ops; struct rsnd_dma dma; - struct rsnd_dai_stream *io; + struct rsnd_priv *priv; struct clk *clk; u32 status; }; /* * status * - * bit - * 0 0: probe 1: remove - * 1 0: init 1: quit - * 2 0: start 1: stop - * 3 0: pcm_new - * 4 0: fallback + * 0xH0000CBA + * + * A 0: probe 1: remove + * B 0: init 1: quit + * C 0: start 1: stop * - * 31 bit is always called (see __rsnd_mod_call) - * 31 0: hw_params + * H is always called (see __rsnd_mod_call) + * H 0: pcm_new + * H 0: fallback + * H 0: hw_params */ #define __rsnd_mod_shift_probe 0 #define __rsnd_mod_shift_remove 0 -#define __rsnd_mod_shift_init 1 -#define __rsnd_mod_shift_quit 1 -#define __rsnd_mod_shift_start 2 -#define __rsnd_mod_shift_stop 2 -#define __rsnd_mod_shift_pcm_new 3 -#define __rsnd_mod_shift_fallback 4 -#define __rsnd_mod_shift_hw_params 31 /* always called */ +#define __rsnd_mod_shift_init 4 +#define __rsnd_mod_shift_quit 4 +#define __rsnd_mod_shift_start 8 +#define __rsnd_mod_shift_stop 8 +#define __rsnd_mod_shift_pcm_new 28 /* always called */ +#define __rsnd_mod_shift_fallback 28 /* always called */ +#define __rsnd_mod_shift_hw_params 28 /* always called */ + +#define __rsnd_mod_add_probe 1 +#define __rsnd_mod_add_remove -1 +#define __rsnd_mod_add_init 1 +#define __rsnd_mod_add_quit -1 +#define __rsnd_mod_add_start 1 +#define __rsnd_mod_add_stop -1 +#define __rsnd_mod_add_pcm_new 0 +#define __rsnd_mod_add_fallback 0 +#define __rsnd_mod_add_hw_params 0 #define __rsnd_mod_call_probe 0 #define __rsnd_mod_call_remove 1 @@ -289,22 +310,25 @@ struct rsnd_mod { #define __rsnd_mod_call_fallback 0 #define __rsnd_mod_call_hw_params 0 -#define rsnd_mod_to_priv(mod) (rsnd_io_to_priv(rsnd_mod_to_io(mod))) +#define rsnd_mod_to_priv(mod) ((mod)->priv) #define rsnd_mod_to_dma(mod) (&(mod)->dma) -#define rsnd_mod_to_io(mod) ((mod)->io) #define rsnd_mod_id(mod) ((mod)->id) #define rsnd_mod_hw_start(mod) clk_enable((mod)->clk) #define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk) -int rsnd_mod_init(struct rsnd_mod *mod, +int rsnd_mod_init(struct rsnd_priv *priv, + struct rsnd_mod *mod, struct rsnd_mod_ops *ops, struct clk *clk, enum rsnd_mod_type type, int id); void rsnd_mod_quit(struct rsnd_mod *mod); char *rsnd_mod_name(struct rsnd_mod *mod); -int rsnd_mod_is_working(struct rsnd_mod *mod); -struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod); +struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod); +void rsnd_mod_interrupt(struct rsnd_mod *mod, + void (*callback)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io)); /* * R-Car sound DAI @@ -329,7 +353,7 @@ struct rsnd_dai_stream { #define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io) #define rsnd_io_to_runtime(io) ((io)->substream ? \ (io)->substream->runtime : NULL) - +int rsnd_io_is_working(struct rsnd_dai_stream *io); struct rsnd_dai { char name[RSND_DAI_NAME_SIZE]; @@ -355,7 +379,8 @@ struct rsnd_dai { struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id); -void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); +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); /* @@ -459,7 +484,8 @@ struct rsnd_kctrl_cfg { unsigned int size; u32 *val; const char * const *texts; - void (*update)(struct rsnd_mod *mod); + void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod); + struct rsnd_dai_stream *io; struct snd_card *card; struct snd_kcontrol *kctrl; }; @@ -479,22 +505,28 @@ void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg); #define rsnd_kctrl_remove(_cfg) _rsnd_kctrl_remove(&((_cfg).cfg)) int rsnd_kctrl_new_m(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), struct rsnd_kctrl_cfg_m *_cfg, u32 max); int rsnd_kctrl_new_s(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), struct rsnd_kctrl_cfg_s *_cfg, u32 max); int rsnd_kctrl_new_e(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, struct rsnd_kctrl_cfg_s *_cfg, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), const char * const *texts, u32 max); @@ -511,8 +543,10 @@ 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); +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); @@ -529,7 +563,7 @@ void rsnd_ssi_remove(struct platform_device *pdev, struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); -int rsnd_ssi_use_busif(struct rsnd_mod *mod); +int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod); /* * R-Car DVC diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c index 050b0dbcee65..8caca2e180c3 100644 --- a/sound/soc/sh/rcar/rsrc-card.c +++ b/sound/soc/sh/rcar/rsrc-card.c @@ -45,61 +45,50 @@ static const struct of_device_id rsrc_card_of_match[] = { }; MODULE_DEVICE_TABLE(of, rsrc_card_of_match); +#define DAI_NAME_NUM 32 struct rsrc_card_dai { - const char *name; unsigned int fmt; unsigned int sysclk; struct clk *clk; + char dai_name[DAI_NAME_NUM]; }; -#define RSRC_FB_NUM 2 /* FE/BE */ #define IDX_CPU 0 #define IDX_CODEC 1 struct rsrc_card_priv { struct snd_soc_card snd_card; - struct rsrc_card_dai_props { - struct rsrc_card_dai cpu_dai; - struct rsrc_card_dai codec_dai; - } dai_props[RSRC_FB_NUM]; struct snd_soc_codec_conf codec_conf; - struct snd_soc_dai_link dai_link[RSRC_FB_NUM]; + struct rsrc_card_dai *dai_props; + struct snd_soc_dai_link *dai_link; + int dai_num; u32 convert_rate; }; #define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev) -#define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i) -#define rsrc_priv_to_props(priv, i) ((priv)->dai_props + i) +#define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i)) +#define rsrc_priv_to_props(priv, i) ((priv)->dai_props + (i)) #define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data) 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_props *dai_props = - &priv->dai_props[rtd - rtd->card->rtd]; + struct rsrc_card_dai *dai_props = + rsrc_priv_to_props(priv, rtd - rtd->card->rtd); int ret; - ret = clk_prepare_enable(dai_props->cpu_dai.clk); - if (ret) - return ret; - - ret = clk_prepare_enable(dai_props->codec_dai.clk); - if (ret) - clk_disable_unprepare(dai_props->cpu_dai.clk); - return ret; + return clk_prepare_enable(dai_props->clk); } 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_props *dai_props = - &priv->dai_props[rtd - rtd->card->rtd]; - - clk_disable_unprepare(dai_props->cpu_dai.clk); + struct rsrc_card_dai *dai_props = + rsrc_priv_to_props(priv, rtd - rtd->card->rtd); - clk_disable_unprepare(dai_props->codec_dai.clk); + clk_disable_unprepare(dai_props->clk); } static struct snd_soc_ops rsrc_card_ops = { @@ -107,21 +96,31 @@ static struct snd_soc_ops rsrc_card_ops = { .shutdown = rsrc_card_shutdown, }; -static int __rsrc_card_dai_init(struct snd_soc_dai *dai, - struct rsrc_card_dai *set) +static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd) { + struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); + 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 ret; - if (set->fmt) { - ret = snd_soc_dai_set_fmt(dai, set->fmt); + dai_link = rsrc_priv_to_link(priv, num); + dai_props = rsrc_priv_to_props(priv, num); + dai = dai_link->dynamic ? + rtd->cpu_dai : + rtd->codec_dai; + + if (dai_props->fmt) { + ret = snd_soc_dai_set_fmt(dai, dai_props->fmt); if (ret && ret != -ENOTSUPP) { dev_err(dai->dev, "set_fmt error\n"); goto err; } } - if (set->sysclk) { - ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); + 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_sysclk error\n"); goto err; @@ -134,27 +133,6 @@ err: return ret; } -static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd) -{ - struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *codec = rtd->codec_dai; - struct snd_soc_dai *cpu = rtd->cpu_dai; - struct rsrc_card_dai_props *dai_props; - int num, ret; - - num = rtd - rtd->card->rtd; - dai_props = &priv->dai_props[num]; - ret = __rsrc_card_dai_init(codec, &dai_props->codec_dai); - if (ret < 0) - return ret; - - ret = __rsrc_card_dai_init(cpu, &dai_props->cpu_dai); - if (ret < 0) - return ret; - - return 0; -} - static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -170,40 +148,47 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static int -rsrc_card_sub_parse_of(struct rsrc_card_priv *priv, - struct device_node *np, - struct rsrc_card_dai *dai, - struct snd_soc_dai_link *dai_link, - int *args_count) +static int rsrc_card_parse_daifmt(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); - const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); - struct of_phandle_args args; - struct device_node **p_node; - struct clk *clk; - const char **dai_name; - const char **name; - u32 val; - int ret; + 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; - if (args_count) { - p_node = &dai_link->cpu_of_node; - dai_name = &dai_link->cpu_dai_name; - name = &dai_link->cpu_name; - } else { - p_node = &dai_link->codec_of_node; - dai_name = &dai_link->codec_dai_name; - name = &dai_link->codec_name; - } + daifmt = snd_soc_of_parse_daifmt(node, NULL, + &bitclkmaster, &framemaster); + daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; - if (!np) { - /* use snd-soc-dummy */ - *p_node = NULL; - *dai_name = "snd-soc-dummy-dai"; - *name = "snd-soc-dummy"; - return 0; - } + if (!bitclkmaster && !framemaster) + return -EINVAL; + + if (codec == bitclkmaster) + daifmt |= (codec == framemaster) ? + SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS; + else + 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); + + return 0; +} + +static int rsrc_card_parse_links(struct device_node *np, + struct rsrc_card_priv *priv, + int idx, bool is_fe) +{ + 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); + struct of_phandle_args args; + int ret; /* * Get node via "sound-dai = <&phandle port>" @@ -214,31 +199,82 @@ rsrc_card_sub_parse_of(struct rsrc_card_priv *priv, if (ret) return ret; - *p_node = args.np; + if (is_fe) { + /* BE is dummy */ + dai_link->codec_of_node = NULL; + dai_link->codec_dai_name = "snd-soc-dummy-dai"; + dai_link->codec_name = "snd-soc-dummy"; + + /* FE settings */ + 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); + + /* set dai_name */ + snprintf(dai_props->dai_name, DAI_NAME_NUM, "fe.%s", + dai_link->cpu_dai_name); + + /* + * In soc_bind_dai_link() will check cpu name after + * of_node matching if dai_link has cpu_dai_name. + * but, it will never match if name was created by + * fmt_single_name() remove cpu_dai_name if cpu_args + * was 0. See: + * fmt_single_name() + * fmt_multiple_name() + */ + if (!args.args_count) + dai_link->cpu_dai_name = NULL; + } else { + struct device *dev = rsrc_priv_to_dev(priv); + const struct rsrc_card_of_data *of_data; - /* Get dai->name */ - ret = snd_soc_of_get_dai_name(np, dai_name); - if (ret < 0) - return ret; + of_data = rsrc_dev_to_of_data(dev); - /* - * FIXME - * - * rsrc assumes DPCM playback/capture - */ - dai_link->dpcm_playback = 1; - dai_link->dpcm_capture = 1; + /* FE is dummy */ + dai_link->cpu_of_node = NULL; + dai_link->cpu_dai_name = "snd-soc-dummy-dai"; + dai_link->cpu_name = "snd-soc-dummy"; - if (args_count) { - *args_count = args.args_count; - dai_link->dynamic = 1; - dai_link->dpcm_merged_format = 1; - } else { - dai_link->no_pcm = 1; - priv->codec_conf.of_node = (*p_node); - priv->codec_conf.name_prefix = of_data->prefix; + /* BE settings */ + 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); + + /* additional name prefix */ + priv->codec_conf.of_node = dai_link->codec_of_node; + priv->codec_conf.name_prefix = of_data->prefix; + + /* set dai_name */ + snprintf(dai_props->dai_name, DAI_NAME_NUM, "be.%s", + dai_link->codec_dai_name); } + /* Simple Card assumes platform == cpu */ + dai_link->platform_of_node = dai_link->cpu_of_node; + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + dai_link->name = dai_props->dai_name; + dai_link->stream_name = dai_props->dai_name; + dai_link->ops = &rsrc_card_ops; + dai_link->init = rsrc_card_dai_init; + + return 0; +} + +static int rsrc_card_parse_clk(struct device_node *np, + struct rsrc_card_priv *priv, + int idx, bool is_fe) +{ + 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); + struct clk *clk; + struct device_node *of_np = is_fe ? dai_link->cpu_of_node : + dai_link->codec_of_node; + u32 val; + /* * Parse dai->sysclk come from "clocks = <&xxx>" * (if system has common clock) @@ -247,173 +283,92 @@ rsrc_card_sub_parse_of(struct rsrc_card_priv *priv, */ if (of_property_read_bool(np, "clocks")) { clk = of_clk_get(np, 0); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - return ret; - } + if (IS_ERR(clk)) + return PTR_ERR(clk); - dai->sysclk = clk_get_rate(clk); - dai->clk = clk; + dai_props->sysclk = clk_get_rate(clk); + dai_props->clk = clk; } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) { - dai->sysclk = val; + dai_props->sysclk = val; } else { - clk = of_clk_get(args.np, 0); + clk = of_clk_get(of_np, 0); if (!IS_ERR(clk)) - dai->sysclk = clk_get_rate(clk); + dai_props->sysclk = clk_get_rate(clk); } return 0; } -static int rsrc_card_parse_daifmt(struct device_node *node, - struct rsrc_card_priv *priv, - struct device_node *codec, - int idx) -{ - struct device_node *bitclkmaster = NULL; - struct device_node *framemaster = NULL; - struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx); - struct rsrc_card_dai *cpu_dai = &dai_props->cpu_dai; - struct rsrc_card_dai *codec_dai = &dai_props->codec_dai; - unsigned int daifmt; - - daifmt = snd_soc_of_parse_daifmt(node, NULL, - &bitclkmaster, &framemaster); - daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; - - if (!bitclkmaster && !framemaster) - return -EINVAL; - - if (codec == bitclkmaster) - daifmt |= (codec == framemaster) ? - SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS; - else - daifmt |= (codec == framemaster) ? - SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; - - cpu_dai->fmt = daifmt; - codec_dai->fmt = daifmt; - - of_node_put(bitclkmaster); - of_node_put(framemaster); - - return 0; -} - static int rsrc_card_dai_link_of(struct device_node *node, + struct device_node *np, struct rsrc_card_priv *priv, int idx) { 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_props *dai_props = rsrc_priv_to_props(priv, idx); - struct device_node *cpu = NULL; - struct device_node *codec = NULL; - char *name; - char prop[128]; - int ret, cpu_args; - - cpu = of_get_child_by_name(node, "cpu"); - codec = of_get_child_by_name(node, "codec"); - - if (!cpu || !codec) { - ret = -EINVAL; - dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); - goto dai_link_of_err; - } + struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx); + bool is_fe = false; + int ret; - ret = rsrc_card_parse_daifmt(node, priv, codec, idx); - if (ret < 0) - goto dai_link_of_err; + if (0 == strcmp(np->name, "cpu")) + is_fe = true; - ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CPU) ? cpu : NULL, - &dai_props->cpu_dai, - dai_link, - &cpu_args); + ret = rsrc_card_parse_daifmt(node, np, priv, idx, is_fe); if (ret < 0) - goto dai_link_of_err; + return ret; - ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CODEC) ? codec : NULL, - &dai_props->codec_dai, - dai_link, - NULL); + ret = rsrc_card_parse_links(np, priv, idx, is_fe); if (ret < 0) - goto dai_link_of_err; - - if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) { - ret = -EINVAL; - goto dai_link_of_err; - } - - /* Simple Card assumes platform == cpu */ - dai_link->platform_of_node = dai_link->cpu_of_node; - - /* DAI link name is created from CPU/CODEC dai name */ - name = devm_kzalloc(dev, - strlen(dai_link->cpu_dai_name) + - strlen(dai_link->codec_dai_name) + 2, - GFP_KERNEL); - if (!name) { - ret = -ENOMEM; - goto dai_link_of_err; - } - - sprintf(name, "%s-%s", dai_link->cpu_dai_name, - dai_link->codec_dai_name); - dai_link->name = dai_link->stream_name = name; - dai_link->ops = &rsrc_card_ops; - dai_link->init = rsrc_card_dai_init; - - if (idx == IDX_CODEC) - dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup; - - dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); - dev_dbg(dev, "\tcpu : %s / %04x / %d\n", - dai_link->cpu_dai_name, - dai_props->cpu_dai.fmt, - dai_props->cpu_dai.sysclk); - dev_dbg(dev, "\tcodec : %s / %04x / %d\n", - dai_link->codec_dai_name, - dai_props->codec_dai.fmt, - dai_props->codec_dai.sysclk); + return ret; - /* - * In soc_bind_dai_link() will check cpu name after - * of_node matching if dai_link has cpu_dai_name. - * but, it will never match if name was created by - * fmt_single_name() remove cpu_dai_name if cpu_args - * was 0. See: - * fmt_single_name() - * fmt_multiple_name() - */ - if (!cpu_args) - dai_link->cpu_dai_name = NULL; + ret = rsrc_card_parse_clk(np, priv, idx, is_fe); + if (ret < 0) + return ret; -dai_link_of_err: - of_node_put(cpu); - of_node_put(codec); + dev_dbg(dev, "\t%s / %04x / %d\n", + dai_props->dai_name, + dai_props->fmt, + dai_props->sysclk); return ret; } static int rsrc_card_parse_of(struct device_node *node, - struct rsrc_card_priv *priv) + struct rsrc_card_priv *priv, + struct device *dev) { - struct device *dev = rsrc_priv_to_dev(priv); 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; + int i, num; if (!node) return -EINVAL; - /* Parse the card name from DT */ - snd_soc_of_parse_card_name(&priv->snd_card, "card-name"); + num = of_get_child_count(node); + props = devm_kzalloc(dev, sizeof(*props) * num, GFP_KERNEL); + links = devm_kzalloc(dev, sizeof(*links) * num, GFP_KERNEL); + if (!props || !links) + return -ENOMEM; + + priv->dai_props = props; + priv->dai_link = links; + priv->dai_num = num; - /* DAPM routes */ + /* Init snd_soc_card */ + priv->snd_card.owner = THIS_MODULE; + priv->snd_card.dev = dev; + priv->snd_card.dai_link = priv->dai_link; + priv->snd_card.num_links = num; + priv->snd_card.codec_conf = &priv->codec_conf; + priv->snd_card.num_configs = 1; priv->snd_card.of_dapm_routes = of_data->routes; priv->snd_card.num_of_dapm_routes = of_data->num_routes; + /* Parse the card name from DT */ + snd_soc_of_parse_card_name(&priv->snd_card, "card-name"); + /* sampling rate convert */ of_property_read_u32(node, "convert-rate", &priv->convert_rate); @@ -421,11 +376,12 @@ static int rsrc_card_parse_of(struct device_node *node, priv->snd_card.name ? priv->snd_card.name : "", priv->convert_rate); - /* FE/BE */ - for (i = 0; i < RSRC_FB_NUM; i++) { - ret = rsrc_card_dai_link_of(node, priv, i); + 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++; } if (!priv->snd_card.name) @@ -452,7 +408,6 @@ static int rsrc_card_unref(struct snd_soc_card *card) static int rsrc_card_probe(struct platform_device *pdev) { struct rsrc_card_priv *priv; - struct snd_soc_dai_link *dai_link; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; int ret; @@ -462,16 +417,7 @@ static int rsrc_card_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - /* Init snd_soc_card */ - priv->snd_card.owner = THIS_MODULE; - priv->snd_card.dev = dev; - dai_link = priv->dai_link; - priv->snd_card.dai_link = dai_link; - priv->snd_card.num_links = RSRC_FB_NUM; - priv->snd_card.codec_conf = &priv->codec_conf; - priv->snd_card.num_configs = 1; - - ret = rsrc_card_parse_of(np, priv); + ret = rsrc_card_parse_of(np, priv, dev); if (ret < 0) { if (ret != -EPROBE_DEFER) dev_err(dev, "parse error %d\n", ret); diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index fbe9166e26d1..c61c17180142 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -117,10 +117,10 @@ struct rsnd_src { /* * Gen1/Gen2 common functions */ -static struct dma_chan *rsnd_src_dma_req(struct rsnd_mod *mod) +static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int is_play = rsnd_io_is_play(io); return rsnd_dma_request_channel(rsnd_src_of_node(priv), @@ -129,9 +129,9 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_mod *mod) } int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, + struct rsnd_dai_stream *io, int use_busif) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(ssi_mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); int ssi_id = rsnd_mod_id(ssi_mod); @@ -174,7 +174,7 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, u32 mask = ~0; rsnd_mod_write(ssi_mod, SSI_BUSIF_ADINR, - rsnd_get_adinr(ssi_mod)); + rsnd_get_adinr(ssi_mod, io)); rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1); rsnd_mod_write(ssi_mod, SSI_CTRL, 0x1); @@ -196,7 +196,8 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, return 0; } -int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod) +int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, + struct rsnd_dai_stream *io) { /* * DMA settings for SSIU @@ -235,10 +236,9 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod) return 0; } -static u32 rsnd_src_convert_rate(struct rsnd_src *src) +static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, + struct rsnd_src *src) { - struct rsnd_mod *mod = &src->mod; - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 convert_rate; @@ -274,7 +274,7 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, * return convert rate if SRC is used, * otherwise, return runtime->rate as usual */ - rate = rsnd_src_convert_rate(src); + rate = rsnd_src_convert_rate(io, src); } if (!rate) @@ -283,12 +283,12 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, return rate; } -static int rsnd_src_set_convert_rate(struct rsnd_mod *mod) +static int rsnd_src_set_convert_rate(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(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(src); + u32 convert_rate = rsnd_src_convert_rate(io, src); u32 fsrate = 0; if (convert_rate) @@ -299,7 +299,7 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod) rsnd_mod_write(mod, SRC_SWRSR, 1); /* Set channel number and output bit length */ - rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod)); + rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod, io)); /* Enable the initial value of IFS */ if (fsrate) { @@ -316,6 +316,7 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod) } static int rsnd_src_hw_params(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *fe_params) { @@ -372,6 +373,7 @@ static int rsnd_src_init(struct rsnd_mod *mod, } static int rsnd_src_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); @@ -411,9 +413,9 @@ static int rsnd_src_stop(struct rsnd_mod *mod) /* * Gen1 functions */ -static int rsnd_src_set_route_gen1(struct rsnd_mod *mod) +static int rsnd_src_set_route_gen1(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct src_route_config { u32 mask; int shift; @@ -448,13 +450,13 @@ static int rsnd_src_set_route_gen1(struct rsnd_mod *mod) return 0; } -static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod) +static int rsnd_src_set_convert_timing_gen1(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(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(src); + u32 convert_rate = rsnd_src_convert_rate(io, src); u32 mask; u32 val; int shift; @@ -506,12 +508,13 @@ static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod) return 0; } -static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod) +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); + ret = rsnd_src_set_convert_rate(mod, io); if (ret < 0) return ret; @@ -523,7 +526,7 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod) rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98); /* Gen1/Gen2 are not compatible */ - if (rsnd_src_convert_rate(src)) + 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 */ @@ -532,6 +535,7 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod) } static int rsnd_src_init_gen1(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { int ret; @@ -540,15 +544,15 @@ static int rsnd_src_init_gen1(struct rsnd_mod *mod, if (ret < 0) return ret; - ret = rsnd_src_set_route_gen1(mod); + ret = rsnd_src_set_route_gen1(io, mod); if (ret < 0) return ret; - ret = rsnd_src_set_convert_rate_gen1(mod); + ret = rsnd_src_set_convert_rate_gen1(mod, io); if (ret < 0) return ret; - ret = rsnd_src_set_convert_timing_gen1(mod); + ret = rsnd_src_set_convert_timing_gen1(io, mod); if (ret < 0) return ret; @@ -556,6 +560,7 @@ static int rsnd_src_init_gen1(struct rsnd_mod *mod, } 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); @@ -566,6 +571,7 @@ static int rsnd_src_start_gen1(struct rsnd_mod *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); @@ -643,9 +649,9 @@ static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod) return ret; } -static int _rsnd_src_start_gen2(struct rsnd_mod *mod) +static int _rsnd_src_start_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11; rsnd_mod_write(mod, SRC_CTRL, val); @@ -670,15 +676,15 @@ static int _rsnd_src_stop_gen2(struct rsnd_mod *mod) return rsnd_src_stop(mod); } -static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) +static void __rsnd_src_interrupt_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - struct rsnd_mod *mod = data; struct rsnd_priv *priv = rsnd_mod_to_priv(mod); spin_lock(&priv->lock); /* ignore all cases if not working */ - if (!rsnd_mod_is_working(mod)) + if (!rsnd_io_is_working(io)) goto rsnd_src_interrupt_gen2_out; if (rsnd_src_error_record_gen2(mod)) { @@ -691,24 +697,32 @@ static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) _rsnd_src_stop_gen2(mod); if (src->err < 1024) - _rsnd_src_start_gen2(mod); + _rsnd_src_start_gen2(mod, io); else dev_warn(dev, "no more SRC restart\n"); } + rsnd_src_interrupt_gen2_out: spin_unlock(&priv->lock); +} + +static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) +{ + struct rsnd_mod *mod = data; + + rsnd_mod_interrupt(mod, __rsnd_src_interrupt_gen2); return IRQ_HANDLED; } -static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) +static int rsnd_src_set_convert_rate_gen2(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 rsnd_dai_stream *io = rsnd_mod_to_io(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(src); + u32 convert_rate = rsnd_src_convert_rate(io, src); u32 cr, route; uint ratio; int ret; @@ -726,7 +740,7 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) return -EINVAL; } - ret = rsnd_src_set_convert_rate(mod); + ret = rsnd_src_set_convert_rate(mod, io); if (ret < 0) return ret; @@ -762,12 +776,12 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) return 0; } -static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod) +static int rsnd_src_set_convert_timing_gen2(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(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(src); + u32 convert_rate = rsnd_src_convert_rate(io, src); int ret; if (convert_rate) @@ -781,6 +795,7 @@ static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod) } static int rsnd_src_probe_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); @@ -802,7 +817,7 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, return ret; } - ret = rsnd_dma_init(priv, + ret = rsnd_dma_init(io, rsnd_mod_to_dma(mod), src->info->dma_id); @@ -810,14 +825,16 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, } static int rsnd_src_remove_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_dma_quit(rsnd_mod_to_dma(mod)); + 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; @@ -826,11 +843,11 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod, if (ret < 0) return ret; - ret = rsnd_src_set_convert_rate_gen2(mod); + ret = rsnd_src_set_convert_rate_gen2(mod, io); if (ret < 0) return ret; - ret = rsnd_src_set_convert_timing_gen2(mod); + ret = rsnd_src_set_convert_timing_gen2(io, mod); if (ret < 0) return ret; @@ -838,31 +855,33 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod, } static int rsnd_src_start_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_dma_start(rsnd_mod_to_dma(mod)); + rsnd_dma_start(io, rsnd_mod_to_dma(mod)); - return _rsnd_src_start_gen2(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(rsnd_mod_to_dma(mod)); + rsnd_dma_stop(io, rsnd_mod_to_dma(mod)); return ret; } -static void rsnd_src_reconvert_update(struct rsnd_mod *mod) +static void rsnd_src_reconvert_update(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(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(src); + u32 convert_rate = rsnd_src_convert_rate(io, src); u32 fsrate; if (!runtime) @@ -878,10 +897,10 @@ static void rsnd_src_reconvert_update(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_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; @@ -912,7 +931,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, /* * enable sync convert */ - ret = rsnd_kctrl_new_s(mod, rtd, + ret = rsnd_kctrl_new_s(mod, io, rtd, rsnd_io_is_play(io) ? "SRC Out Rate Switch" : "SRC In Rate Switch", @@ -921,7 +940,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, if (ret < 0) return ret; - ret = rsnd_kctrl_new_s(mod, rtd, + ret = rsnd_kctrl_new_s(mod, io, rtd, rsnd_io_is_play(io) ? "SRC Out Rate" : "SRC In Rate", @@ -1046,7 +1065,7 @@ int rsnd_src_probe(struct platform_device *pdev, src->info = &info->src_info[i]; - ret = rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i); + ret = rsnd_mod_init(priv, &src->mod, ops, clk, RSND_MOD_SRC, i); if (ret) return ret; } diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 50fa3928a003..2fbe59f7f9b5 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -87,10 +87,9 @@ struct rsnd_ssi { #define rsnd_ssi_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi") -int rsnd_ssi_use_busif(struct rsnd_mod *mod) +int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int use_busif = 0; if (!rsnd_ssi_is_dma_mode(mod)) @@ -199,15 +198,17 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, } } - cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ? - DMEN : /* DMA : enable DMA */ - DIEN; /* PIO : enable Data interrupt */ - + if (rsnd_ssi_is_dma_mode(&ssi->mod)) { + 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 | - UIEN | OIEN | EN; + EN; rsnd_mod_write(&ssi->mod, SSICR, cr); @@ -224,10 +225,9 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod)); } -static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi) +static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi) { struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(&ssi->mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct device *dev = rsnd_priv_to_dev(priv); u32 cr; @@ -261,7 +261,7 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi) struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); if (ssi_parent) - rsnd_ssi_hw_stop(ssi_parent); + rsnd_ssi_hw_stop(io, ssi_parent); else rsnd_ssi_master_clk_stop(ssi); } @@ -279,10 +279,10 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi) * SSI mod common functions */ static int rsnd_ssi_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 cr; @@ -330,6 +330,7 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, } static int rsnd_ssi_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); @@ -346,6 +347,7 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, } static int rsnd_ssi_hw_params(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -369,7 +371,8 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod, /* It will be removed on rsnd_ssi_hw_stop */ ssi->chan = chan; if (ssi_parent) - return rsnd_ssi_hw_params(&ssi_parent->mod, substream, params); + return rsnd_ssi_hw_params(&ssi_parent->mod, io, + substream, params); return 0; } @@ -386,12 +389,12 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 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); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - rsnd_src_ssiu_start(mod, rsnd_ssi_use_busif(mod)); + rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io, mod)); rsnd_ssi_hw_start(ssi, io); @@ -401,6 +404,7 @@ static int rsnd_ssi_start(struct rsnd_mod *mod, } 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); @@ -409,26 +413,26 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod, rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); - rsnd_ssi_hw_stop(ssi); + rsnd_ssi_hw_stop(io, ssi); - rsnd_src_ssiu_stop(mod); + rsnd_src_ssiu_stop(mod, io); return 0; } -static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) +static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - struct rsnd_ssi *ssi = data; - struct rsnd_mod *mod = &ssi->mod; + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int is_dma = rsnd_ssi_is_dma_mode(mod); u32 status; + bool elapsed = false; spin_lock(&priv->lock); /* ignore all cases if not working */ - if (!rsnd_mod_is_working(mod)) + if (!rsnd_io_is_working(io)) goto rsnd_ssi_interrupt_out; status = rsnd_mod_read(mod, SSISR); @@ -449,11 +453,11 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) else *buf = rsnd_mod_read(mod, SSIRDR); - rsnd_dai_pointer_update(io, sizeof(*buf)); + elapsed = rsnd_dai_pointer_update(io, sizeof(*buf)); } - /* PIO / DMA */ - if (status & (UIRQ | OIRQ)) { + /* DMA only */ + if (is_dma && (status & (UIRQ | OIRQ))) { struct device *dev = rsnd_priv_to_dev(priv); /* @@ -462,9 +466,9 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) dev_dbg(dev, "%s[%d] restart\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); - rsnd_ssi_stop(mod, priv); + rsnd_ssi_stop(mod, io, priv); if (ssi->err < 1024) - rsnd_ssi_start(mod, priv); + rsnd_ssi_start(mod, io, priv); else dev_warn(dev, "no more SSI restart\n"); } @@ -474,6 +478,16 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) rsnd_ssi_interrupt_out: spin_unlock(&priv->lock); + if (elapsed) + rsnd_dai_period_elapsed(io); +} + +static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) +{ + struct rsnd_mod *mod = data; + + rsnd_mod_interrupt(mod, __rsnd_ssi_interrupt); + return IRQ_HANDLED; } @@ -481,6 +495,7 @@ rsnd_ssi_interrupt_out: * SSI PIO */ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); @@ -490,7 +505,7 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, ret = devm_request_irq(dev, ssi->info->irq, rsnd_ssi_interrupt, IRQF_SHARED, - dev_name(dev), ssi); + dev_name(dev), mod); return ret; } @@ -506,6 +521,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { }; static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); @@ -516,25 +532,26 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, ret = devm_request_irq(dev, ssi->info->irq, rsnd_ssi_interrupt, IRQF_SHARED, - dev_name(dev), ssi); + dev_name(dev), mod); if (ret) return ret; ret = rsnd_dma_init( - priv, rsnd_mod_to_dma(mod), + io, rsnd_mod_to_dma(mod), dma_id); return ret; } static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { 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(rsnd_mod_to_dma(mod)); + rsnd_dma_quit(io, rsnd_mod_to_dma(mod)); /* PIO will request IRQ again */ devm_free_irq(dev, irq, ssi); @@ -543,6 +560,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, } static int rsnd_ssi_fallback(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); @@ -563,37 +581,39 @@ static int rsnd_ssi_fallback(struct rsnd_mod *mod, } 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(dma); + rsnd_dma_start(io, dma); - rsnd_ssi_start(mod, priv); + 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, priv); + rsnd_ssi_stop(mod, io, priv); - rsnd_dma_stop(dma); + rsnd_dma_stop(io, dma); return 0; } -static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_mod *mod) +static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int is_play = rsnd_io_is_play(io); char *name; - if (rsnd_ssi_use_busif(mod)) + if (rsnd_ssi_use_busif(io, mod)) name = is_play ? "rxu" : "txu"; else name = is_play ? "rx" : "tx"; @@ -776,7 +796,7 @@ int rsnd_ssi_probe(struct platform_device *pdev, else if (rsnd_ssi_pio_available(ssi)) ops = &rsnd_ssi_pio_ops; - ret = rsnd_mod_init(&ssi->mod, ops, clk, RSND_MOD_SSI, i); + ret = rsnd_mod_init(priv, &ssi->mod, ops, clk, RSND_MOD_SSI, i); if (ret) return ret; |