mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 03:01:39 -04:00
ASoC: Updates for v4.5
This is quite a busy release on the driver front with a lot of new
drivers being added but comparatively quiet on the core side with only
one big change going in and that a fairly straightforward refactoring.
- Conversion of the array of DAI links to a list by Mengdong Lin,
supporting dynamically adding and removing DAI links.
- Some more fixes for the topology code, though it is still not final
and ready for enabling in production. We really need to get to the
point where that can be done.
- A pile of changes for Intel SkyLake drivers which hopefully deliver
some useful initial functionality for systems with this chipset,
though there is more work still to come.
- New drivers for a number of Imagination Technologies IPs.
- Lots of new features and cleanups for the Renesas drivers.
- ANC support for WM5110.
- New driver for Atmel class D speaker drivers.
- New drivers for Cirrus CS47L24 and WM1831.
- New driver for Dialog DA7128.
- New drivers for Realtek RT5659 and RT56156.
- New driver for Rockchip RK3036.
- New driver for TI PC3168A
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQEcBAABAgAGBQJWee4AAAoJECTWi3JdVIfQwPkH/jMpijne8cfnOm0jQ0WyvXPd
HSig93ZAJ2x/+uR7yrmF2BsxhnT6G1lmDb2zxYhHGEAMyBtdA/xtOjO1wTRjXgIo
9p7ui66uP/sAW+NfHsvXVyf/8vV9nw8t0CsyBf+ijynz6MiDigQR7e8RA7xZR7oQ
oBabHPZb85OJjxmn2XlqaxUoxNhBdAS3gNoctMy9VZDwSLiunOvYTGBoBA43LQk6
6DN5VRu7wg05ruN6t5xZ5bd5WAtvgN+tSsRAabmunWDnILCa8OaOmzWPv3QITIva
PQLauiya4N0/YgyNGYaBY/6pc5u9SHzq0caYUq0I6WY6Phjo7wfU8/+y4NqUPn4=
=d18k
-----END PGP SIGNATURE-----
Merge tag 'asoc-v4.5' into asoc-next
ASoC: Updates for v4.5
This is quite a busy release on the driver front with a lot of new
drivers being added but comparatively quiet on the core side with only
one big change going in and that a fairly straightforward refactoring.
- Conversion of the array of DAI links to a list by Mengdong Lin,
supporting dynamically adding and removing DAI links.
- Some more fixes for the topology code, though it is still not final
and ready for enabling in production. We really need to get to the
point where that can be done.
- A pile of changes for Intel SkyLake drivers which hopefully deliver
some useful initial functionality for systems with this chipset,
though there is more work still to come.
- New drivers for a number of Imagination Technologies IPs.
- Lots of new features and cleanups for the Renesas drivers.
- ANC support for WM5110.
- New driver for Atmel class D speaker drivers.
- New drivers for Cirrus CS47L24 and WM1831.
- New driver for Dialog DA7128.
- New drivers for Realtek RT5659 and RT56156.
- New driver for Rockchip RK3036.
- New driver for TI PC3168A
# gpg: Signature made Wed 23 Dec 2015 00:42:40 GMT using RSA key ID 5D5487D0
# gpg: Good signature from "Mark Brown <[email protected]>"
# gpg: aka "Mark Brown <[email protected]>"
# gpg: aka "Mark Brown <[email protected]>"
# gpg: aka "Mark Brown <[email protected]>"
# gpg: aka "Mark Brown <[email protected]>"
# gpg: aka "Mark Brown <[email protected]>"
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg: There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 3F25 68AA C269 98F9 E813 A1C5 C3F4 36CA 30F5 D8EB
# Subkey fingerprint: ADE6 68AA 6757 18B5 9FE2 9FEA 24D6 8B72 5D54 87D0
This commit is contained in:
commit
fffe9b89d8
145 changed files with 26491 additions and 3223 deletions
|
|
@ -50,6 +50,7 @@ source "sound/soc/jz4740/Kconfig"
|
|||
source "sound/soc/nuc900/Kconfig"
|
||||
source "sound/soc/omap/Kconfig"
|
||||
source "sound/soc/kirkwood/Kconfig"
|
||||
source "sound/soc/img/Kconfig"
|
||||
source "sound/soc/intel/Kconfig"
|
||||
source "sound/soc/mediatek/Kconfig"
|
||||
source "sound/soc/mxs/Kconfig"
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ obj-$(CONFIG_SND_SOC) += davinci/
|
|||
obj-$(CONFIG_SND_SOC) += dwc/
|
||||
obj-$(CONFIG_SND_SOC) += fsl/
|
||||
obj-$(CONFIG_SND_SOC) += jz4740/
|
||||
obj-$(CONFIG_SND_SOC) += img/
|
||||
obj-$(CONFIG_SND_SOC) += intel/
|
||||
obj-$(CONFIG_SND_SOC) += mediatek/
|
||||
obj-$(CONFIG_SND_SOC) += mxs/
|
||||
|
|
|
|||
|
|
@ -68,4 +68,13 @@ config SND_ATMEL_SOC_CLASSD
|
|||
help
|
||||
Say Y if you want to add support for Atmel ASoC driver for boards using
|
||||
CLASSD.
|
||||
|
||||
config SND_ATMEL_SOC_PDMIC
|
||||
tristate "Atmel ASoC driver for boards using PDMIC"
|
||||
depends on OF && (ARCH_AT91 || COMPILE_TEST)
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Say Y if you want to add support for Atmel ASoC driver for boards using
|
||||
PDMIC.
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@ snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
|
|||
snd-atmel-soc-wm8904-objs := atmel_wm8904.o
|
||||
snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
|
||||
snd-atmel-soc-classd-objs := atmel-classd.o
|
||||
snd-atmel-soc-pdmic-objs := atmel-pdmic.o
|
||||
|
||||
obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
|
||||
obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o
|
||||
obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
|
||||
obj-$(CONFIG_SND_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o
|
||||
obj-$(CONFIG_SND_ATMEL_SOC_PDMIC) += snd-atmel-soc-pdmic.o
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ static const struct snd_pcm_hardware atmel_classd_hw = {
|
|||
.rates = ATMEL_CLASSD_RATES,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 96000,
|
||||
.channels_min = 2,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 64 * 1024,
|
||||
.period_bytes_min = 256,
|
||||
|
|
@ -145,7 +145,7 @@ static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = {
|
|||
|
||||
static struct snd_soc_dai_driver atmel_classd_cpu_dai = {
|
||||
.playback = {
|
||||
.channels_min = 2,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = ATMEL_CLASSD_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
||||
|
|
@ -171,9 +171,13 @@ atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (params_channels(params) == 1)
|
||||
slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
else
|
||||
slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
|
||||
slave_config->direction = DMA_MEM_TO_DEV;
|
||||
slave_config->dst_addr = dd->phy_base + CLASSD_THR;
|
||||
slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
slave_config->dst_maxburst = 1;
|
||||
slave_config->src_maxburst = 1;
|
||||
slave_config->device_fc = false;
|
||||
|
|
@ -486,7 +490,7 @@ static struct snd_soc_dai_driver atmel_classd_codec_dai = {
|
|||
.name = ATMEL_CLASSD_CODEC_DAI_NAME,
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = ATMEL_CLASSD_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
|
|
@ -636,8 +640,10 @@ static int atmel_classd_probe(struct platform_device *pdev)
|
|||
|
||||
/* register sound card */
|
||||
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
|
||||
if (!card)
|
||||
return -ENOMEM;
|
||||
if (!card) {
|
||||
ret = -ENOMEM;
|
||||
goto unregister_codec;
|
||||
}
|
||||
|
||||
snd_soc_card_set_drvdata(card, dd);
|
||||
platform_set_drvdata(pdev, card);
|
||||
|
|
@ -645,16 +651,20 @@ static int atmel_classd_probe(struct platform_device *pdev)
|
|||
ret = atmel_classd_asoc_card_init(dev, card);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to init sound card\n");
|
||||
return ret;
|
||||
goto unregister_codec;
|
||||
}
|
||||
|
||||
ret = devm_snd_soc_register_card(dev, card);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register sound card: %d\n", ret);
|
||||
return ret;
|
||||
goto unregister_codec;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_codec:
|
||||
snd_soc_unregister_codec(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atmel_classd_remove(struct platform_device *pdev)
|
||||
|
|
|
|||
738
sound/soc/atmel/atmel-pdmic.c
Normal file
738
sound/soc/atmel/atmel-pdmic.c
Normal file
|
|
@ -0,0 +1,738 @@
|
|||
/* Atmel PDMIC driver
|
||||
*
|
||||
* Copyright (C) 2015 Atmel
|
||||
*
|
||||
* Author: Songjun Wu <songjun.wu@atmel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 or later
|
||||
* as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "atmel-pdmic.h"
|
||||
|
||||
struct atmel_pdmic_pdata {
|
||||
u32 mic_min_freq;
|
||||
u32 mic_max_freq;
|
||||
s32 mic_offset;
|
||||
const char *card_name;
|
||||
};
|
||||
|
||||
struct atmel_pdmic {
|
||||
dma_addr_t phy_base;
|
||||
struct regmap *regmap;
|
||||
struct clk *pclk;
|
||||
struct clk *gclk;
|
||||
int irq;
|
||||
struct snd_pcm_substream *substream;
|
||||
const struct atmel_pdmic_pdata *pdata;
|
||||
};
|
||||
|
||||
static const struct of_device_id atmel_pdmic_of_match[] = {
|
||||
{
|
||||
.compatible = "atmel,sama5d2-pdmic",
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, atmel_pdmic_of_match);
|
||||
|
||||
#define PDMIC_OFFSET_MAX_VAL S16_MAX
|
||||
#define PDMIC_OFFSET_MIN_VAL S16_MIN
|
||||
|
||||
static struct atmel_pdmic_pdata *atmel_pdmic_dt_init(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct atmel_pdmic_pdata *pdata;
|
||||
|
||||
if (!np) {
|
||||
dev_err(dev, "device node not found\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (of_property_read_string(np, "atmel,model", &pdata->card_name))
|
||||
pdata->card_name = "PDMIC";
|
||||
|
||||
if (of_property_read_u32(np, "atmel,mic-min-freq",
|
||||
&pdata->mic_min_freq)) {
|
||||
dev_err(dev, "failed to get mic-min-freq\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "atmel,mic-max-freq",
|
||||
&pdata->mic_max_freq)) {
|
||||
dev_err(dev, "failed to get mic-max-freq\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (pdata->mic_max_freq < pdata->mic_min_freq) {
|
||||
dev_err(dev,
|
||||
"mic-max-freq should not less than mic-min-freq\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (of_property_read_s32(np, "atmel,mic-offset", &pdata->mic_offset))
|
||||
pdata->mic_offset = 0;
|
||||
|
||||
if (pdata->mic_offset > PDMIC_OFFSET_MAX_VAL) {
|
||||
dev_warn(dev,
|
||||
"mic-offset value %d is larger than the max value %d, the max value is specified\n",
|
||||
pdata->mic_offset, PDMIC_OFFSET_MAX_VAL);
|
||||
pdata->mic_offset = PDMIC_OFFSET_MAX_VAL;
|
||||
} else if (pdata->mic_offset < PDMIC_OFFSET_MIN_VAL) {
|
||||
dev_warn(dev,
|
||||
"mic-offset value %d is less than the min value %d, the min value is specified\n",
|
||||
pdata->mic_offset, PDMIC_OFFSET_MIN_VAL);
|
||||
pdata->mic_offset = PDMIC_OFFSET_MIN_VAL;
|
||||
}
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
/* cpu dai component */
|
||||
static int atmel_pdmic_cpu_dai_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(dd->gclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(dd->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Clear all bits in the Control Register(PDMIC_CR) */
|
||||
regmap_write(dd->regmap, PDMIC_CR, 0);
|
||||
|
||||
dd->substream = substream;
|
||||
|
||||
/* Enable the overrun error interrupt */
|
||||
regmap_write(dd->regmap, PDMIC_IER, PDMIC_IER_OVRE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atmel_pdmic_cpu_dai_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
|
||||
|
||||
/* Disable the overrun error interrupt */
|
||||
regmap_write(dd->regmap, PDMIC_IDR, PDMIC_IDR_OVRE);
|
||||
|
||||
clk_disable_unprepare(dd->gclk);
|
||||
clk_disable_unprepare(dd->pclk);
|
||||
}
|
||||
|
||||
static int atmel_pdmic_cpu_dai_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
|
||||
u32 val;
|
||||
|
||||
/* Clean the PDMIC Converted Data Register */
|
||||
return regmap_read(dd->regmap, PDMIC_CDR, &val);
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops atmel_pdmic_cpu_dai_ops = {
|
||||
.startup = atmel_pdmic_cpu_dai_startup,
|
||||
.shutdown = atmel_pdmic_cpu_dai_shutdown,
|
||||
.prepare = atmel_pdmic_cpu_dai_prepare,
|
||||
};
|
||||
|
||||
#define ATMEL_PDMIC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
static struct snd_soc_dai_driver atmel_pdmic_cpu_dai = {
|
||||
.capture = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = ATMEL_PDMIC_FORMATS,},
|
||||
.ops = &atmel_pdmic_cpu_dai_ops,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver atmel_pdmic_cpu_dai_component = {
|
||||
.name = "atmel-pdmic",
|
||||
};
|
||||
|
||||
/* platform */
|
||||
#define ATMEL_PDMIC_MAX_BUF_SIZE (64 * 1024)
|
||||
#define ATMEL_PDMIC_PREALLOC_BUF_SIZE ATMEL_PDMIC_MAX_BUF_SIZE
|
||||
|
||||
static const struct snd_pcm_hardware atmel_pdmic_hw = {
|
||||
.info = SNDRV_PCM_INFO_MMAP
|
||||
| SNDRV_PCM_INFO_MMAP_VALID
|
||||
| SNDRV_PCM_INFO_INTERLEAVED
|
||||
| SNDRV_PCM_INFO_RESUME
|
||||
| SNDRV_PCM_INFO_PAUSE,
|
||||
.formats = ATMEL_PDMIC_FORMATS,
|
||||
.buffer_bytes_max = ATMEL_PDMIC_MAX_BUF_SIZE,
|
||||
.period_bytes_min = 256,
|
||||
.period_bytes_max = 32 * 1024,
|
||||
.periods_min = 2,
|
||||
.periods_max = 256,
|
||||
};
|
||||
|
||||
static int
|
||||
atmel_pdmic_platform_configure_dma(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct dma_slave_config *slave_config)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
|
||||
int ret;
|
||||
|
||||
ret = snd_hwparams_to_dma_slave_config(substream, params,
|
||||
slave_config);
|
||||
if (ret) {
|
||||
dev_err(rtd->platform->dev,
|
||||
"hw params to dma slave configure failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
slave_config->src_addr = dd->phy_base + PDMIC_CDR;
|
||||
slave_config->src_maxburst = 1;
|
||||
slave_config->dst_maxburst = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_dmaengine_pcm_config
|
||||
atmel_pdmic_dmaengine_pcm_config = {
|
||||
.prepare_slave_config = atmel_pdmic_platform_configure_dma,
|
||||
.pcm_hardware = &atmel_pdmic_hw,
|
||||
.prealloc_buffer_size = ATMEL_PDMIC_PREALLOC_BUF_SIZE,
|
||||
};
|
||||
|
||||
/* codec */
|
||||
/* Mic Gain = dgain * 2^(-scale) */
|
||||
struct mic_gain {
|
||||
unsigned int dgain;
|
||||
unsigned int scale;
|
||||
};
|
||||
|
||||
/* range from -90 dB to 90 dB */
|
||||
static const struct mic_gain mic_gain_table[] = {
|
||||
{ 1, 15}, { 1, 14}, /* -90, -84 dB */
|
||||
{ 3, 15}, { 1, 13}, { 3, 14}, { 1, 12}, /* -81, -78, -75, -72 dB */
|
||||
{ 5, 14}, { 13, 15}, /* -70, -68 dB */
|
||||
{ 9, 14}, { 21, 15}, { 23, 15}, { 13, 14}, /* -65 ~ -62 dB */
|
||||
{ 29, 15}, { 33, 15}, { 37, 15}, { 41, 15}, /* -61 ~ -58 dB */
|
||||
{ 23, 14}, { 13, 13}, { 58, 15}, { 65, 15}, /* -57 ~ -54 dB */
|
||||
{ 73, 15}, { 41, 14}, { 23, 13}, { 13, 12}, /* -53 ~ -50 dB */
|
||||
{ 29, 13}, { 65, 14}, { 73, 14}, { 41, 13}, /* -49 ~ -46 dB */
|
||||
{ 23, 12}, { 207, 15}, { 29, 12}, { 65, 13}, /* -45 ~ -42 dB */
|
||||
{ 73, 13}, { 41, 12}, { 23, 11}, { 413, 15}, /* -41 ~ -38 dB */
|
||||
{ 463, 15}, { 519, 15}, { 583, 15}, { 327, 14}, /* -37 ~ -34 dB */
|
||||
{ 367, 14}, { 823, 15}, { 231, 13}, { 1036, 15}, /* -33 ~ -30 dB */
|
||||
{ 1163, 15}, { 1305, 15}, { 183, 12}, { 1642, 15}, /* -29 ~ -26 dB */
|
||||
{ 1843, 15}, { 2068, 15}, { 145, 11}, { 2603, 15}, /* -25 ~ -22 dB */
|
||||
{ 365, 12}, { 3277, 15}, { 3677, 15}, { 4125, 15}, /* -21 ~ -18 dB */
|
||||
{ 4629, 15}, { 5193, 15}, { 5827, 15}, { 3269, 14}, /* -17 ~ -14 dB */
|
||||
{ 917, 12}, { 8231, 15}, { 9235, 15}, { 5181, 14}, /* -13 ~ -10 dB */
|
||||
{11627, 15}, {13045, 15}, {14637, 15}, {16423, 15}, /* -9 ~ -6 dB */
|
||||
{18427, 15}, {20675, 15}, { 5799, 13}, {26029, 15}, /* -5 ~ -2 dB */
|
||||
{ 7301, 13}, { 1, 0}, {18383, 14}, {10313, 13}, /* -1 ~ 2 dB */
|
||||
{23143, 14}, {25967, 14}, {29135, 14}, {16345, 13}, /* 3 ~ 6 dB */
|
||||
{ 4585, 11}, {20577, 13}, { 1443, 9}, {25905, 13}, /* 7 ~ 10 dB */
|
||||
{14533, 12}, { 8153, 11}, { 2287, 9}, {20529, 12}, /* 11 ~ 14 dB */
|
||||
{11517, 11}, { 6461, 10}, {28997, 12}, { 4067, 9}, /* 15 ~ 18 dB */
|
||||
{18253, 11}, { 10, 0}, {22979, 11}, {25783, 11}, /* 19 ~ 22 dB */
|
||||
{28929, 11}, {32459, 11}, { 9105, 9}, {20431, 10}, /* 23 ~ 26 dB */
|
||||
{22925, 10}, {12861, 9}, { 7215, 8}, {16191, 9}, /* 27 ~ 30 dB */
|
||||
{ 9083, 8}, {20383, 9}, {11435, 8}, { 6145, 7}, /* 31 ~ 34 dB */
|
||||
{ 3599, 6}, {32305, 9}, {18123, 8}, {20335, 8}, /* 35 ~ 38 dB */
|
||||
{ 713, 3}, { 100, 0}, { 7181, 6}, { 8057, 6}, /* 39 ~ 42 dB */
|
||||
{ 565, 2}, {20287, 7}, {11381, 6}, {25539, 7}, /* 43 ~ 46 dB */
|
||||
{ 1791, 3}, { 4019, 4}, { 9019, 5}, {20239, 6}, /* 47 ~ 50 dB */
|
||||
{ 5677, 4}, {25479, 6}, { 7147, 4}, { 8019, 4}, /* 51 ~ 54 dB */
|
||||
{17995, 5}, {20191, 5}, {11327, 4}, {12709, 4}, /* 55 ~ 58 dB */
|
||||
{ 3565, 2}, { 1000, 0}, { 1122, 0}, { 1259, 0}, /* 59 ~ 62 dB */
|
||||
{ 2825, 1}, {12679, 3}, { 7113, 2}, { 7981, 2}, /* 63 ~ 66 dB */
|
||||
{ 8955, 2}, {20095, 3}, {22547, 3}, {12649, 2}, /* 67 ~ 70 dB */
|
||||
{28385, 3}, { 3981, 0}, {17867, 2}, {20047, 2}, /* 71 ~ 74 dB */
|
||||
{11247, 1}, {12619, 1}, {14159, 1}, {31773, 2}, /* 75 ~ 78 dB */
|
||||
{17825, 1}, {10000, 0}, {11220, 0}, {12589, 0}, /* 79 ~ 82 dB */
|
||||
{28251, 1}, {15849, 0}, {17783, 0}, {19953, 0}, /* 83 ~ 86 dB */
|
||||
{22387, 0}, {25119, 0}, {28184, 0}, {31623, 0}, /* 87 ~ 90 dB */
|
||||
};
|
||||
|
||||
static const DECLARE_TLV_DB_RANGE(mic_gain_tlv,
|
||||
0, 1, TLV_DB_SCALE_ITEM(-9000, 600, 0),
|
||||
2, 5, TLV_DB_SCALE_ITEM(-8100, 300, 0),
|
||||
6, 7, TLV_DB_SCALE_ITEM(-7000, 200, 0),
|
||||
8, ARRAY_SIZE(mic_gain_table)-1, TLV_DB_SCALE_ITEM(-6500, 100, 0),
|
||||
);
|
||||
|
||||
int pdmic_get_mic_volsw(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
||||
unsigned int dgain_val, scale_val;
|
||||
int i;
|
||||
|
||||
dgain_val = (snd_soc_read(codec, PDMIC_DSPR1) & PDMIC_DSPR1_DGAIN_MASK)
|
||||
>> PDMIC_DSPR1_DGAIN_SHIFT;
|
||||
|
||||
scale_val = (snd_soc_read(codec, PDMIC_DSPR0) & PDMIC_DSPR0_SCALE_MASK)
|
||||
>> PDMIC_DSPR0_SCALE_SHIFT;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mic_gain_table); i++) {
|
||||
if ((mic_gain_table[i].dgain == dgain_val) &&
|
||||
(mic_gain_table[i].scale == scale_val))
|
||||
ucontrol->value.integer.value[0] = i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pdmic_put_mic_volsw(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
|
||||
int max = mc->max;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
val = ucontrol->value.integer.value[0];
|
||||
|
||||
if (val > max)
|
||||
return -EINVAL;
|
||||
|
||||
ret = snd_soc_update_bits(codec, PDMIC_DSPR1, PDMIC_DSPR1_DGAIN_MASK,
|
||||
mic_gain_table[val].dgain << PDMIC_DSPR1_DGAIN_SHIFT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_update_bits(codec, PDMIC_DSPR0, PDMIC_DSPR0_SCALE_MASK,
|
||||
mic_gain_table[val].scale << PDMIC_DSPR0_SCALE_SHIFT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new atmel_pdmic_snd_controls[] = {
|
||||
SOC_SINGLE_EXT_TLV("Mic Capture Volume", PDMIC_DSPR1, PDMIC_DSPR1_DGAIN_SHIFT,
|
||||
ARRAY_SIZE(mic_gain_table)-1, 0,
|
||||
pdmic_get_mic_volsw, pdmic_put_mic_volsw, mic_gain_tlv),
|
||||
|
||||
SOC_SINGLE("High Pass Filter Switch", PDMIC_DSPR0,
|
||||
PDMIC_DSPR0_HPFBYP_SHIFT, 1, 1),
|
||||
|
||||
SOC_SINGLE("SINCC Filter Switch", PDMIC_DSPR0, PDMIC_DSPR0_SINBYP_SHIFT, 1, 1),
|
||||
};
|
||||
|
||||
static int atmel_pdmic_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec);
|
||||
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(card);
|
||||
|
||||
snd_soc_update_bits(codec, PDMIC_DSPR1, PDMIC_DSPR1_OFFSET_MASK,
|
||||
(u32)(dd->pdata->mic_offset << PDMIC_DSPR1_OFFSET_SHIFT));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_pdmic = {
|
||||
.probe = atmel_pdmic_codec_probe,
|
||||
.controls = atmel_pdmic_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(atmel_pdmic_snd_controls),
|
||||
};
|
||||
|
||||
/* codec dai component */
|
||||
#define PDMIC_MR_PRESCAL_MAX_VAL 127
|
||||
|
||||
static int
|
||||
atmel_pdmic_codec_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *codec_dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
unsigned int rate_min = substream->runtime->hw.rate_min;
|
||||
unsigned int rate_max = substream->runtime->hw.rate_max;
|
||||
int fs = params_rate(params);
|
||||
int bits = params_width(params);
|
||||
unsigned long pclk_rate, gclk_rate;
|
||||
unsigned int f_pdmic;
|
||||
u32 mr_val, dspr0_val, pclk_prescal, gclk_prescal;
|
||||
|
||||
if (params_channels(params) != 1) {
|
||||
dev_err(codec->dev,
|
||||
"only supports one channel\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((fs < rate_min) || (fs > rate_max)) {
|
||||
dev_err(codec->dev,
|
||||
"sample rate is %dHz, min rate is %dHz, max rate is %dHz\n",
|
||||
fs, rate_min, rate_max);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (bits) {
|
||||
case 16:
|
||||
dspr0_val = (PDMIC_DSPR0_SIZE_16_BITS
|
||||
<< PDMIC_DSPR0_SIZE_SHIFT);
|
||||
break;
|
||||
case 32:
|
||||
dspr0_val = (PDMIC_DSPR0_SIZE_32_BITS
|
||||
<< PDMIC_DSPR0_SIZE_SHIFT);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((fs << 7) > (rate_max << 6)) {
|
||||
f_pdmic = fs << 6;
|
||||
dspr0_val |= PDMIC_DSPR0_OSR_64 << PDMIC_DSPR0_OSR_SHIFT;
|
||||
} else {
|
||||
f_pdmic = fs << 7;
|
||||
dspr0_val |= PDMIC_DSPR0_OSR_128 << PDMIC_DSPR0_OSR_SHIFT;
|
||||
}
|
||||
|
||||
pclk_rate = clk_get_rate(dd->pclk);
|
||||
gclk_rate = clk_get_rate(dd->gclk);
|
||||
|
||||
/* PRESCAL = SELCK/(2*f_pdmic) - 1*/
|
||||
pclk_prescal = (u32)(pclk_rate/(f_pdmic << 1)) - 1;
|
||||
gclk_prescal = (u32)(gclk_rate/(f_pdmic << 1)) - 1;
|
||||
|
||||
if ((pclk_prescal > PDMIC_MR_PRESCAL_MAX_VAL) ||
|
||||
(gclk_rate/((gclk_prescal + 1) << 1) <
|
||||
pclk_rate/((pclk_prescal + 1) << 1))) {
|
||||
mr_val = gclk_prescal << PDMIC_MR_PRESCAL_SHIFT;
|
||||
mr_val |= PDMIC_MR_CLKS_GCK << PDMIC_MR_CLKS_SHIFT;
|
||||
} else {
|
||||
mr_val = pclk_prescal << PDMIC_MR_PRESCAL_SHIFT;
|
||||
mr_val |= PDMIC_MR_CLKS_PCK << PDMIC_MR_CLKS_SHIFT;
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, PDMIC_MR,
|
||||
PDMIC_MR_PRESCAL_MASK | PDMIC_MR_CLKS_MASK, mr_val);
|
||||
|
||||
snd_soc_update_bits(codec, PDMIC_DSPR0,
|
||||
PDMIC_DSPR0_OSR_MASK | PDMIC_DSPR0_SIZE_MASK, dspr0_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_pdmic_codec_dai_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *codec_dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
|
||||
snd_soc_update_bits(codec, PDMIC_CR, PDMIC_CR_ENPDM_MASK,
|
||||
PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_pdmic_codec_dai_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd, struct snd_soc_dai *codec_dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
u32 val;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
val = PDMIC_CR_ENPDM_EN << PDMIC_CR_ENPDM_SHIFT;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
val = PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, PDMIC_CR, PDMIC_CR_ENPDM_MASK, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops atmel_pdmic_codec_dai_ops = {
|
||||
.hw_params = atmel_pdmic_codec_dai_hw_params,
|
||||
.prepare = atmel_pdmic_codec_dai_prepare,
|
||||
.trigger = atmel_pdmic_codec_dai_trigger,
|
||||
};
|
||||
|
||||
#define ATMEL_PDMIC_CODEC_DAI_NAME "atmel-pdmic-hifi"
|
||||
|
||||
static struct snd_soc_dai_driver atmel_pdmic_codec_dai = {
|
||||
.name = ATMEL_PDMIC_CODEC_DAI_NAME,
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = ATMEL_PDMIC_FORMATS,
|
||||
},
|
||||
.ops = &atmel_pdmic_codec_dai_ops,
|
||||
};
|
||||
|
||||
/* ASoC sound card */
|
||||
static int atmel_pdmic_asoc_card_init(struct device *dev,
|
||||
struct snd_soc_card *card)
|
||||
{
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
struct atmel_pdmic *dd = snd_soc_card_get_drvdata(card);
|
||||
|
||||
dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL);
|
||||
if (!dai_link)
|
||||
return -ENOMEM;
|
||||
|
||||
dai_link->name = "PDMIC";
|
||||
dai_link->stream_name = "PDMIC PCM";
|
||||
dai_link->codec_dai_name = ATMEL_PDMIC_CODEC_DAI_NAME;
|
||||
dai_link->cpu_dai_name = dev_name(dev);
|
||||
dai_link->codec_name = dev_name(dev);
|
||||
dai_link->platform_name = dev_name(dev);
|
||||
|
||||
card->dai_link = dai_link;
|
||||
card->num_links = 1;
|
||||
card->name = dd->pdata->card_name;
|
||||
card->dev = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atmel_pdmic_get_sample_rate(struct atmel_pdmic *dd,
|
||||
unsigned int *rate_min, unsigned int *rate_max)
|
||||
{
|
||||
u32 mic_min_freq = dd->pdata->mic_min_freq;
|
||||
u32 mic_max_freq = dd->pdata->mic_max_freq;
|
||||
u32 clk_max_rate = (u32)(clk_get_rate(dd->pclk) >> 1);
|
||||
u32 clk_min_rate = (u32)(clk_get_rate(dd->gclk) >> 8);
|
||||
|
||||
if (mic_max_freq > clk_max_rate)
|
||||
mic_max_freq = clk_max_rate;
|
||||
|
||||
if (mic_min_freq < clk_min_rate)
|
||||
mic_min_freq = clk_min_rate;
|
||||
|
||||
*rate_min = DIV_ROUND_CLOSEST(mic_min_freq, 128);
|
||||
*rate_max = mic_max_freq >> 6;
|
||||
}
|
||||
|
||||
/* PDMIC interrupt handler */
|
||||
static irqreturn_t atmel_pdmic_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct atmel_pdmic *dd = (struct atmel_pdmic *)dev_id;
|
||||
u32 pdmic_isr;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
regmap_read(dd->regmap, PDMIC_ISR, &pdmic_isr);
|
||||
|
||||
if (pdmic_isr & PDMIC_ISR_OVRE) {
|
||||
regmap_update_bits(dd->regmap, PDMIC_CR, PDMIC_CR_ENPDM_MASK,
|
||||
PDMIC_CR_ENPDM_DIS << PDMIC_CR_ENPDM_SHIFT);
|
||||
|
||||
snd_pcm_stop_xrun(dd->substream);
|
||||
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* regmap configuration */
|
||||
#define ATMEL_PDMIC_REG_MAX 0x124
|
||||
static const struct regmap_config atmel_pdmic_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = ATMEL_PDMIC_REG_MAX,
|
||||
};
|
||||
|
||||
static int atmel_pdmic_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct atmel_pdmic *dd;
|
||||
struct resource *res;
|
||||
void __iomem *io_base;
|
||||
const struct atmel_pdmic_pdata *pdata;
|
||||
struct snd_soc_card *card;
|
||||
unsigned int rate_min, rate_max;
|
||||
int ret;
|
||||
|
||||
pdata = atmel_pdmic_dt_init(dev);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
|
||||
dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
|
||||
if (!dd)
|
||||
return -ENOMEM;
|
||||
|
||||
dd->pdata = pdata;
|
||||
|
||||
dd->irq = platform_get_irq(pdev, 0);
|
||||
if (dd->irq < 0) {
|
||||
ret = dd->irq;
|
||||
dev_err(dev, "failed to could not get irq: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dd->pclk = devm_clk_get(dev, "pclk");
|
||||
if (IS_ERR(dd->pclk)) {
|
||||
ret = PTR_ERR(dd->pclk);
|
||||
dev_err(dev, "failed to get peripheral clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dd->gclk = devm_clk_get(dev, "gclk");
|
||||
if (IS_ERR(dd->gclk)) {
|
||||
ret = PTR_ERR(dd->gclk);
|
||||
dev_err(dev, "failed to get GCK: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The gclk clock frequency must always be tree times
|
||||
* lower than the pclk clock frequency
|
||||
*/
|
||||
ret = clk_set_rate(dd->gclk, clk_get_rate(dd->pclk)/3);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set GCK clock rate: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "no memory resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
io_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(io_base)) {
|
||||
ret = PTR_ERR(io_base);
|
||||
dev_err(dev, "failed to remap register memory: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dd->phy_base = res->start;
|
||||
|
||||
dd->regmap = devm_regmap_init_mmio(dev, io_base,
|
||||
&atmel_pdmic_regmap_config);
|
||||
if (IS_ERR(dd->regmap)) {
|
||||
ret = PTR_ERR(dd->regmap);
|
||||
dev_err(dev, "failed to init register map: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, dd->irq, atmel_pdmic_interrupt, 0,
|
||||
"PDMIC", (void *)dd);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
|
||||
dd->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get the minimal and maximal sample rate that micphone supports */
|
||||
atmel_pdmic_get_sample_rate(dd, &rate_min, &rate_max);
|
||||
|
||||
/* register cpu dai */
|
||||
atmel_pdmic_cpu_dai.capture.rate_min = rate_min;
|
||||
atmel_pdmic_cpu_dai.capture.rate_max = rate_max;
|
||||
ret = devm_snd_soc_register_component(dev,
|
||||
&atmel_pdmic_cpu_dai_component,
|
||||
&atmel_pdmic_cpu_dai, 1);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not register CPU DAI: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* register platform */
|
||||
ret = devm_snd_dmaengine_pcm_register(dev,
|
||||
&atmel_pdmic_dmaengine_pcm_config,
|
||||
0);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not register platform: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* register codec and codec dai */
|
||||
atmel_pdmic_codec_dai.capture.rate_min = rate_min;
|
||||
atmel_pdmic_codec_dai.capture.rate_max = rate_max;
|
||||
ret = snd_soc_register_codec(dev, &soc_codec_dev_pdmic,
|
||||
&atmel_pdmic_codec_dai, 1);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not register codec: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* register sound card */
|
||||
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
|
||||
if (!card) {
|
||||
ret = -ENOMEM;
|
||||
goto unregister_codec;
|
||||
}
|
||||
|
||||
snd_soc_card_set_drvdata(card, dd);
|
||||
platform_set_drvdata(pdev, card);
|
||||
|
||||
ret = atmel_pdmic_asoc_card_init(dev, card);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to init sound card: %d\n", ret);
|
||||
goto unregister_codec;
|
||||
}
|
||||
|
||||
ret = devm_snd_soc_register_card(dev, card);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register sound card: %d\n", ret);
|
||||
goto unregister_codec;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_codec:
|
||||
snd_soc_unregister_codec(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atmel_pdmic_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_codec(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver atmel_pdmic_driver = {
|
||||
.driver = {
|
||||
.name = "atmel-pdmic",
|
||||
.of_match_table = of_match_ptr(atmel_pdmic_of_match),
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
.probe = atmel_pdmic_probe,
|
||||
.remove = atmel_pdmic_remove,
|
||||
};
|
||||
module_platform_driver(atmel_pdmic_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Atmel PDMIC driver under ALSA SoC architecture");
|
||||
MODULE_AUTHOR("Songjun Wu <[email protected]>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
80
sound/soc/atmel/atmel-pdmic.h
Normal file
80
sound/soc/atmel/atmel-pdmic.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#ifndef __ATMEL_PDMIC_H_
|
||||
#define __ATMEL_PDMIC_H_
|
||||
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#define PDMIC_CR 0x00000000
|
||||
|
||||
#define PDMIC_CR_SWRST 0x1
|
||||
#define PDMIC_CR_SWRST_MASK BIT(0)
|
||||
#define PDMIC_CR_SWRST_SHIFT (0)
|
||||
|
||||
#define PDMIC_CR_ENPDM_DIS 0x0
|
||||
#define PDMIC_CR_ENPDM_EN 0x1
|
||||
#define PDMIC_CR_ENPDM_MASK BIT(4)
|
||||
#define PDMIC_CR_ENPDM_SHIFT (4)
|
||||
|
||||
#define PDMIC_MR 0x00000004
|
||||
|
||||
#define PDMIC_MR_CLKS_PCK 0x0
|
||||
#define PDMIC_MR_CLKS_GCK 0x1
|
||||
#define PDMIC_MR_CLKS_MASK BIT(4)
|
||||
#define PDMIC_MR_CLKS_SHIFT (4)
|
||||
|
||||
#define PDMIC_MR_PRESCAL_MASK GENMASK(14, 8)
|
||||
#define PDMIC_MR_PRESCAL_SHIFT (8)
|
||||
|
||||
#define PDMIC_CDR 0x00000014
|
||||
|
||||
#define PDMIC_IER 0x00000018
|
||||
#define PDMIC_IER_OVRE BIT(25)
|
||||
|
||||
#define PDMIC_IDR 0x0000001c
|
||||
#define PDMIC_IDR_OVRE BIT(25)
|
||||
|
||||
#define PDMIC_IMR 0x00000020
|
||||
|
||||
#define PDMIC_ISR 0x00000024
|
||||
#define PDMIC_ISR_OVRE BIT(25)
|
||||
|
||||
#define PDMIC_DSPR0 0x00000058
|
||||
|
||||
#define PDMIC_DSPR0_HPFBYP_DIS 0x1
|
||||
#define PDMIC_DSPR0_HPFBYP_EN 0x0
|
||||
#define PDMIC_DSPR0_HPFBYP_MASK BIT(1)
|
||||
#define PDMIC_DSPR0_HPFBYP_SHIFT (1)
|
||||
|
||||
#define PDMIC_DSPR0_SINBYP_DIS 0x1
|
||||
#define PDMIC_DSPR0_SINBYP_EN 0x0
|
||||
#define PDMIC_DSPR0_SINBYP_MASK BIT(2)
|
||||
#define PDMIC_DSPR0_SINBYP_SHIFT (2)
|
||||
|
||||
#define PDMIC_DSPR0_SIZE_16_BITS 0x0
|
||||
#define PDMIC_DSPR0_SIZE_32_BITS 0x1
|
||||
#define PDMIC_DSPR0_SIZE_MASK BIT(3)
|
||||
#define PDMIC_DSPR0_SIZE_SHIFT (3)
|
||||
|
||||
#define PDMIC_DSPR0_OSR_128 0x0
|
||||
#define PDMIC_DSPR0_OSR_64 0x1
|
||||
#define PDMIC_DSPR0_OSR_MASK GENMASK(6, 4)
|
||||
#define PDMIC_DSPR0_OSR_SHIFT (4)
|
||||
|
||||
#define PDMIC_DSPR0_SCALE_MASK GENMASK(11, 8)
|
||||
#define PDMIC_DSPR0_SCALE_SHIFT (8)
|
||||
|
||||
#define PDMIC_DSPR0_SHIFT_MASK GENMASK(15, 12)
|
||||
#define PDMIC_DSPR0_SHIFT_SHIFT (12)
|
||||
|
||||
#define PDMIC_DSPR1 0x0000005c
|
||||
|
||||
#define PDMIC_DSPR1_DGAIN_MASK GENMASK(14, 0)
|
||||
#define PDMIC_DSPR1_DGAIN_SHIFT (0)
|
||||
|
||||
#define PDMIC_DSPR1_OFFSET_MASK GENMASK(31, 16)
|
||||
#define PDMIC_DSPR1_OFFSET_SHIFT (16)
|
||||
|
||||
#define PDMIC_WPMR 0x000000e4
|
||||
|
||||
#define PDMIC_WPSR 0x000000e8
|
||||
|
||||
#endif
|
||||
|
|
@ -183,6 +183,7 @@ static struct platform_driver atmel_asoc_wm8904_driver = {
|
|||
.driver = {
|
||||
.name = "atmel-wm8904-audio",
|
||||
.of_match_table = of_match_ptr(atmel_asoc_wm8904_dt_ids),
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
.probe = atmel_asoc_wm8904_probe,
|
||||
.remove = atmel_asoc_wm8904_remove,
|
||||
|
|
|
|||
|
|
@ -55,9 +55,11 @@ config SND_SOC_ALL_CODECS
|
|||
select SND_SOC_CS4271_SPI if SPI_MASTER
|
||||
select SND_SOC_CS42XX8_I2C if I2C
|
||||
select SND_SOC_CS4349 if I2C
|
||||
select SND_SOC_CS47L24 if MFD_CS47L24
|
||||
select SND_SOC_CX20442 if TTY
|
||||
select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
|
||||
select SND_SOC_DA7213 if I2C
|
||||
select SND_SOC_DA7218 if I2C
|
||||
select SND_SOC_DA7219 if I2C
|
||||
select SND_SOC_DA732X if I2C
|
||||
select SND_SOC_DA9055 if I2C
|
||||
|
|
@ -68,6 +70,7 @@ config SND_SOC_ALL_CODECS
|
|||
select SND_SOC_GTM601
|
||||
select SND_SOC_HDAC_HDMI
|
||||
select SND_SOC_ICS43432
|
||||
select SND_SOC_INNO_RK3036
|
||||
select SND_SOC_ISABELLE if I2C
|
||||
select SND_SOC_JZ4740_CODEC
|
||||
select SND_SOC_LM4857 if I2C
|
||||
|
|
@ -86,14 +89,18 @@ config SND_SOC_ALL_CODECS
|
|||
select SND_SOC_PCM1681 if I2C
|
||||
select SND_SOC_PCM1792A if SPI_MASTER
|
||||
select SND_SOC_PCM3008
|
||||
select SND_SOC_PCM3168A_I2C if I2C
|
||||
select SND_SOC_PCM3168A_SPI if SPI_MASTER
|
||||
select SND_SOC_PCM512x_I2C if I2C
|
||||
select SND_SOC_PCM512x_SPI if SPI_MASTER
|
||||
select SND_SOC_RT286 if I2C
|
||||
select SND_SOC_RT298 if I2C
|
||||
select SND_SOC_RT5616 if I2C
|
||||
select SND_SOC_RT5631 if I2C
|
||||
select SND_SOC_RT5640 if I2C
|
||||
select SND_SOC_RT5645 if I2C
|
||||
select SND_SOC_RT5651 if I2C
|
||||
select SND_SOC_RT5659 if I2C
|
||||
select SND_SOC_RT5670 if I2C
|
||||
select SND_SOC_RT5677 if I2C && SPI_MASTER
|
||||
select SND_SOC_SGTL5000 if I2C
|
||||
|
|
@ -196,10 +203,12 @@ config SND_SOC_88PM860X
|
|||
|
||||
config SND_SOC_ARIZONA
|
||||
tristate
|
||||
default y if SND_SOC_CS47L24=y
|
||||
default y if SND_SOC_WM5102=y
|
||||
default y if SND_SOC_WM5110=y
|
||||
default y if SND_SOC_WM8997=y
|
||||
default y if SND_SOC_WM8998=y
|
||||
default m if SND_SOC_CS47L24=m
|
||||
default m if SND_SOC_WM5102=m
|
||||
default m if SND_SOC_WM5110=m
|
||||
default m if SND_SOC_WM8997=m
|
||||
|
|
@ -212,9 +221,12 @@ config SND_SOC_WM_HUBS
|
|||
|
||||
config SND_SOC_WM_ADSP
|
||||
tristate
|
||||
select SND_SOC_COMPRESS
|
||||
default y if SND_SOC_CS47L24=y
|
||||
default y if SND_SOC_WM5102=y
|
||||
default y if SND_SOC_WM5110=y
|
||||
default y if SND_SOC_WM2200=y
|
||||
default m if SND_SOC_CS47L24=m
|
||||
default m if SND_SOC_WM5102=m
|
||||
default m if SND_SOC_WM5110=m
|
||||
default m if SND_SOC_WM2200=m
|
||||
|
|
@ -423,6 +435,9 @@ config SND_SOC_CS4349
|
|||
tristate "Cirrus Logic CS4349 CODEC"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_CS47L24
|
||||
tristate
|
||||
|
||||
config SND_SOC_CX20442
|
||||
tristate
|
||||
depends on TTY
|
||||
|
|
@ -440,6 +455,9 @@ config SND_SOC_DA7210
|
|||
config SND_SOC_DA7213
|
||||
tristate
|
||||
|
||||
config SND_SOC_DA7218
|
||||
tristate
|
||||
|
||||
config SND_SOC_DA7219
|
||||
tristate
|
||||
|
||||
|
|
@ -477,6 +495,9 @@ config SND_SOC_HDAC_HDMI
|
|||
config SND_SOC_ICS43432
|
||||
tristate
|
||||
|
||||
config SND_SOC_INNO_RK3036
|
||||
tristate "Inno codec driver for RK3036 SoC"
|
||||
|
||||
config SND_SOC_ISABELLE
|
||||
tristate
|
||||
|
||||
|
|
@ -512,6 +533,21 @@ config SND_SOC_PCM1792A
|
|||
config SND_SOC_PCM3008
|
||||
tristate
|
||||
|
||||
config SND_SOC_PCM3168A
|
||||
tristate
|
||||
|
||||
config SND_SOC_PCM3168A_I2C
|
||||
tristate "Texas Instruments PCM3168A CODEC - I2C"
|
||||
depends on I2C
|
||||
select SND_SOC_PCM3168A
|
||||
select REGMAP_I2C
|
||||
|
||||
config SND_SOC_PCM3168A_SPI
|
||||
tristate "Texas Instruments PCM3168A CODEC - SPI"
|
||||
depends on SPI_MASTER
|
||||
select SND_SOC_PCM3168A
|
||||
select REGMAP_SPI
|
||||
|
||||
config SND_SOC_PCM512x
|
||||
tristate
|
||||
|
||||
|
|
@ -529,14 +565,18 @@ config SND_SOC_PCM512x_SPI
|
|||
|
||||
config SND_SOC_RL6231
|
||||
tristate
|
||||
default y if SND_SOC_RT5616=y
|
||||
default y if SND_SOC_RT5640=y
|
||||
default y if SND_SOC_RT5645=y
|
||||
default y if SND_SOC_RT5651=y
|
||||
default y if SND_SOC_RT5659=y
|
||||
default y if SND_SOC_RT5670=y
|
||||
default y if SND_SOC_RT5677=y
|
||||
default m if SND_SOC_RT5616=m
|
||||
default m if SND_SOC_RT5640=m
|
||||
default m if SND_SOC_RT5645=m
|
||||
default m if SND_SOC_RT5651=m
|
||||
default m if SND_SOC_RT5659=m
|
||||
default m if SND_SOC_RT5670=m
|
||||
default m if SND_SOC_RT5677=m
|
||||
|
||||
|
|
@ -555,6 +595,9 @@ config SND_SOC_RT298
|
|||
tristate
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_RT5616
|
||||
tristate
|
||||
|
||||
config SND_SOC_RT5631
|
||||
tristate "Realtek ALC5631/RT5631 CODEC"
|
||||
depends on I2C
|
||||
|
|
@ -568,6 +611,9 @@ config SND_SOC_RT5645
|
|||
config SND_SOC_RT5651
|
||||
tristate
|
||||
|
||||
config SND_SOC_RT5659
|
||||
tristate
|
||||
|
||||
config SND_SOC_RT5670
|
||||
tristate
|
||||
|
||||
|
|
@ -844,7 +890,8 @@ config SND_SOC_WM8971
|
|||
tristate
|
||||
|
||||
config SND_SOC_WM8974
|
||||
tristate
|
||||
tristate "Wolfson Microelectronics WM8974 codec"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_WM8978
|
||||
tristate "Wolfson Microelectronics WM8978 codec"
|
||||
|
|
@ -897,6 +944,7 @@ config SND_SOC_WM9712
|
|||
|
||||
config SND_SOC_WM9713
|
||||
tristate
|
||||
select REGMAP_AC97
|
||||
|
||||
# Amp
|
||||
config SND_SOC_LM4857
|
||||
|
|
|
|||
|
|
@ -47,9 +47,11 @@ snd-soc-cs4271-spi-objs := cs4271-spi.o
|
|||
snd-soc-cs42xx8-objs := cs42xx8.o
|
||||
snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
|
||||
snd-soc-cs4349-objs := cs4349.o
|
||||
snd-soc-cs47l24-objs := cs47l24.o
|
||||
snd-soc-cx20442-objs := cx20442.o
|
||||
snd-soc-da7210-objs := da7210.o
|
||||
snd-soc-da7213-objs := da7213.o
|
||||
snd-soc-da7218-objs := da7218.o
|
||||
snd-soc-da7219-objs := da7219.o da7219-aad.o
|
||||
snd-soc-da732x-objs := da732x.o
|
||||
snd-soc-da9055-objs := da9055.o
|
||||
|
|
@ -61,6 +63,7 @@ snd-soc-es8328-spi-objs := es8328-spi.o
|
|||
snd-soc-gtm601-objs := gtm601.o
|
||||
snd-soc-hdac-hdmi-objs := hdac_hdmi.o
|
||||
snd-soc-ics43432-objs := ics43432.o
|
||||
snd-soc-inno-rk3036-objs := inno_rk3036.o
|
||||
snd-soc-isabelle-objs := isabelle.o
|
||||
snd-soc-jz4740-codec-objs := jz4740.o
|
||||
snd-soc-l3-objs := l3.o
|
||||
|
|
@ -79,6 +82,9 @@ snd-soc-nau8825-objs := nau8825.o
|
|||
snd-soc-pcm1681-objs := pcm1681.o
|
||||
snd-soc-pcm1792a-codec-objs := pcm1792a.o
|
||||
snd-soc-pcm3008-objs := pcm3008.o
|
||||
snd-soc-pcm3168a-objs := pcm3168a.o
|
||||
snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
|
||||
snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o
|
||||
snd-soc-pcm512x-objs := pcm512x.o
|
||||
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
|
||||
snd-soc-pcm512x-spi-objs := pcm512x-spi.o
|
||||
|
|
@ -86,10 +92,12 @@ snd-soc-rl6231-objs := rl6231.o
|
|||
snd-soc-rl6347a-objs := rl6347a.o
|
||||
snd-soc-rt286-objs := rt286.o
|
||||
snd-soc-rt298-objs := rt298.o
|
||||
snd-soc-rt5616-objs := rt5616.o
|
||||
snd-soc-rt5631-objs := rt5631.o
|
||||
snd-soc-rt5640-objs := rt5640.o
|
||||
snd-soc-rt5645-objs := rt5645.o
|
||||
snd-soc-rt5651-objs := rt5651.o
|
||||
snd-soc-rt5659-objs := rt5659.o
|
||||
snd-soc-rt5670-objs := rt5670.o
|
||||
snd-soc-rt5677-objs := rt5677.o
|
||||
snd-soc-rt5677-spi-objs := rt5677-spi.o
|
||||
|
|
@ -243,9 +251,11 @@ obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o
|
|||
obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
|
||||
obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
|
||||
obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o
|
||||
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
|
||||
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
|
||||
obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o
|
||||
obj-$(CONFIG_SND_SOC_DA7218) += snd-soc-da7218.o
|
||||
obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o
|
||||
obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
|
||||
obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
|
||||
|
|
@ -257,6 +267,7 @@ obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
|
|||
obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o
|
||||
obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
|
||||
obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o
|
||||
obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o
|
||||
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
|
||||
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
|
||||
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
|
||||
|
|
@ -275,6 +286,9 @@ obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o
|
|||
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
|
||||
obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
|
||||
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
|
||||
obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o
|
||||
obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_PCM3168A_SPI) += snd-soc-pcm3168a-spi.o
|
||||
obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
|
||||
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
|
||||
|
|
@ -282,10 +296,12 @@ 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_RT298) += snd-soc-rt298.o
|
||||
obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o
|
||||
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
|
||||
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
|
||||
obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o
|
||||
obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o
|
||||
obj-$(CONFIG_SND_SOC_RT5659) += snd-soc-rt5659.o
|
||||
obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o
|
||||
obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o
|
||||
obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o
|
||||
|
|
|
|||
|
|
@ -70,18 +70,11 @@
|
|||
#define FMT_MASK (0xf8)
|
||||
|
||||
/* CTRL2 */
|
||||
#define DFS_MASK (3 << 2)
|
||||
#define DFS_NORMAL_SPEED (0 << 2)
|
||||
#define DFS_DOUBLE_SPEED (1 << 2)
|
||||
#define DFS_QUAD_SPEED (2 << 2)
|
||||
|
||||
struct ak4613_priv {
|
||||
struct mutex lock;
|
||||
|
||||
unsigned int fmt;
|
||||
u8 fmt_ctrl;
|
||||
int cnt;
|
||||
};
|
||||
|
||||
struct ak4613_formats {
|
||||
unsigned int width;
|
||||
unsigned int fmt;
|
||||
|
|
@ -92,6 +85,16 @@ struct ak4613_interface {
|
|||
struct ak4613_formats playback;
|
||||
};
|
||||
|
||||
struct ak4613_priv {
|
||||
struct mutex lock;
|
||||
const struct ak4613_interface *iface;
|
||||
|
||||
unsigned int fmt;
|
||||
u8 oc;
|
||||
u8 ic;
|
||||
int cnt;
|
||||
};
|
||||
|
||||
/*
|
||||
* Playback Volume
|
||||
*
|
||||
|
|
@ -126,7 +129,7 @@ static const struct reg_default ak4613_reg[] = {
|
|||
{ 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 },
|
||||
};
|
||||
|
||||
#define AUDIO_IFACE_IDX_TO_VAL(i) (i << 3)
|
||||
#define AUDIO_IFACE_TO_VAL(fmts) ((fmts - ak4613_iface) << 3)
|
||||
#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt }
|
||||
static const struct ak4613_interface ak4613_iface[] = {
|
||||
/* capture */ /* playback */
|
||||
|
|
@ -240,7 +243,7 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
|
|||
priv->cnt = 0;
|
||||
}
|
||||
if (!priv->cnt)
|
||||
priv->fmt_ctrl = NO_FMT;
|
||||
priv->iface = NULL;
|
||||
mutex_unlock(&priv->lock);
|
||||
}
|
||||
|
||||
|
|
@ -265,13 +268,35 @@ static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool ak4613_dai_fmt_matching(const struct ak4613_interface *iface,
|
||||
int is_play,
|
||||
unsigned int fmt, unsigned int width)
|
||||
{
|
||||
const struct ak4613_formats *fmts;
|
||||
|
||||
fmts = (is_play) ? &iface->playback : &iface->capture;
|
||||
|
||||
if (fmts->fmt != fmt)
|
||||
return false;
|
||||
|
||||
if (fmt == SND_SOC_DAIFMT_RIGHT_J) {
|
||||
if (fmts->width != width)
|
||||
return false;
|
||||
} else {
|
||||
if (fmts->width < width)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||
const struct ak4613_formats *fmts;
|
||||
const struct ak4613_interface *iface;
|
||||
struct device *dev = codec->dev;
|
||||
unsigned int width = params_width(params);
|
||||
unsigned int fmt = priv->fmt;
|
||||
|
|
@ -305,33 +330,27 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
|
|||
* It doesn't support TDM at this point
|
||||
*/
|
||||
fmt_ctrl = NO_FMT;
|
||||
for (i = 0; i < ARRAY_SIZE(ak4613_iface); i++) {
|
||||
fmts = (is_play) ? &ak4613_iface[i].playback :
|
||||
&ak4613_iface[i].capture;
|
||||
|
||||
if (fmts->fmt != fmt)
|
||||
continue;
|
||||
|
||||
if (fmt == SND_SOC_DAIFMT_RIGHT_J) {
|
||||
if (fmts->width != width)
|
||||
continue;
|
||||
} else {
|
||||
if (fmts->width < width)
|
||||
continue;
|
||||
}
|
||||
|
||||
fmt_ctrl = AUDIO_IFACE_IDX_TO_VAL(i);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = -EINVAL;
|
||||
if (fmt_ctrl == NO_FMT)
|
||||
goto hw_params_end;
|
||||
iface = NULL;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
if ((priv->fmt_ctrl == NO_FMT) ||
|
||||
(priv->fmt_ctrl == fmt_ctrl)) {
|
||||
priv->fmt_ctrl = fmt_ctrl;
|
||||
if (priv->iface) {
|
||||
if (ak4613_dai_fmt_matching(priv->iface, is_play, fmt, width))
|
||||
iface = priv->iface;
|
||||
} else {
|
||||
for (i = ARRAY_SIZE(ak4613_iface); i >= 0; i--) {
|
||||
if (!ak4613_dai_fmt_matching(ak4613_iface + i,
|
||||
is_play,
|
||||
fmt, width))
|
||||
continue;
|
||||
iface = ak4613_iface + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((priv->iface == NULL) ||
|
||||
(priv->iface == iface)) {
|
||||
priv->iface = iface;
|
||||
priv->cnt++;
|
||||
ret = 0;
|
||||
}
|
||||
|
|
@ -340,8 +359,13 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
|
|||
if (ret < 0)
|
||||
goto hw_params_end;
|
||||
|
||||
fmt_ctrl = AUDIO_IFACE_TO_VAL(iface);
|
||||
|
||||
snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl);
|
||||
snd_soc_write(codec, CTRL2, ctrl2);
|
||||
snd_soc_update_bits(codec, CTRL2, DFS_MASK, ctrl2);
|
||||
|
||||
snd_soc_write(codec, ICTRL, priv->ic);
|
||||
snd_soc_write(codec, OCTRL, priv->oc);
|
||||
|
||||
hw_params_end:
|
||||
if (ret < 0)
|
||||
|
|
@ -431,6 +455,28 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
|
|||
.num_dapm_routes = ARRAY_SIZE(ak4613_intercon),
|
||||
};
|
||||
|
||||
static void ak4613_parse_of(struct ak4613_priv *priv,
|
||||
struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
char prop[32];
|
||||
int i;
|
||||
|
||||
/* Input 1 - 2 */
|
||||
for (i = 0; i < 2; i++) {
|
||||
snprintf(prop, sizeof(prop), "asahi-kasei,in%d-single-end", i + 1);
|
||||
if (!of_get_property(np, prop, NULL))
|
||||
priv->ic |= 1 << i;
|
||||
}
|
||||
|
||||
/* Output 1 - 6 */
|
||||
for (i = 0; i < 6; i++) {
|
||||
snprintf(prop, sizeof(prop), "asahi-kasei,out%d-single-end", i + 1);
|
||||
if (!of_get_property(np, prop, NULL))
|
||||
priv->oc |= 1 << i;
|
||||
}
|
||||
}
|
||||
|
||||
static int ak4613_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
|
@ -458,7 +504,9 @@ static int ak4613_i2c_probe(struct i2c_client *i2c,
|
|||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->fmt_ctrl = NO_FMT;
|
||||
ak4613_parse_of(priv, dev);
|
||||
|
||||
priv->iface = NULL;
|
||||
priv->cnt = 0;
|
||||
|
||||
mutex_init(&priv->lock);
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(arizona_init_gpio);
|
||||
|
||||
const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
|
||||
const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
|
||||
"None",
|
||||
"Tone Generator 1",
|
||||
"Tone Generator 2",
|
||||
|
|
@ -418,7 +418,7 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(arizona_mixer_texts);
|
||||
|
||||
int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
|
||||
unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
|
||||
0x00, /* None */
|
||||
0x04, /* Tone */
|
||||
0x05,
|
||||
|
|
@ -555,12 +555,12 @@ const char *arizona_sample_rate_val_to_name(unsigned int rate_val)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(arizona_sample_rate_val_to_name);
|
||||
|
||||
const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = {
|
||||
const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = {
|
||||
"SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate",
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(arizona_rate_text);
|
||||
|
||||
const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = {
|
||||
const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = {
|
||||
0, 1, 2, 8,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(arizona_rate_val);
|
||||
|
|
@ -702,6 +702,100 @@ const struct soc_enum arizona_in_dmic_osr[] = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(arizona_in_dmic_osr);
|
||||
|
||||
static const char * const arizona_anc_input_src_text[] = {
|
||||
"None", "IN1", "IN2", "IN3", "IN4",
|
||||
};
|
||||
|
||||
static const char * const arizona_anc_channel_src_text[] = {
|
||||
"None", "Left", "Right", "Combine",
|
||||
};
|
||||
|
||||
const struct soc_enum arizona_anc_input_src[] = {
|
||||
SOC_ENUM_SINGLE(ARIZONA_ANC_SRC,
|
||||
ARIZONA_IN_RXANCL_SEL_SHIFT,
|
||||
ARRAY_SIZE(arizona_anc_input_src_text),
|
||||
arizona_anc_input_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_FCL_ADC_REFORMATTER_CONTROL,
|
||||
ARIZONA_FCL_MIC_MODE_SEL,
|
||||
ARRAY_SIZE(arizona_anc_channel_src_text),
|
||||
arizona_anc_channel_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_ANC_SRC,
|
||||
ARIZONA_IN_RXANCR_SEL_SHIFT,
|
||||
ARRAY_SIZE(arizona_anc_input_src_text),
|
||||
arizona_anc_input_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_FCR_ADC_REFORMATTER_CONTROL,
|
||||
ARIZONA_FCR_MIC_MODE_SEL,
|
||||
ARRAY_SIZE(arizona_anc_channel_src_text),
|
||||
arizona_anc_channel_src_text),
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(arizona_anc_input_src);
|
||||
|
||||
static const char * const arizona_anc_ng_texts[] = {
|
||||
"None",
|
||||
"Internal",
|
||||
"External",
|
||||
};
|
||||
|
||||
SOC_ENUM_SINGLE_DECL(arizona_anc_ng_enum, SND_SOC_NOPM, 0,
|
||||
arizona_anc_ng_texts);
|
||||
EXPORT_SYMBOL_GPL(arizona_anc_ng_enum);
|
||||
|
||||
static const char * const arizona_output_anc_src_text[] = {
|
||||
"None", "RXANCL", "RXANCR",
|
||||
};
|
||||
|
||||
const struct soc_enum arizona_output_anc_src[] = {
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L,
|
||||
ARIZONA_OUT1L_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1R,
|
||||
ARIZONA_OUT1R_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2L,
|
||||
ARIZONA_OUT2L_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2R,
|
||||
ARIZONA_OUT2R_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L,
|
||||
ARIZONA_OUT3L_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_DAC_VOLUME_LIMIT_3R,
|
||||
ARIZONA_OUT3R_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_4L,
|
||||
ARIZONA_OUT4L_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_4R,
|
||||
ARIZONA_OUT4R_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_5L,
|
||||
ARIZONA_OUT5L_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_5R,
|
||||
ARIZONA_OUT5R_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_6L,
|
||||
ARIZONA_OUT6L_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_6R,
|
||||
ARIZONA_OUT6R_ANC_SRC_SHIFT,
|
||||
ARRAY_SIZE(arizona_output_anc_src_text),
|
||||
arizona_output_anc_src_text),
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(arizona_output_anc_src);
|
||||
|
||||
static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
|
||||
{
|
||||
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||
|
|
@ -1023,6 +1117,31 @@ void arizona_init_dvfs(struct arizona_priv *priv)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(arizona_init_dvfs);
|
||||
|
||||
int arizona_anc_ev(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol,
|
||||
int event)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
|
||||
unsigned int mask = 0x3 << w->shift;
|
||||
unsigned int val;
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
val = 1 << w->shift;
|
||||
break;
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
val = 1 << (w->shift + 1);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, ARIZONA_CLOCK_CONTROL, mask, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(arizona_anc_ev);
|
||||
|
||||
static unsigned int arizona_opclk_ref_48k_rates[] = {
|
||||
6144000,
|
||||
12288000,
|
||||
|
|
@ -1095,7 +1214,7 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
|
|||
unsigned int reg;
|
||||
unsigned int mask = ARIZONA_SYSCLK_FREQ_MASK | ARIZONA_SYSCLK_SRC_MASK;
|
||||
unsigned int val = source << ARIZONA_SYSCLK_SRC_SHIFT;
|
||||
unsigned int *clk;
|
||||
int *clk;
|
||||
|
||||
switch (clk_id) {
|
||||
case ARIZONA_CLK_SYSCLK:
|
||||
|
|
@ -1901,18 +2020,18 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
|
|||
}
|
||||
|
||||
switch (fll->arizona->type) {
|
||||
case WM5102:
|
||||
case WM8997:
|
||||
return init_ratio;
|
||||
case WM5110:
|
||||
case WM8280:
|
||||
if (fll->arizona->rev < 3 || sync)
|
||||
return init_ratio;
|
||||
break;
|
||||
case WM8998:
|
||||
case WM1814:
|
||||
default:
|
||||
if (sync)
|
||||
return init_ratio;
|
||||
break;
|
||||
default:
|
||||
return init_ratio;
|
||||
}
|
||||
|
||||
cfg->fratio = init_ratio - 1;
|
||||
|
|
@ -2093,9 +2212,9 @@ static int arizona_enable_fll(struct arizona_fll *fll)
|
|||
/* Facilitate smooth refclk across the transition */
|
||||
regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9,
|
||||
ARIZONA_FLL1_GAIN_MASK, 0);
|
||||
regmap_update_bits_async(fll->arizona->regmap, fll->base + 1,
|
||||
ARIZONA_FLL1_FREERUN,
|
||||
ARIZONA_FLL1_FREERUN);
|
||||
regmap_update_bits(fll->arizona->regmap, fll->base + 1,
|
||||
ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN);
|
||||
udelay(32);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@
|
|||
#define ARIZONA_CLK_98MHZ 5
|
||||
#define ARIZONA_CLK_147MHZ 6
|
||||
|
||||
#define ARIZONA_MAX_DAI 6
|
||||
#define ARIZONA_MAX_DAI 8
|
||||
#define ARIZONA_MAX_ADSP 4
|
||||
|
||||
#define ARIZONA_DVFS_SR1_RQ 0x001
|
||||
|
|
@ -96,8 +96,8 @@ struct arizona_priv {
|
|||
#define ARIZONA_NUM_MIXER_INPUTS 104
|
||||
|
||||
extern const unsigned int arizona_mixer_tlv[];
|
||||
extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
|
||||
extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
|
||||
extern const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
|
||||
extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
|
||||
|
||||
#define ARIZONA_GAINMUX_CONTROLS(name, base) \
|
||||
SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \
|
||||
|
|
@ -216,8 +216,8 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
|
|||
#define ARIZONA_RATE_ENUM_SIZE 4
|
||||
#define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14
|
||||
|
||||
extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
|
||||
extern const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE];
|
||||
extern const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
|
||||
extern const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE];
|
||||
extern const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
|
||||
extern const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
|
||||
|
||||
|
|
@ -242,6 +242,10 @@ extern const struct soc_enum arizona_in_dmic_osr[];
|
|||
|
||||
extern const struct snd_kcontrol_new arizona_adsp2_rate_controls[];
|
||||
|
||||
extern const struct soc_enum arizona_anc_input_src[];
|
||||
extern const struct soc_enum arizona_anc_ng_enum;
|
||||
extern const struct soc_enum arizona_output_anc_src[];
|
||||
|
||||
extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol,
|
||||
int event);
|
||||
|
|
@ -251,6 +255,9 @@ extern int arizona_out_ev(struct snd_soc_dapm_widget *w,
|
|||
extern int arizona_hp_ev(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol,
|
||||
int event);
|
||||
extern int arizona_anc_ev(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol,
|
||||
int event);
|
||||
|
||||
extern int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
|
|
|
|||
1148
sound/soc/codecs/cs47l24.c
Normal file
1148
sound/soc/codecs/cs47l24.c
Normal file
File diff suppressed because it is too large
Load diff
23
sound/soc/codecs/cs47l24.h
Normal file
23
sound/soc/codecs/cs47l24.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* cs47l24.h -- ALSA SoC Audio driver for Cirrus Logic CS47L24
|
||||
*
|
||||
* Copyright 2015 Cirrus Logic Inc.
|
||||
*
|
||||
* Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _CS47L24_H
|
||||
#define _CS47L24_H
|
||||
|
||||
#include "arizona.h"
|
||||
|
||||
#define CS47L24_FLL1 1
|
||||
#define CS47L24_FLL2 2
|
||||
#define CS47L24_FLL1_REFCLK 3
|
||||
#define CS47L24_FLL2_REFCLK 4
|
||||
|
||||
#endif
|
||||
3314
sound/soc/codecs/da7218.c
Normal file
3314
sound/soc/codecs/da7218.c
Normal file
File diff suppressed because it is too large
Load diff
1414
sound/soc/codecs/da7218.h
Normal file
1414
sound/soc/codecs/da7218.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -968,10 +968,11 @@ static const struct snd_soc_dapm_route da7219_audio_map[] = {
|
|||
{"Mixin PGA", NULL, "Mic PGA"},
|
||||
{"ADC", NULL, "Mixin PGA"},
|
||||
|
||||
{"Sidetone Filter", NULL, "ADC"},
|
||||
{"Mixer In", NULL, "Mixer In Supply"},
|
||||
{"Mixer In", "Mic Switch", "ADC"},
|
||||
|
||||
{"Sidetone Filter", NULL, "Mixer In"},
|
||||
|
||||
{"Tone Generator", NULL, "TONE"},
|
||||
|
||||
DA7219_OUT_DAI_MUX_ROUTES("Out DAIL Mux"),
|
||||
|
|
@ -1073,11 +1074,8 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
|
|||
u32 freq_ref;
|
||||
u64 frac_div;
|
||||
|
||||
/* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */
|
||||
if (da7219->mclk_rate == 32768) {
|
||||
indiv_bits = DA7219_PLL_INDIV_2_5_MHZ;
|
||||
indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL;
|
||||
} else if (da7219->mclk_rate < 2000000) {
|
||||
/* Verify 2MHz - 54MHz MCLK provided, and set input divider */
|
||||
if (da7219->mclk_rate < 2000000) {
|
||||
dev_err(codec->dev, "PLL input clock %d below valid range\n",
|
||||
da7219->mclk_rate);
|
||||
return -EINVAL;
|
||||
|
|
@ -1118,9 +1116,6 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
|
|||
case DA7219_SYSCLK_PLL_SRM:
|
||||
pll_ctrl |= DA7219_PLL_MODE_SRM;
|
||||
break;
|
||||
case DA7219_SYSCLK_PLL_32KHZ:
|
||||
pll_ctrl |= DA7219_PLL_MODE_32KHZ;
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "Invalid PLL config\n");
|
||||
return -EINVAL;
|
||||
|
|
@ -1306,7 +1301,7 @@ static int da7219_hw_params(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
channels = params_channels(params);
|
||||
if ((channels < 1) | (channels > DA7219_DAI_CH_NUM_MAX)) {
|
||||
if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
|
||||
dev_err(codec->dev,
|
||||
"Invalid number of channels, only 1 to %d supported\n",
|
||||
DA7219_DAI_CH_NUM_MAX);
|
||||
|
|
@ -1405,28 +1400,12 @@ static const struct of_device_id da7219_of_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, da7219_of_match);
|
||||
|
||||
static enum da7219_ldo_lvl_sel da7219_of_ldo_lvl(struct snd_soc_codec *codec,
|
||||
u32 val)
|
||||
{
|
||||
switch (val) {
|
||||
case 1050:
|
||||
return DA7219_LDO_LVL_SEL_1_05V;
|
||||
case 1100:
|
||||
return DA7219_LDO_LVL_SEL_1_10V;
|
||||
case 1200:
|
||||
return DA7219_LDO_LVL_SEL_1_20V;
|
||||
case 1400:
|
||||
return DA7219_LDO_LVL_SEL_1_40V;
|
||||
default:
|
||||
dev_warn(codec->dev, "Invalid LDO level");
|
||||
return DA7219_LDO_LVL_SEL_1_05V;
|
||||
}
|
||||
}
|
||||
|
||||
static enum da7219_micbias_voltage
|
||||
da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
|
||||
{
|
||||
switch (val) {
|
||||
case 1600:
|
||||
return DA7219_MICBIAS_1_6V;
|
||||
case 1800:
|
||||
return DA7219_MICBIAS_1_8V;
|
||||
case 2000:
|
||||
|
|
@ -1469,9 +1448,6 @@ static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec)
|
|||
if (!pdata)
|
||||
return NULL;
|
||||
|
||||
if (of_property_read_u32(np, "dlg,ldo-lvl", &of_val32) >= 0)
|
||||
pdata->ldo_lvl_sel = da7219_of_ldo_lvl(codec, of_val32);
|
||||
|
||||
if (of_property_read_u32(np, "dlg,micbias-lvl", &of_val32) >= 0)
|
||||
pdata->micbias_lvl = da7219_of_micbias_lvl(codec, of_val32);
|
||||
else
|
||||
|
|
@ -1516,24 +1492,13 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec,
|
|||
snd_soc_update_bits(codec, DA7219_REFERENCES,
|
||||
DA7219_BIAS_EN_MASK,
|
||||
DA7219_BIAS_EN_MASK);
|
||||
|
||||
/* Enable Internal Digital LDO */
|
||||
snd_soc_update_bits(codec, DA7219_LDO_CTRL,
|
||||
DA7219_LDO_EN_MASK,
|
||||
DA7219_LDO_EN_MASK);
|
||||
}
|
||||
break;
|
||||
case SND_SOC_BIAS_OFF:
|
||||
/* Only disable if jack detection not active */
|
||||
if (!da7219->aad->jack) {
|
||||
/* Bypass Internal Digital LDO */
|
||||
snd_soc_update_bits(codec, DA7219_LDO_CTRL,
|
||||
DA7219_LDO_EN_MASK, 0);
|
||||
|
||||
/* Master bias */
|
||||
/* Only disable master bias if jack detection not active */
|
||||
if (!da7219->aad->jack)
|
||||
snd_soc_update_bits(codec, DA7219_REFERENCES,
|
||||
DA7219_BIAS_EN_MASK, 0);
|
||||
}
|
||||
|
||||
/* MCLK */
|
||||
if (da7219->mclk)
|
||||
|
|
@ -1600,21 +1565,9 @@ static void da7219_handle_pdata(struct snd_soc_codec *codec)
|
|||
if (pdata) {
|
||||
u8 micbias_lvl = 0;
|
||||
|
||||
/* Internal LDO */
|
||||
switch (pdata->ldo_lvl_sel) {
|
||||
case DA7219_LDO_LVL_SEL_1_05V:
|
||||
case DA7219_LDO_LVL_SEL_1_10V:
|
||||
case DA7219_LDO_LVL_SEL_1_20V:
|
||||
case DA7219_LDO_LVL_SEL_1_40V:
|
||||
snd_soc_update_bits(codec, DA7219_LDO_CTRL,
|
||||
DA7219_LDO_LEVEL_SELECT_MASK,
|
||||
(pdata->ldo_lvl_sel <<
|
||||
DA7219_LDO_LEVEL_SELECT_SHIFT));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Mic Bias voltages */
|
||||
switch (pdata->micbias_lvl) {
|
||||
case DA7219_MICBIAS_1_6V:
|
||||
case DA7219_MICBIAS_1_8V:
|
||||
case DA7219_MICBIAS_2_0V:
|
||||
case DA7219_MICBIAS_2_2V:
|
||||
|
|
@ -1662,10 +1615,12 @@ static int da7219_probe(struct snd_soc_codec *codec)
|
|||
/* Check if MCLK provided */
|
||||
da7219->mclk = devm_clk_get(codec->dev, "mclk");
|
||||
if (IS_ERR(da7219->mclk)) {
|
||||
if (PTR_ERR(da7219->mclk) != -ENOENT)
|
||||
return PTR_ERR(da7219->mclk);
|
||||
else
|
||||
if (PTR_ERR(da7219->mclk) != -ENOENT) {
|
||||
ret = PTR_ERR(da7219->mclk);
|
||||
goto err_disable_reg;
|
||||
} else {
|
||||
da7219->mclk = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Default PC counter to free-running */
|
||||
|
|
@ -1693,7 +1648,16 @@ static int da7219_probe(struct snd_soc_codec *codec)
|
|||
snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
|
||||
|
||||
/* Initialise AAD block */
|
||||
return da7219_aad_init(codec);
|
||||
ret = da7219_aad_init(codec);
|
||||
if (ret)
|
||||
goto err_disable_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable_reg:
|
||||
regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int da7219_remove(struct snd_soc_codec *codec)
|
||||
|
|
@ -1776,7 +1740,7 @@ static struct reg_default da7219_reg_defaults[] = {
|
|||
{ DA7219_DIG_ROUTING_DAC, 0x32 },
|
||||
{ DA7219_DAI_OFFSET_LOWER, 0x00 },
|
||||
{ DA7219_DAI_OFFSET_UPPER, 0x00 },
|
||||
{ DA7219_REFERENCES, 0x00 },
|
||||
{ DA7219_REFERENCES, 0x08 },
|
||||
{ DA7219_MIXIN_L_SELECT, 0x00 },
|
||||
{ DA7219_MIXIN_L_GAIN, 0x03 },
|
||||
{ DA7219_ADC_L_GAIN, 0x6F },
|
||||
|
|
@ -1811,7 +1775,6 @@ static struct reg_default da7219_reg_defaults[] = {
|
|||
{ DA7219_CHIP_ID1, 0x23 },
|
||||
{ DA7219_CHIP_ID2, 0x93 },
|
||||
{ DA7219_CHIP_REVISION, 0x00 },
|
||||
{ DA7219_LDO_CTRL, 0x00 },
|
||||
{ DA7219_IO_CTRL, 0x00 },
|
||||
{ DA7219_GAIN_RAMP_CTRL, 0x00 },
|
||||
{ DA7219_PC_COUNT, 0x02 },
|
||||
|
|
|
|||
|
|
@ -85,7 +85,6 @@
|
|||
#define DA7219_CHIP_ID1 0x81
|
||||
#define DA7219_CHIP_ID2 0x82
|
||||
#define DA7219_CHIP_REVISION 0x83
|
||||
#define DA7219_LDO_CTRL 0x90
|
||||
#define DA7219_IO_CTRL 0x91
|
||||
#define DA7219_GAIN_RAMP_CTRL 0x92
|
||||
#define DA7219_PC_COUNT 0x94
|
||||
|
|
@ -207,7 +206,6 @@
|
|||
#define DA7219_PLL_MODE_BYPASS (0x0 << 6)
|
||||
#define DA7219_PLL_MODE_NORMAL (0x1 << 6)
|
||||
#define DA7219_PLL_MODE_SRM (0x2 << 6)
|
||||
#define DA7219_PLL_MODE_32KHZ (0x3 << 6)
|
||||
|
||||
/* DA7219_PLL_FRAC_TOP = 0x22 */
|
||||
#define DA7219_PLL_FBDIV_FRAC_TOP_SHIFT 0
|
||||
|
|
@ -569,12 +567,6 @@
|
|||
#define DA7219_CHIP_MAJOR_SHIFT 4
|
||||
#define DA7219_CHIP_MAJOR_MASK (0xF << 4)
|
||||
|
||||
/* DA7219_LDO_CTRL = 0x90 */
|
||||
#define DA7219_LDO_LEVEL_SELECT_SHIFT 4
|
||||
#define DA7219_LDO_LEVEL_SELECT_MASK (0x3 << 4)
|
||||
#define DA7219_LDO_EN_SHIFT 7
|
||||
#define DA7219_LDO_EN_MASK (0x1 << 7)
|
||||
|
||||
/* DA7219_IO_CTRL = 0x91 */
|
||||
#define DA7219_IO_VOLTAGE_LEVEL_SHIFT 0
|
||||
#define DA7219_IO_VOLTAGE_LEVEL_MASK (0x1 << 0)
|
||||
|
|
@ -787,7 +779,6 @@ enum da7219_sys_clk {
|
|||
DA7219_SYSCLK_MCLK = 0,
|
||||
DA7219_SYSCLK_PLL,
|
||||
DA7219_SYSCLK_PLL_SRM,
|
||||
DA7219_SYSCLK_PLL_32KHZ
|
||||
};
|
||||
|
||||
/* Regulators */
|
||||
|
|
|
|||
490
sound/soc/codecs/inno_rk3036.c
Normal file
490
sound/soc/codecs/inno_rk3036.c
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
/*
|
||||
* Driver of Inno codec for rk3036 by Rockchip Inc.
|
||||
*
|
||||
* Author: Rockchip Inc.
|
||||
* Author: Zheng ShunQian<zhengsq@rock-chips.com>
|
||||
*/
|
||||
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/soc-dai.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "inno_rk3036.h"
|
||||
|
||||
struct rk3036_codec_priv {
|
||||
void __iomem *base;
|
||||
struct clk *pclk;
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static const DECLARE_TLV_DB_MINMAX(rk3036_codec_hp_tlv, -39, 0);
|
||||
|
||||
static int rk3036_codec_antipop_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
||||
uinfo->count = 2;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3036_codec_antipop_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
int val, ret, regval;
|
||||
|
||||
ret = snd_soc_component_read(component, INNO_R09, ®val);
|
||||
if (ret)
|
||||
return ret;
|
||||
val = ((regval >> INNO_R09_HPL_ANITPOP_SHIFT) &
|
||||
INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
|
||||
ucontrol->value.integer.value[0] = val;
|
||||
|
||||
val = ((regval >> INNO_R09_HPR_ANITPOP_SHIFT) &
|
||||
INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
|
||||
ucontrol->value.integer.value[1] = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3036_codec_antipop_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
int val, ret, regmsk;
|
||||
|
||||
val = (ucontrol->value.integer.value[0] ?
|
||||
INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<
|
||||
INNO_R09_HPL_ANITPOP_SHIFT;
|
||||
val |= (ucontrol->value.integer.value[1] ?
|
||||
INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<
|
||||
INNO_R09_HPR_ANITPOP_SHIFT;
|
||||
|
||||
regmsk = INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPL_ANITPOP_SHIFT |
|
||||
INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPR_ANITPOP_SHIFT;
|
||||
|
||||
ret = snd_soc_component_update_bits(component, INNO_R09,
|
||||
regmsk, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SOC_RK3036_CODEC_ANTIPOP_DECL(xname) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
|
||||
.info = rk3036_codec_antipop_info, .get = rk3036_codec_antipop_get, \
|
||||
.put = rk3036_codec_antipop_put, }
|
||||
|
||||
static const struct snd_kcontrol_new rk3036_codec_dapm_controls[] = {
|
||||
SOC_DOUBLE_R_RANGE_TLV("Headphone Volume", INNO_R07, INNO_R08,
|
||||
INNO_HP_GAIN_SHIFT, INNO_HP_GAIN_N39DB,
|
||||
INNO_HP_GAIN_0DB, 0, rk3036_codec_hp_tlv),
|
||||
SOC_DOUBLE("Zero Cross Switch", INNO_R06, INNO_R06_VOUTL_CZ_SHIFT,
|
||||
INNO_R06_VOUTR_CZ_SHIFT, 1, 0),
|
||||
SOC_DOUBLE("Headphone Switch", INNO_R09, INNO_R09_HPL_MUTE_SHIFT,
|
||||
INNO_R09_HPR_MUTE_SHIFT, 1, 0),
|
||||
SOC_RK3036_CODEC_ANTIPOP_DECL("Anti-pop Switch"),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new rk3036_codec_hpl_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("DAC Left Out Switch", INNO_R09,
|
||||
INNO_R09_DACL_SWITCH_SHIFT, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new rk3036_codec_hpr_mixer_controls[] = {
|
||||
SOC_DAPM_SINGLE("DAC Right Out Switch", INNO_R09,
|
||||
INNO_R09_DACR_SWITCH_SHIFT, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new rk3036_codec_hpl_switch_controls[] = {
|
||||
SOC_DAPM_SINGLE("HP Left Out Switch", INNO_R05,
|
||||
INNO_R05_HPL_WORK_SHIFT, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new rk3036_codec_hpr_switch_controls[] = {
|
||||
SOC_DAPM_SINGLE("HP Right Out Switch", INNO_R05,
|
||||
INNO_R05_HPR_WORK_SHIFT, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget rk3036_codec_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY_S("DAC PWR", 1, INNO_R06,
|
||||
INNO_R06_DAC_EN_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DACL VREF", 2, INNO_R04,
|
||||
INNO_R04_DACL_VREF_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DACR VREF", 2, INNO_R04,
|
||||
INNO_R04_DACR_VREF_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DACL HiLo VREF", 3, INNO_R06,
|
||||
INNO_R06_DACL_HILO_VREF_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DACR HiLo VREF", 3, INNO_R06,
|
||||
INNO_R06_DACR_HILO_VREF_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DACR CLK", 3, INNO_R04,
|
||||
INNO_R04_DACR_CLK_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DACL CLK", 3, INNO_R04,
|
||||
INNO_R04_DACL_CLK_SHIFT, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_DAC("DACL", "Left Playback", INNO_R04,
|
||||
INNO_R04_DACL_SW_SHIFT, 0),
|
||||
SND_SOC_DAPM_DAC("DACR", "Right Playback", INNO_R04,
|
||||
INNO_R04_DACR_SW_SHIFT, 0),
|
||||
|
||||
SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
|
||||
rk3036_codec_hpl_mixer_controls,
|
||||
ARRAY_SIZE(rk3036_codec_hpl_mixer_controls)),
|
||||
SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
|
||||
rk3036_codec_hpr_mixer_controls,
|
||||
ARRAY_SIZE(rk3036_codec_hpr_mixer_controls)),
|
||||
|
||||
SND_SOC_DAPM_PGA("HP Left Out", INNO_R05,
|
||||
INNO_R05_HPL_EN_SHIFT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("HP Right Out", INNO_R05,
|
||||
INNO_R05_HPR_EN_SHIFT, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_MIXER("HP Left Switch", SND_SOC_NOPM, 0, 0,
|
||||
rk3036_codec_hpl_switch_controls,
|
||||
ARRAY_SIZE(rk3036_codec_hpl_switch_controls)),
|
||||
SND_SOC_DAPM_MIXER("HP Right Switch", SND_SOC_NOPM, 0, 0,
|
||||
rk3036_codec_hpr_switch_controls,
|
||||
ARRAY_SIZE(rk3036_codec_hpr_switch_controls)),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("HPL"),
|
||||
SND_SOC_DAPM_OUTPUT("HPR"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route rk3036_codec_dapm_routes[] = {
|
||||
{"DACL VREF", NULL, "DAC PWR"},
|
||||
{"DACR VREF", NULL, "DAC PWR"},
|
||||
{"DACL HiLo VREF", NULL, "DAC PWR"},
|
||||
{"DACR HiLo VREF", NULL, "DAC PWR"},
|
||||
{"DACL CLK", NULL, "DAC PWR"},
|
||||
{"DACR CLK", NULL, "DAC PWR"},
|
||||
|
||||
{"DACL", NULL, "DACL VREF"},
|
||||
{"DACL", NULL, "DACL HiLo VREF"},
|
||||
{"DACL", NULL, "DACL CLK"},
|
||||
{"DACR", NULL, "DACR VREF"},
|
||||
{"DACR", NULL, "DACR HiLo VREF"},
|
||||
{"DACR", NULL, "DACR CLK"},
|
||||
|
||||
{"Left Headphone Mixer", "DAC Left Out Switch", "DACL"},
|
||||
{"Right Headphone Mixer", "DAC Right Out Switch", "DACR"},
|
||||
{"HP Left Out", NULL, "Left Headphone Mixer"},
|
||||
{"HP Right Out", NULL, "Right Headphone Mixer"},
|
||||
|
||||
{"HP Left Switch", "HP Left Out Switch", "HP Left Out"},
|
||||
{"HP Right Switch", "HP Right Out Switch", "HP Right Out"},
|
||||
|
||||
{"HPL", NULL, "HP Left Switch"},
|
||||
{"HPR", NULL, "HP Right Switch"},
|
||||
};
|
||||
|
||||
static int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
unsigned int reg01_val = 0, reg02_val = 0, reg03_val = 0;
|
||||
|
||||
dev_dbg(codec->dev, "rk3036_codec dai set fmt : %08x\n", fmt);
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
reg01_val |= INNO_R01_PINDIR_IN_SLAVE |
|
||||
INNO_R01_I2SMODE_SLAVE;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
reg01_val |= INNO_R01_PINDIR_OUT_MASTER |
|
||||
INNO_R01_I2SMODE_MASTER;
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "invalid fmt\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
reg02_val |= INNO_R02_DACM_PCM;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
reg02_val |= INNO_R02_DACM_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
reg02_val |= INNO_R02_DACM_RJM;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
reg02_val |= INNO_R02_DACM_LJM;
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "set dai format failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
reg02_val |= INNO_R02_LRCP_NORMAL;
|
||||
reg03_val |= INNO_R03_BCP_NORMAL;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
reg02_val |= INNO_R02_LRCP_REVERSAL;
|
||||
reg03_val |= INNO_R03_BCP_REVERSAL;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
reg02_val |= INNO_R02_LRCP_REVERSAL;
|
||||
reg03_val |= INNO_R03_BCP_NORMAL;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
reg02_val |= INNO_R02_LRCP_NORMAL;
|
||||
reg03_val |= INNO_R03_BCP_REVERSAL;
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "set dai format failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, INNO_R01, INNO_R01_I2SMODE_MSK |
|
||||
INNO_R01_PINDIR_MSK, reg01_val);
|
||||
snd_soc_update_bits(codec, INNO_R02, INNO_R02_LRCP_MSK |
|
||||
INNO_R02_DACM_MSK, reg02_val);
|
||||
snd_soc_update_bits(codec, INNO_R03, INNO_R03_BCP_MSK, reg03_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3036_codec_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
unsigned int reg02_val = 0, reg03_val = 0;
|
||||
|
||||
switch (params_format(hw_params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
reg02_val |= INNO_R02_VWL_16BIT;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
reg02_val |= INNO_R02_VWL_20BIT;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
reg02_val |= INNO_R02_VWL_24BIT;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
reg02_val |= INNO_R02_VWL_32BIT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg02_val |= INNO_R02_LRCP_NORMAL;
|
||||
reg03_val |= INNO_R03_FWL_32BIT | INNO_R03_DACR_WORK;
|
||||
|
||||
snd_soc_update_bits(codec, INNO_R02, INNO_R02_LRCP_MSK |
|
||||
INNO_R02_VWL_MSK, reg02_val);
|
||||
snd_soc_update_bits(codec, INNO_R03, INNO_R03_DACR_MSK |
|
||||
INNO_R03_FWL_MSK, reg03_val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define RK3036_CODEC_RATES (SNDRV_PCM_RATE_8000 | \
|
||||
SNDRV_PCM_RATE_16000 | \
|
||||
SNDRV_PCM_RATE_32000 | \
|
||||
SNDRV_PCM_RATE_44100 | \
|
||||
SNDRV_PCM_RATE_48000 | \
|
||||
SNDRV_PCM_RATE_96000)
|
||||
|
||||
#define RK3036_CODEC_FMTS (SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S20_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
static struct snd_soc_dai_ops rk3036_codec_dai_ops = {
|
||||
.set_fmt = rk3036_codec_dai_set_fmt,
|
||||
.hw_params = rk3036_codec_dai_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver rk3036_codec_dai_driver[] = {
|
||||
{
|
||||
.name = "rk3036-codec-dai",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = RK3036_CODEC_RATES,
|
||||
.formats = RK3036_CODEC_FMTS,
|
||||
},
|
||||
.ops = &rk3036_codec_dai_ops,
|
||||
.symmetric_rates = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static void rk3036_codec_reset(struct snd_soc_codec *codec)
|
||||
{
|
||||
snd_soc_write(codec, INNO_R00,
|
||||
INNO_R00_CSR_RESET | INNO_R00_CDCR_RESET);
|
||||
snd_soc_write(codec, INNO_R00,
|
||||
INNO_R00_CSR_WORK | INNO_R00_CDCR_WORK);
|
||||
}
|
||||
|
||||
static int rk3036_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
rk3036_codec_reset(codec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3036_codec_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
rk3036_codec_reset(codec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk3036_codec_set_bias_level(struct snd_soc_codec *codec,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
/* set a big current for capacitor charging. */
|
||||
snd_soc_write(codec, INNO_R10, INNO_R10_MAX_CUR);
|
||||
/* start precharge */
|
||||
snd_soc_write(codec, INNO_R06, INNO_R06_DAC_PRECHARGE);
|
||||
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_OFF:
|
||||
/* set a big current for capacitor discharging. */
|
||||
snd_soc_write(codec, INNO_R10, INNO_R10_MAX_CUR);
|
||||
/* start discharge. */
|
||||
snd_soc_write(codec, INNO_R06, INNO_R06_DAC_DISCHARGE);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver rk3036_codec_driver = {
|
||||
.probe = rk3036_codec_probe,
|
||||
.remove = rk3036_codec_remove,
|
||||
.set_bias_level = rk3036_codec_set_bias_level,
|
||||
.controls = rk3036_codec_dapm_controls,
|
||||
.num_controls = ARRAY_SIZE(rk3036_codec_dapm_controls),
|
||||
.dapm_routes = rk3036_codec_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(rk3036_codec_dapm_routes),
|
||||
.dapm_widgets = rk3036_codec_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(rk3036_codec_dapm_widgets),
|
||||
};
|
||||
|
||||
static const struct regmap_config rk3036_codec_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
};
|
||||
|
||||
#define GRF_SOC_CON0 0x00140
|
||||
#define GRF_ACODEC_SEL (BIT(10) | BIT(16 + 10))
|
||||
|
||||
static int rk3036_codec_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rk3036_codec_priv *priv;
|
||||
struct device_node *of_node = pdev->dev.of_node;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
struct regmap *grf;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
priv->base = base;
|
||||
priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,
|
||||
&rk3036_codec_regmap_config);
|
||||
if (IS_ERR(priv->regmap)) {
|
||||
dev_err(&pdev->dev, "init regmap failed\n");
|
||||
return PTR_ERR(priv->regmap);
|
||||
}
|
||||
|
||||
grf = syscon_regmap_lookup_by_phandle(of_node, "rockchip,grf");
|
||||
if (IS_ERR(grf)) {
|
||||
dev_err(&pdev->dev, "needs 'rockchip,grf' property\n");
|
||||
return PTR_ERR(grf);
|
||||
}
|
||||
ret = regmap_write(grf, GRF_SOC_CON0, GRF_ACODEC_SEL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not write to GRF: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->pclk = devm_clk_get(&pdev->dev, "acodec_pclk");
|
||||
if (IS_ERR(priv->pclk))
|
||||
return PTR_ERR(priv->pclk);
|
||||
|
||||
ret = clk_prepare_enable(priv->pclk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable clk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->dev = &pdev->dev;
|
||||
dev_set_drvdata(&pdev->dev, priv);
|
||||
|
||||
ret = snd_soc_register_codec(&pdev->dev, &rk3036_codec_driver,
|
||||
rk3036_codec_dai_driver,
|
||||
ARRAY_SIZE(rk3036_codec_dai_driver));
|
||||
if (ret) {
|
||||
clk_disable_unprepare(priv->pclk);
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rk3036_codec_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rk3036_codec_priv *priv = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
snd_soc_unregister_codec(&pdev->dev);
|
||||
clk_disable_unprepare(priv->pclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id rk3036_codec_of_match[] = {
|
||||
{ .compatible = "rockchip,rk3036-codec", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rk3036_codec_of_match);
|
||||
|
||||
static struct platform_driver rk3036_codec_platform_driver = {
|
||||
.driver = {
|
||||
.name = "rk3036-codec-platform",
|
||||
.of_match_table = of_match_ptr(rk3036_codec_of_match),
|
||||
},
|
||||
.probe = rk3036_codec_platform_probe,
|
||||
.remove = rk3036_codec_platform_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(rk3036_codec_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Rockchip Inc.");
|
||||
MODULE_DESCRIPTION("Rockchip rk3036 codec driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
123
sound/soc/codecs/inno_rk3036.h
Normal file
123
sound/soc/codecs/inno_rk3036.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Driver of Inno Codec for rk3036 by Rockchip Inc.
|
||||
*
|
||||
* Author: Zheng ShunQian<zhengsq@rock-chips.com>
|
||||
*/
|
||||
|
||||
#ifndef _INNO_RK3036_CODEC_H
|
||||
#define _INNO_RK3036_CODEC_H
|
||||
|
||||
/* codec registers */
|
||||
#define INNO_R00 0x00
|
||||
#define INNO_R01 0x0c
|
||||
#define INNO_R02 0x10
|
||||
#define INNO_R03 0x14
|
||||
#define INNO_R04 0x88
|
||||
#define INNO_R05 0x8c
|
||||
#define INNO_R06 0x90
|
||||
#define INNO_R07 0x94
|
||||
#define INNO_R08 0x98
|
||||
#define INNO_R09 0x9c
|
||||
#define INNO_R10 0xa0
|
||||
|
||||
/* register bit filed */
|
||||
#define INNO_R00_CSR_RESET (0x0 << 0) /*codec system reset*/
|
||||
#define INNO_R00_CSR_WORK (0x1 << 0)
|
||||
#define INNO_R00_CDCR_RESET (0x0 << 1) /*codec digital core reset*/
|
||||
#define INNO_R00_CDCR_WORK (0x1 << 1)
|
||||
#define INNO_R00_PRB_DISABLE (0x0 << 6) /*power reset bypass*/
|
||||
#define INNO_R00_PRB_ENABLE (0x1 << 6)
|
||||
|
||||
#define INNO_R01_I2SMODE_MSK (0x1 << 4)
|
||||
#define INNO_R01_I2SMODE_SLAVE (0x0 << 4)
|
||||
#define INNO_R01_I2SMODE_MASTER (0x1 << 4)
|
||||
#define INNO_R01_PINDIR_MSK (0x1 << 5)
|
||||
#define INNO_R01_PINDIR_IN_SLAVE (0x0 << 5) /*direction of pin*/
|
||||
#define INNO_R01_PINDIR_OUT_MASTER (0x1 << 5)
|
||||
|
||||
#define INNO_R02_LRS_MSK (0x1 << 2)
|
||||
#define INNO_R02_LRS_NORMAL (0x0 << 2) /*DAC Left Right Swap*/
|
||||
#define INNO_R02_LRS_SWAP (0x1 << 2)
|
||||
#define INNO_R02_DACM_MSK (0x3 << 3)
|
||||
#define INNO_R02_DACM_PCM (0x3 << 3) /*DAC Mode*/
|
||||
#define INNO_R02_DACM_I2S (0x2 << 3)
|
||||
#define INNO_R02_DACM_LJM (0x1 << 3)
|
||||
#define INNO_R02_DACM_RJM (0x0 << 3)
|
||||
#define INNO_R02_VWL_MSK (0x3 << 5)
|
||||
#define INNO_R02_VWL_32BIT (0x3 << 5) /*1/2Frame Valid Word Len*/
|
||||
#define INNO_R02_VWL_24BIT (0x2 << 5)
|
||||
#define INNO_R02_VWL_20BIT (0x1 << 5)
|
||||
#define INNO_R02_VWL_16BIT (0x0 << 5)
|
||||
#define INNO_R02_LRCP_MSK (0x1 << 7)
|
||||
#define INNO_R02_LRCP_NORMAL (0x0 << 7) /*Left Right Polarity*/
|
||||
#define INNO_R02_LRCP_REVERSAL (0x1 << 7)
|
||||
|
||||
#define INNO_R03_BCP_MSK (0x1 << 0)
|
||||
#define INNO_R03_BCP_NORMAL (0x0 << 0) /*DAC bit clock polarity*/
|
||||
#define INNO_R03_BCP_REVERSAL (0x1 << 0)
|
||||
#define INNO_R03_DACR_MSK (0x1 << 1)
|
||||
#define INNO_R03_DACR_RESET (0x0 << 1) /*DAC Reset*/
|
||||
#define INNO_R03_DACR_WORK (0x1 << 1)
|
||||
#define INNO_R03_FWL_MSK (0x3 << 2)
|
||||
#define INNO_R03_FWL_32BIT (0x3 << 2) /*1/2Frame Word Length*/
|
||||
#define INNO_R03_FWL_24BIT (0x2 << 2)
|
||||
#define INNO_R03_FWL_20BIT (0x1 << 2)
|
||||
#define INNO_R03_FWL_16BIT (0x0 << 2)
|
||||
|
||||
#define INNO_R04_DACR_SW_SHIFT 0
|
||||
#define INNO_R04_DACL_SW_SHIFT 1
|
||||
#define INNO_R04_DACR_CLK_SHIFT 2
|
||||
#define INNO_R04_DACL_CLK_SHIFT 3
|
||||
#define INNO_R04_DACR_VREF_SHIFT 4
|
||||
#define INNO_R04_DACL_VREF_SHIFT 5
|
||||
|
||||
#define INNO_R05_HPR_EN_SHIFT 0
|
||||
#define INNO_R05_HPL_EN_SHIFT 1
|
||||
#define INNO_R05_HPR_WORK_SHIFT 2
|
||||
#define INNO_R05_HPL_WORK_SHIFT 3
|
||||
|
||||
#define INNO_R06_VOUTR_CZ_SHIFT 0
|
||||
#define INNO_R06_VOUTL_CZ_SHIFT 1
|
||||
#define INNO_R06_DACR_HILO_VREF_SHIFT 2
|
||||
#define INNO_R06_DACL_HILO_VREF_SHIFT 3
|
||||
#define INNO_R06_DAC_EN_SHIFT 5
|
||||
|
||||
#define INNO_R06_DAC_PRECHARGE (0x0 << 4) /*PreCharge control for DAC*/
|
||||
#define INNO_R06_DAC_DISCHARGE (0x1 << 4)
|
||||
|
||||
#define INNO_HP_GAIN_SHIFT 0
|
||||
/* Gain of output, 1.5db step: -39db(0x0) ~ 0db(0x1a) ~ 6db(0x1f) */
|
||||
#define INNO_HP_GAIN_0DB 0x1a
|
||||
#define INNO_HP_GAIN_N39DB 0x0
|
||||
|
||||
#define INNO_R09_HP_ANTIPOP_MSK 0x3
|
||||
#define INNO_R09_HP_ANTIPOP_OFF 0x1
|
||||
#define INNO_R09_HP_ANTIPOP_ON 0x2
|
||||
#define INNO_R09_HPR_ANITPOP_SHIFT 0
|
||||
#define INNO_R09_HPL_ANITPOP_SHIFT 2
|
||||
#define INNO_R09_HPR_MUTE_SHIFT 4
|
||||
#define INNO_R09_HPL_MUTE_SHIFT 5
|
||||
#define INNO_R09_DACR_SWITCH_SHIFT 6
|
||||
#define INNO_R09_DACL_SWITCH_SHIFT 7
|
||||
|
||||
#define INNO_R10_CHARGE_SEL_CUR_400I_YES (0x0 << 0)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_400I_NO (0x1 << 0)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_260I_YES (0x0 << 1)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_260I_NO (0x1 << 1)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_130I_YES (0x0 << 2)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_130I_NO (0x1 << 2)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_100I_YES (0x0 << 3)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_100I_NO (0x1 << 3)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_050I_YES (0x0 << 4)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_050I_NO (0x1 << 4)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_027I_YES (0x0 << 5)
|
||||
#define INNO_R10_CHARGE_SEL_CUR_027I_NO (0x1 << 5)
|
||||
|
||||
#define INNO_R10_MAX_CUR (INNO_R10_CHARGE_SEL_CUR_400I_YES | \
|
||||
INNO_R10_CHARGE_SEL_CUR_260I_YES | \
|
||||
INNO_R10_CHARGE_SEL_CUR_130I_YES | \
|
||||
INNO_R10_CHARGE_SEL_CUR_100I_YES | \
|
||||
INNO_R10_CHARGE_SEL_CUR_050I_YES | \
|
||||
INNO_R10_CHARGE_SEL_CUR_027I_YES)
|
||||
|
||||
#endif
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
* max98357a.c -- MAX98357A ALSA SoC Codec driver
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
|
|
@ -123,10 +124,19 @@ static const struct of_device_id max98357a_device_id[] = {
|
|||
MODULE_DEVICE_TABLE(of, max98357a_device_id);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id max98357a_acpi_match[] = {
|
||||
{ "MX98357A", 0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, max98357a_acpi_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver max98357a_platform_driver = {
|
||||
.driver = {
|
||||
.name = "max98357a",
|
||||
.of_match_table = of_match_ptr(max98357a_device_id),
|
||||
.acpi_match_table = ACPI_PTR(max98357a_acpi_match),
|
||||
},
|
||||
.probe = max98357a_platform_probe,
|
||||
.remove = max98357a_platform_remove,
|
||||
|
|
|
|||
66
sound/soc/codecs/pcm3168a-i2c.c
Normal file
66
sound/soc/codecs/pcm3168a-i2c.c
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* PCM3168A codec i2c driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "pcm3168a.h"
|
||||
|
||||
static int pcm3168a_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(i2c, &pcm3168a_regmap);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
return pcm3168a_probe(&i2c->dev, regmap);
|
||||
}
|
||||
|
||||
static int pcm3168a_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
pcm3168a_remove(&i2c->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id pcm3168a_i2c_id[] = {
|
||||
{ "pcm3168a", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id);
|
||||
|
||||
static const struct of_device_id pcm3168a_of_match[] = {
|
||||
{ .compatible = "ti,pcm3168a", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pcm3168a_of_match);
|
||||
|
||||
static struct i2c_driver pcm3168a_i2c_driver = {
|
||||
.probe = pcm3168a_i2c_probe,
|
||||
.remove = pcm3168a_i2c_remove,
|
||||
.id_table = pcm3168a_i2c_id,
|
||||
.driver = {
|
||||
.name = "pcm3168a",
|
||||
.of_match_table = pcm3168a_of_match,
|
||||
.pm = &pcm3168a_pm_ops,
|
||||
},
|
||||
};
|
||||
module_i2c_driver(pcm3168a_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("PCM3168A I2C codec driver");
|
||||
MODULE_AUTHOR("Damien Horsley <[email protected]>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
65
sound/soc/codecs/pcm3168a-spi.c
Normal file
65
sound/soc/codecs/pcm3168a-spi.c
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* PCM3168A codec spi driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "pcm3168a.h"
|
||||
|
||||
static int pcm3168a_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &pcm3168a_regmap);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
return pcm3168a_probe(&spi->dev, regmap);
|
||||
}
|
||||
|
||||
static int pcm3168a_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
pcm3168a_remove(&spi->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id pcm3168a_spi_id[] = {
|
||||
{ "pcm3168a", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, pcm3168a_spi_id);
|
||||
|
||||
static const struct of_device_id pcm3168a_of_match[] = {
|
||||
{ .compatible = "ti,pcm3168a", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pcm3168a_of_match);
|
||||
|
||||
static struct spi_driver pcm3168a_spi_driver = {
|
||||
.probe = pcm3168a_spi_probe,
|
||||
.remove = pcm3168a_spi_remove,
|
||||
.id_table = pcm3168a_spi_id,
|
||||
.driver = {
|
||||
.name = "pcm3168a",
|
||||
.of_match_table = pcm3168a_of_match,
|
||||
.pm = &pcm3168a_pm_ops,
|
||||
},
|
||||
};
|
||||
module_spi_driver(pcm3168a_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("PCM3168A SPI codec driver");
|
||||
MODULE_AUTHOR("Damien Horsley <[email protected]>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
767
sound/soc/codecs/pcm3168a.c
Normal file
767
sound/soc/codecs/pcm3168a.c
Normal file
|
|
@ -0,0 +1,767 @@
|
|||
/*
|
||||
* PCM3168A codec driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#include "pcm3168a.h"
|
||||
|
||||
#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
#define PCM3168A_FMT_I2S 0x0
|
||||
#define PCM3168A_FMT_LEFT_J 0x1
|
||||
#define PCM3168A_FMT_RIGHT_J 0x2
|
||||
#define PCM3168A_FMT_RIGHT_J_16 0x3
|
||||
#define PCM3168A_FMT_DSP_A 0x4
|
||||
#define PCM3168A_FMT_DSP_B 0x5
|
||||
#define PCM3168A_FMT_DSP_MASK 0x4
|
||||
|
||||
#define PCM3168A_NUM_SUPPLIES 6
|
||||
static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = {
|
||||
"VDD1",
|
||||
"VDD2",
|
||||
"VCCAD1",
|
||||
"VCCAD2",
|
||||
"VCCDA1",
|
||||
"VCCDA2"
|
||||
};
|
||||
|
||||
struct pcm3168a_priv {
|
||||
struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES];
|
||||
struct regmap *regmap;
|
||||
struct clk *scki;
|
||||
bool adc_master_mode;
|
||||
bool dac_master_mode;
|
||||
unsigned long sysclk;
|
||||
unsigned int adc_fmt;
|
||||
unsigned int dac_fmt;
|
||||
};
|
||||
|
||||
static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_d1_roll_off, PCM3168A_DAC_OP_FLT,
|
||||
PCM3168A_DAC_FLT_SHIFT, pcm3168a_roll_off);
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_d2_roll_off, PCM3168A_DAC_OP_FLT,
|
||||
PCM3168A_DAC_FLT_SHIFT + 1, pcm3168a_roll_off);
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_d3_roll_off, PCM3168A_DAC_OP_FLT,
|
||||
PCM3168A_DAC_FLT_SHIFT + 2, pcm3168a_roll_off);
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_d4_roll_off, PCM3168A_DAC_OP_FLT,
|
||||
PCM3168A_DAC_FLT_SHIFT + 3, pcm3168a_roll_off);
|
||||
|
||||
static const char *const pcm3168a_volume_type[] = {
|
||||
"Individual", "Master + Individual" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_volume_type, PCM3168A_DAC_ATT_DEMP_ZF,
|
||||
PCM3168A_DAC_ATMDDA_SHIFT, pcm3168a_volume_type);
|
||||
|
||||
static const char *const pcm3168a_att_speed_mult[] = { "2048", "4096" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_att_mult, PCM3168A_DAC_ATT_DEMP_ZF,
|
||||
PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_att_speed_mult);
|
||||
|
||||
static const char *const pcm3168a_demp[] = {
|
||||
"Disabled", "48khz", "44.1khz", "32khz" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_demp, PCM3168A_DAC_ATT_DEMP_ZF,
|
||||
PCM3168A_DAC_DEMP_SHIFT, pcm3168a_demp);
|
||||
|
||||
static const char *const pcm3168a_zf_func[] = {
|
||||
"DAC 1/2/3/4 AND", "DAC 1/2/3/4 OR", "DAC 1/2/3 AND",
|
||||
"DAC 1/2/3 OR", "DAC 4 AND", "DAC 4 OR" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_func, PCM3168A_DAC_ATT_DEMP_ZF,
|
||||
PCM3168A_DAC_AZRO_SHIFT, pcm3168a_zf_func);
|
||||
|
||||
static const char *const pcm3168a_pol[] = { "Active High", "Active Low" };
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_pol, PCM3168A_DAC_ATT_DEMP_ZF,
|
||||
PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_pol);
|
||||
|
||||
static const char *const pcm3168a_con[] = { "Differential", "Single-Ended" };
|
||||
|
||||
static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc1_con, PCM3168A_ADC_SEAD,
|
||||
0, 1, pcm3168a_con);
|
||||
static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc2_con, PCM3168A_ADC_SEAD,
|
||||
2, 3, pcm3168a_con);
|
||||
static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc3_con, PCM3168A_ADC_SEAD,
|
||||
4, 5, pcm3168a_con);
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_volume_type, PCM3168A_ADC_ATT_OVF,
|
||||
PCM3168A_ADC_ATMDAD_SHIFT, pcm3168a_volume_type);
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_att_mult, PCM3168A_ADC_ATT_OVF,
|
||||
PCM3168A_ADC_ATSPAD_SHIFT, pcm3168a_att_speed_mult);
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_ov_pol, PCM3168A_ADC_ATT_OVF,
|
||||
PCM3168A_ADC_OVFP_SHIFT, pcm3168a_pol);
|
||||
|
||||
/* -100db to 0db, register values 0-54 cause mute */
|
||||
static const DECLARE_TLV_DB_SCALE(pcm3168a_dac_tlv, -10050, 50, 1);
|
||||
|
||||
/* -100db to 20db, register values 0-14 cause mute */
|
||||
static const DECLARE_TLV_DB_SCALE(pcm3168a_adc_tlv, -10050, 50, 1);
|
||||
|
||||
static const struct snd_kcontrol_new pcm3168a_snd_controls[] = {
|
||||
SOC_SINGLE("DAC Power-Save Switch", PCM3168A_DAC_PWR_MST_FMT,
|
||||
PCM3168A_DAC_PSMDA_SHIFT, 1, 1),
|
||||
SOC_ENUM("DAC1 Digital Filter roll-off", pcm3168a_d1_roll_off),
|
||||
SOC_ENUM("DAC2 Digital Filter roll-off", pcm3168a_d2_roll_off),
|
||||
SOC_ENUM("DAC3 Digital Filter roll-off", pcm3168a_d3_roll_off),
|
||||
SOC_ENUM("DAC4 Digital Filter roll-off", pcm3168a_d4_roll_off),
|
||||
SOC_DOUBLE("DAC1 Invert Switch", PCM3168A_DAC_INV, 0, 1, 1, 0),
|
||||
SOC_DOUBLE("DAC2 Invert Switch", PCM3168A_DAC_INV, 2, 3, 1, 0),
|
||||
SOC_DOUBLE("DAC3 Invert Switch", PCM3168A_DAC_INV, 4, 5, 1, 0),
|
||||
SOC_DOUBLE("DAC4 Invert Switch", PCM3168A_DAC_INV, 6, 7, 1, 0),
|
||||
SOC_DOUBLE_STS("DAC1 Zero Flag", PCM3168A_DAC_ZERO, 0, 1, 1, 0),
|
||||
SOC_DOUBLE_STS("DAC2 Zero Flag", PCM3168A_DAC_ZERO, 2, 3, 1, 0),
|
||||
SOC_DOUBLE_STS("DAC3 Zero Flag", PCM3168A_DAC_ZERO, 4, 5, 1, 0),
|
||||
SOC_DOUBLE_STS("DAC4 Zero Flag", PCM3168A_DAC_ZERO, 6, 7, 1, 0),
|
||||
SOC_ENUM("DAC Volume Control Type", pcm3168a_dac_volume_type),
|
||||
SOC_ENUM("DAC Volume Rate Multiplier", pcm3168a_dac_att_mult),
|
||||
SOC_ENUM("DAC De-Emphasis", pcm3168a_dac_demp),
|
||||
SOC_ENUM("DAC Zero Flag Function", pcm3168a_dac_zf_func),
|
||||
SOC_ENUM("DAC Zero Flag Polarity", pcm3168a_dac_zf_pol),
|
||||
SOC_SINGLE_RANGE_TLV("Master Playback Volume",
|
||||
PCM3168A_DAC_VOL_MASTER, 0, 54, 255, 0,
|
||||
pcm3168a_dac_tlv),
|
||||
SOC_DOUBLE_R_RANGE_TLV("DAC1 Playback Volume",
|
||||
PCM3168A_DAC_VOL_CHAN_START,
|
||||
PCM3168A_DAC_VOL_CHAN_START + 1,
|
||||
0, 54, 255, 0, pcm3168a_dac_tlv),
|
||||
SOC_DOUBLE_R_RANGE_TLV("DAC2 Playback Volume",
|
||||
PCM3168A_DAC_VOL_CHAN_START + 2,
|
||||
PCM3168A_DAC_VOL_CHAN_START + 3,
|
||||
0, 54, 255, 0, pcm3168a_dac_tlv),
|
||||
SOC_DOUBLE_R_RANGE_TLV("DAC3 Playback Volume",
|
||||
PCM3168A_DAC_VOL_CHAN_START + 4,
|
||||
PCM3168A_DAC_VOL_CHAN_START + 5,
|
||||
0, 54, 255, 0, pcm3168a_dac_tlv),
|
||||
SOC_DOUBLE_R_RANGE_TLV("DAC4 Playback Volume",
|
||||
PCM3168A_DAC_VOL_CHAN_START + 6,
|
||||
PCM3168A_DAC_VOL_CHAN_START + 7,
|
||||
0, 54, 255, 0, pcm3168a_dac_tlv),
|
||||
SOC_SINGLE("ADC1 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
|
||||
PCM3168A_ADC_BYP_SHIFT, 1, 1),
|
||||
SOC_SINGLE("ADC2 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
|
||||
PCM3168A_ADC_BYP_SHIFT + 1, 1, 1),
|
||||
SOC_SINGLE("ADC3 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
|
||||
PCM3168A_ADC_BYP_SHIFT + 2, 1, 1),
|
||||
SOC_ENUM("ADC1 Connection Type", pcm3168a_adc1_con),
|
||||
SOC_ENUM("ADC2 Connection Type", pcm3168a_adc2_con),
|
||||
SOC_ENUM("ADC3 Connection Type", pcm3168a_adc3_con),
|
||||
SOC_DOUBLE("ADC1 Invert Switch", PCM3168A_ADC_INV, 0, 1, 1, 0),
|
||||
SOC_DOUBLE("ADC2 Invert Switch", PCM3168A_ADC_INV, 2, 3, 1, 0),
|
||||
SOC_DOUBLE("ADC3 Invert Switch", PCM3168A_ADC_INV, 4, 5, 1, 0),
|
||||
SOC_DOUBLE("ADC1 Mute Switch", PCM3168A_ADC_MUTE, 0, 1, 1, 0),
|
||||
SOC_DOUBLE("ADC2 Mute Switch", PCM3168A_ADC_MUTE, 2, 3, 1, 0),
|
||||
SOC_DOUBLE("ADC3 Mute Switch", PCM3168A_ADC_MUTE, 4, 5, 1, 0),
|
||||
SOC_DOUBLE_STS("ADC1 Overflow Flag", PCM3168A_ADC_OV, 0, 1, 1, 0),
|
||||
SOC_DOUBLE_STS("ADC2 Overflow Flag", PCM3168A_ADC_OV, 2, 3, 1, 0),
|
||||
SOC_DOUBLE_STS("ADC3 Overflow Flag", PCM3168A_ADC_OV, 4, 5, 1, 0),
|
||||
SOC_ENUM("ADC Volume Control Type", pcm3168a_adc_volume_type),
|
||||
SOC_ENUM("ADC Volume Rate Multiplier", pcm3168a_adc_att_mult),
|
||||
SOC_ENUM("ADC Overflow Flag Polarity", pcm3168a_adc_ov_pol),
|
||||
SOC_SINGLE_RANGE_TLV("Master Capture Volume",
|
||||
PCM3168A_ADC_VOL_MASTER, 0, 14, 255, 0,
|
||||
pcm3168a_adc_tlv),
|
||||
SOC_DOUBLE_R_RANGE_TLV("ADC1 Capture Volume",
|
||||
PCM3168A_ADC_VOL_CHAN_START,
|
||||
PCM3168A_ADC_VOL_CHAN_START + 1,
|
||||
0, 14, 255, 0, pcm3168a_adc_tlv),
|
||||
SOC_DOUBLE_R_RANGE_TLV("ADC2 Capture Volume",
|
||||
PCM3168A_ADC_VOL_CHAN_START + 2,
|
||||
PCM3168A_ADC_VOL_CHAN_START + 3,
|
||||
0, 14, 255, 0, pcm3168a_adc_tlv),
|
||||
SOC_DOUBLE_R_RANGE_TLV("ADC3 Capture Volume",
|
||||
PCM3168A_ADC_VOL_CHAN_START + 4,
|
||||
PCM3168A_ADC_VOL_CHAN_START + 5,
|
||||
0, 14, 255, 0, pcm3168a_adc_tlv)
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget pcm3168a_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_DAC("DAC1", "Playback", PCM3168A_DAC_OP_FLT,
|
||||
PCM3168A_DAC_OPEDA_SHIFT, 1),
|
||||
SND_SOC_DAPM_DAC("DAC2", "Playback", PCM3168A_DAC_OP_FLT,
|
||||
PCM3168A_DAC_OPEDA_SHIFT + 1, 1),
|
||||
SND_SOC_DAPM_DAC("DAC3", "Playback", PCM3168A_DAC_OP_FLT,
|
||||
PCM3168A_DAC_OPEDA_SHIFT + 2, 1),
|
||||
SND_SOC_DAPM_DAC("DAC4", "Playback", PCM3168A_DAC_OP_FLT,
|
||||
PCM3168A_DAC_OPEDA_SHIFT + 3, 1),
|
||||
|
||||
SND_SOC_DAPM_OUTPUT("AOUT1L"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT1R"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT2L"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT2R"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT3L"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT3R"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT4L"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUT4R"),
|
||||
|
||||
SND_SOC_DAPM_ADC("ADC1", "Capture", PCM3168A_ADC_PWR_HPFB,
|
||||
PCM3168A_ADC_PSVAD_SHIFT, 1),
|
||||
SND_SOC_DAPM_ADC("ADC2", "Capture", PCM3168A_ADC_PWR_HPFB,
|
||||
PCM3168A_ADC_PSVAD_SHIFT + 1, 1),
|
||||
SND_SOC_DAPM_ADC("ADC3", "Capture", PCM3168A_ADC_PWR_HPFB,
|
||||
PCM3168A_ADC_PSVAD_SHIFT + 2, 1),
|
||||
|
||||
SND_SOC_DAPM_INPUT("AIN1L"),
|
||||
SND_SOC_DAPM_INPUT("AIN1R"),
|
||||
SND_SOC_DAPM_INPUT("AIN2L"),
|
||||
SND_SOC_DAPM_INPUT("AIN2R"),
|
||||
SND_SOC_DAPM_INPUT("AIN3L"),
|
||||
SND_SOC_DAPM_INPUT("AIN3R")
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route pcm3168a_dapm_routes[] = {
|
||||
/* Playback */
|
||||
{ "AOUT1L", NULL, "DAC1" },
|
||||
{ "AOUT1R", NULL, "DAC1" },
|
||||
|
||||
{ "AOUT2L", NULL, "DAC2" },
|
||||
{ "AOUT2R", NULL, "DAC2" },
|
||||
|
||||
{ "AOUT3L", NULL, "DAC3" },
|
||||
{ "AOUT3R", NULL, "DAC3" },
|
||||
|
||||
{ "AOUT4L", NULL, "DAC4" },
|
||||
{ "AOUT4R", NULL, "DAC4" },
|
||||
|
||||
/* Capture */
|
||||
{ "ADC1", NULL, "AIN1L" },
|
||||
{ "ADC1", NULL, "AIN1R" },
|
||||
|
||||
{ "ADC2", NULL, "AIN2L" },
|
||||
{ "ADC2", NULL, "AIN2R" },
|
||||
|
||||
{ "ADC3", NULL, "AIN3L" },
|
||||
{ "ADC3", NULL, "AIN3R" }
|
||||
};
|
||||
|
||||
static unsigned int pcm3168a_scki_ratios[] = {
|
||||
768,
|
||||
512,
|
||||
384,
|
||||
256,
|
||||
192,
|
||||
128
|
||||
};
|
||||
|
||||
#define PCM3168A_NUM_SCKI_RATIOS_DAC ARRAY_SIZE(pcm3168a_scki_ratios)
|
||||
#define PCM3168A_NUM_SCKI_RATIOS_ADC (ARRAY_SIZE(pcm3168a_scki_ratios) - 2)
|
||||
|
||||
#define PCM1368A_MAX_SYSCLK 36864000
|
||||
|
||||
static int pcm3168a_reset(struct pcm3168a_priv *pcm3168a)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Internal reset is de-asserted after 3846 SCKI cycles */
|
||||
msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk));
|
||||
|
||||
return regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE,
|
||||
PCM3168A_MRST_MASK | PCM3168A_SRST_MASK);
|
||||
}
|
||||
|
||||
static int pcm3168a_digital_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
regmap_write(pcm3168a->regmap, PCM3168A_DAC_MUTE, mute ? 0xff : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(dai->codec);
|
||||
|
||||
if (freq > PCM1368A_MAX_SYSCLK)
|
||||
return -EINVAL;
|
||||
|
||||
pcm3168a->sysclk = freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
|
||||
unsigned int format, bool dac)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
|
||||
u32 fmt, reg, mask, shift;
|
||||
bool master_mode;
|
||||
|
||||
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
fmt = PCM3168A_FMT_LEFT_J;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
fmt = PCM3168A_FMT_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
fmt = PCM3168A_FMT_RIGHT_J;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
fmt = PCM3168A_FMT_DSP_A;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
fmt = PCM3168A_FMT_DSP_B;
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "unsupported dai format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
master_mode = false;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
master_mode = true;
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "unsupported master/slave mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (format & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dac) {
|
||||
reg = PCM3168A_DAC_PWR_MST_FMT;
|
||||
mask = PCM3168A_DAC_FMT_MASK;
|
||||
shift = PCM3168A_DAC_FMT_SHIFT;
|
||||
pcm3168a->dac_master_mode = master_mode;
|
||||
pcm3168a->dac_fmt = fmt;
|
||||
} else {
|
||||
reg = PCM3168A_ADC_MST_FMT;
|
||||
mask = PCM3168A_ADC_FMTAD_MASK;
|
||||
shift = PCM3168A_ADC_FMTAD_SHIFT;
|
||||
pcm3168a->adc_master_mode = master_mode;
|
||||
pcm3168a->adc_fmt = fmt;
|
||||
}
|
||||
|
||||
regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm3168a_set_dai_fmt_dac(struct snd_soc_dai *dai,
|
||||
unsigned int format)
|
||||
{
|
||||
return pcm3168a_set_dai_fmt(dai, format, true);
|
||||
}
|
||||
|
||||
static int pcm3168a_set_dai_fmt_adc(struct snd_soc_dai *dai,
|
||||
unsigned int format)
|
||||
{
|
||||
return pcm3168a_set_dai_fmt(dai, format, false);
|
||||
}
|
||||
|
||||
static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
|
||||
bool tx, master_mode;
|
||||
u32 val, mask, shift, reg;
|
||||
unsigned int rate, channels, fmt, ratio, max_ratio;
|
||||
int i, min_frame_size;
|
||||
snd_pcm_format_t format;
|
||||
|
||||
rate = params_rate(params);
|
||||
format = params_format(params);
|
||||
channels = params_channels(params);
|
||||
|
||||
ratio = pcm3168a->sysclk / rate;
|
||||
|
||||
tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
if (tx) {
|
||||
max_ratio = PCM3168A_NUM_SCKI_RATIOS_DAC;
|
||||
reg = PCM3168A_DAC_PWR_MST_FMT;
|
||||
mask = PCM3168A_DAC_MSDA_MASK;
|
||||
shift = PCM3168A_DAC_MSDA_SHIFT;
|
||||
master_mode = pcm3168a->dac_master_mode;
|
||||
fmt = pcm3168a->dac_fmt;
|
||||
} else {
|
||||
max_ratio = PCM3168A_NUM_SCKI_RATIOS_ADC;
|
||||
reg = PCM3168A_ADC_MST_FMT;
|
||||
mask = PCM3168A_ADC_MSAD_MASK;
|
||||
shift = PCM3168A_ADC_MSAD_SHIFT;
|
||||
master_mode = pcm3168a->adc_master_mode;
|
||||
fmt = pcm3168a->adc_fmt;
|
||||
}
|
||||
|
||||
for (i = 0; i < max_ratio; i++) {
|
||||
if (pcm3168a_scki_ratios[i] == ratio)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == max_ratio) {
|
||||
dev_err(codec->dev, "unsupported sysclk ratio\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
min_frame_size = params_width(params) * 2;
|
||||
switch (min_frame_size) {
|
||||
case 32:
|
||||
if (master_mode || (fmt != PCM3168A_FMT_RIGHT_J)) {
|
||||
dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
fmt = PCM3168A_FMT_RIGHT_J_16;
|
||||
break;
|
||||
case 48:
|
||||
if (master_mode || (fmt & PCM3168A_FMT_DSP_MASK)) {
|
||||
dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case 64:
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "unsupported frame size: %d\n", min_frame_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (master_mode)
|
||||
val = ((i + 1) << shift);
|
||||
else
|
||||
val = 0;
|
||||
|
||||
regmap_update_bits(pcm3168a->regmap, reg, mask, val);
|
||||
|
||||
if (tx) {
|
||||
mask = PCM3168A_DAC_FMT_MASK;
|
||||
shift = PCM3168A_DAC_FMT_SHIFT;
|
||||
} else {
|
||||
mask = PCM3168A_ADC_FMTAD_MASK;
|
||||
shift = PCM3168A_ADC_FMTAD_SHIFT;
|
||||
}
|
||||
|
||||
regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = {
|
||||
.set_fmt = pcm3168a_set_dai_fmt_dac,
|
||||
.set_sysclk = pcm3168a_set_dai_sysclk,
|
||||
.hw_params = pcm3168a_hw_params,
|
||||
.digital_mute = pcm3168a_digital_mute
|
||||
};
|
||||
|
||||
static const struct snd_soc_dai_ops pcm3168a_adc_dai_ops = {
|
||||
.set_fmt = pcm3168a_set_dai_fmt_adc,
|
||||
.set_sysclk = pcm3168a_set_dai_sysclk,
|
||||
.hw_params = pcm3168a_hw_params
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver pcm3168a_dais[] = {
|
||||
{
|
||||
.name = "pcm3168a-dac",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = PCM3168A_FORMATS
|
||||
},
|
||||
.ops = &pcm3168a_dac_dai_ops
|
||||
},
|
||||
{
|
||||
.name = "pcm3168a-adc",
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 6,
|
||||
.rates = SNDRV_PCM_RATE_8000_96000,
|
||||
.formats = PCM3168A_FORMATS
|
||||
},
|
||||
.ops = &pcm3168a_adc_dai_ops
|
||||
},
|
||||
};
|
||||
|
||||
static const struct reg_default pcm3168a_reg_default[] = {
|
||||
{ PCM3168A_RST_SMODE, PCM3168A_MRST_MASK | PCM3168A_SRST_MASK },
|
||||
{ PCM3168A_DAC_PWR_MST_FMT, 0x00 },
|
||||
{ PCM3168A_DAC_OP_FLT, 0x00 },
|
||||
{ PCM3168A_DAC_INV, 0x00 },
|
||||
{ PCM3168A_DAC_MUTE, 0x00 },
|
||||
{ PCM3168A_DAC_ZERO, 0x00 },
|
||||
{ PCM3168A_DAC_ATT_DEMP_ZF, 0x00 },
|
||||
{ PCM3168A_DAC_VOL_MASTER, 0xff },
|
||||
{ PCM3168A_DAC_VOL_CHAN_START, 0xff },
|
||||
{ PCM3168A_DAC_VOL_CHAN_START + 1, 0xff },
|
||||
{ PCM3168A_DAC_VOL_CHAN_START + 2, 0xff },
|
||||
{ PCM3168A_DAC_VOL_CHAN_START + 3, 0xff },
|
||||
{ PCM3168A_DAC_VOL_CHAN_START + 4, 0xff },
|
||||
{ PCM3168A_DAC_VOL_CHAN_START + 5, 0xff },
|
||||
{ PCM3168A_DAC_VOL_CHAN_START + 6, 0xff },
|
||||
{ PCM3168A_DAC_VOL_CHAN_START + 7, 0xff },
|
||||
{ PCM3168A_ADC_SMODE, 0x00 },
|
||||
{ PCM3168A_ADC_MST_FMT, 0x00 },
|
||||
{ PCM3168A_ADC_PWR_HPFB, 0x00 },
|
||||
{ PCM3168A_ADC_SEAD, 0x00 },
|
||||
{ PCM3168A_ADC_INV, 0x00 },
|
||||
{ PCM3168A_ADC_MUTE, 0x00 },
|
||||
{ PCM3168A_ADC_OV, 0x00 },
|
||||
{ PCM3168A_ADC_ATT_OVF, 0x00 },
|
||||
{ PCM3168A_ADC_VOL_MASTER, 0xd3 },
|
||||
{ PCM3168A_ADC_VOL_CHAN_START, 0xd3 },
|
||||
{ PCM3168A_ADC_VOL_CHAN_START + 1, 0xd3 },
|
||||
{ PCM3168A_ADC_VOL_CHAN_START + 2, 0xd3 },
|
||||
{ PCM3168A_ADC_VOL_CHAN_START + 3, 0xd3 },
|
||||
{ PCM3168A_ADC_VOL_CHAN_START + 4, 0xd3 },
|
||||
{ PCM3168A_ADC_VOL_CHAN_START + 5, 0xd3 }
|
||||
};
|
||||
|
||||
static bool pcm3168a_readable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
if (reg >= PCM3168A_RST_SMODE)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case PCM3168A_DAC_ZERO:
|
||||
case PCM3168A_ADC_OV:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool pcm3168a_writeable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
if (reg < PCM3168A_RST_SMODE)
|
||||
return false;
|
||||
|
||||
switch (reg) {
|
||||
case PCM3168A_DAC_ZERO:
|
||||
case PCM3168A_ADC_OV:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const struct regmap_config pcm3168a_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = PCM3168A_ADC_VOL_CHAN_START + 5,
|
||||
.reg_defaults = pcm3168a_reg_default,
|
||||
.num_reg_defaults = ARRAY_SIZE(pcm3168a_reg_default),
|
||||
.readable_reg = pcm3168a_readable_register,
|
||||
.volatile_reg = pcm3168a_volatile_register,
|
||||
.writeable_reg = pcm3168a_writeable_register,
|
||||
.cache_type = REGCACHE_FLAT
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(pcm3168a_regmap);
|
||||
|
||||
static const struct snd_soc_codec_driver pcm3168a_driver = {
|
||||
.idle_bias_off = true,
|
||||
.controls = pcm3168a_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(pcm3168a_snd_controls),
|
||||
.dapm_widgets = pcm3168a_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(pcm3168a_dapm_widgets),
|
||||
.dapm_routes = pcm3168a_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(pcm3168a_dapm_routes)
|
||||
};
|
||||
|
||||
int pcm3168a_probe(struct device *dev, struct regmap *regmap)
|
||||
{
|
||||
struct pcm3168a_priv *pcm3168a;
|
||||
int ret, i;
|
||||
|
||||
pcm3168a = devm_kzalloc(dev, sizeof(*pcm3168a), GFP_KERNEL);
|
||||
if (pcm3168a == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, pcm3168a);
|
||||
|
||||
pcm3168a->scki = devm_clk_get(dev, "scki");
|
||||
if (IS_ERR(pcm3168a->scki)) {
|
||||
ret = PTR_ERR(pcm3168a->scki);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to acquire clock 'scki': %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(pcm3168a->scki);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable mclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pcm3168a->sysclk = clk_get_rate(pcm3168a->scki);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pcm3168a->supplies); i++)
|
||||
pcm3168a->supplies[i].supply = pcm3168a_supply_names[i];
|
||||
|
||||
ret = devm_regulator_bulk_get(dev,
|
||||
ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to request supplies: %d\n", ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies),
|
||||
pcm3168a->supplies);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable supplies: %d\n", ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
pcm3168a->regmap = regmap;
|
||||
if (IS_ERR(pcm3168a->regmap)) {
|
||||
ret = PTR_ERR(pcm3168a->regmap);
|
||||
dev_err(dev, "failed to allocate regmap: %d\n", ret);
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
ret = pcm3168a_reset(pcm3168a);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to reset device: %d\n", ret);
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_idle(dev);
|
||||
|
||||
ret = snd_soc_register_codec(dev, &pcm3168a_driver, pcm3168a_dais,
|
||||
ARRAY_SIZE(pcm3168a_dais));
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register codec: %d\n", ret);
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_regulator:
|
||||
regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
|
||||
pcm3168a->supplies);
|
||||
err_clk:
|
||||
clk_disable_unprepare(pcm3168a->scki);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcm3168a_probe);
|
||||
|
||||
void pcm3168a_remove(struct device *dev)
|
||||
{
|
||||
struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
|
||||
|
||||
snd_soc_unregister_codec(dev);
|
||||
pm_runtime_disable(dev);
|
||||
regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
|
||||
pcm3168a->supplies);
|
||||
clk_disable_unprepare(pcm3168a->scki);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcm3168a_remove);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pcm3168a_rt_resume(struct device *dev)
|
||||
{
|
||||
struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(pcm3168a->scki);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable mclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies),
|
||||
pcm3168a->supplies);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable supplies: %d\n", ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
ret = pcm3168a_reset(pcm3168a);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to reset device: %d\n", ret);
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
regcache_cache_only(pcm3168a->regmap, false);
|
||||
|
||||
regcache_mark_dirty(pcm3168a->regmap);
|
||||
|
||||
ret = regcache_sync(pcm3168a->regmap);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to sync regmap: %d\n", ret);
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_regulator:
|
||||
regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
|
||||
pcm3168a->supplies);
|
||||
err_clk:
|
||||
clk_disable_unprepare(pcm3168a->scki);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pcm3168a_rt_suspend(struct device *dev)
|
||||
{
|
||||
struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
|
||||
|
||||
regcache_cache_only(pcm3168a->regmap, true);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
|
||||
pcm3168a->supplies);
|
||||
|
||||
clk_disable_unprepare(pcm3168a->scki);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
const struct dev_pm_ops pcm3168a_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL)
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(pcm3168a_pm_ops);
|
||||
|
||||
MODULE_DESCRIPTION("PCM3168A codec driver");
|
||||
MODULE_AUTHOR("Damien Horsley <[email protected]>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
100
sound/soc/codecs/pcm3168a.h
Normal file
100
sound/soc/codecs/pcm3168a.h
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* PCM3168A codec driver header
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __PCM3168A_H__
|
||||
#define __PCM3168A_H__
|
||||
|
||||
extern const struct dev_pm_ops pcm3168a_pm_ops;
|
||||
extern const struct regmap_config pcm3168a_regmap;
|
||||
|
||||
extern int pcm3168a_probe(struct device *dev, struct regmap *regmap);
|
||||
extern void pcm3168a_remove(struct device *dev);
|
||||
|
||||
#define PCM3168A_RST_SMODE 0x40
|
||||
#define PCM3168A_MRST_MASK 0x80
|
||||
#define PCM3168A_SRST_MASK 0x40
|
||||
#define PCM3168A_DAC_SRDA_SHIFT 0
|
||||
#define PCM3168A_DAC_SRDA_MASK 0x3
|
||||
|
||||
#define PCM3168A_DAC_PWR_MST_FMT 0x41
|
||||
#define PCM3168A_DAC_PSMDA_SHIFT 7
|
||||
#define PCM3168A_DAC_PSMDA_MASK 0x80
|
||||
#define PCM3168A_DAC_MSDA_SHIFT 4
|
||||
#define PCM3168A_DAC_MSDA_MASK 0x70
|
||||
#define PCM3168A_DAC_FMT_SHIFT 0
|
||||
#define PCM3168A_DAC_FMT_MASK 0xf
|
||||
|
||||
#define PCM3168A_DAC_OP_FLT 0x42
|
||||
#define PCM3168A_DAC_OPEDA_SHIFT 4
|
||||
#define PCM3168A_DAC_OPEDA_MASK 0xf0
|
||||
#define PCM3168A_DAC_FLT_SHIFT 0
|
||||
#define PCM3168A_DAC_FLT_MASK 0xf
|
||||
|
||||
#define PCM3168A_DAC_INV 0x43
|
||||
|
||||
#define PCM3168A_DAC_MUTE 0x44
|
||||
|
||||
#define PCM3168A_DAC_ZERO 0x45
|
||||
|
||||
#define PCM3168A_DAC_ATT_DEMP_ZF 0x46
|
||||
#define PCM3168A_DAC_ATMDDA_MASK 0x80
|
||||
#define PCM3168A_DAC_ATMDDA_SHIFT 7
|
||||
#define PCM3168A_DAC_ATSPDA_MASK 0x40
|
||||
#define PCM3168A_DAC_ATSPDA_SHIFT 6
|
||||
#define PCM3168A_DAC_DEMP_SHIFT 4
|
||||
#define PCM3168A_DAC_DEMP_MASK 0x30
|
||||
#define PCM3168A_DAC_AZRO_SHIFT 1
|
||||
#define PCM3168A_DAC_AZRO_MASK 0xe
|
||||
#define PCM3168A_DAC_ZREV_MASK 0x1
|
||||
#define PCM3168A_DAC_ZREV_SHIFT 0
|
||||
|
||||
#define PCM3168A_DAC_VOL_MASTER 0x47
|
||||
|
||||
#define PCM3168A_DAC_VOL_CHAN_START 0x48
|
||||
|
||||
#define PCM3168A_ADC_SMODE 0x50
|
||||
#define PCM3168A_ADC_SRAD_SHIFT 0
|
||||
#define PCM3168A_ADC_SRAD_MASK 0x3
|
||||
|
||||
#define PCM3168A_ADC_MST_FMT 0x51
|
||||
#define PCM3168A_ADC_MSAD_SHIFT 4
|
||||
#define PCM3168A_ADC_MSAD_MASK 0x70
|
||||
#define PCM3168A_ADC_FMTAD_SHIFT 0
|
||||
#define PCM3168A_ADC_FMTAD_MASK 0x7
|
||||
|
||||
#define PCM3168A_ADC_PWR_HPFB 0x52
|
||||
#define PCM3168A_ADC_PSVAD_SHIFT 4
|
||||
#define PCM3168A_ADC_PSVAD_MASK 0x70
|
||||
#define PCM3168A_ADC_BYP_SHIFT 0
|
||||
#define PCM3168A_ADC_BYP_MASK 0x7
|
||||
|
||||
#define PCM3168A_ADC_SEAD 0x53
|
||||
|
||||
#define PCM3168A_ADC_INV 0x54
|
||||
|
||||
#define PCM3168A_ADC_MUTE 0x55
|
||||
|
||||
#define PCM3168A_ADC_OV 0x56
|
||||
|
||||
#define PCM3168A_ADC_ATT_OVF 0x57
|
||||
#define PCM3168A_ADC_ATMDAD_MASK 0x80
|
||||
#define PCM3168A_ADC_ATMDAD_SHIFT 7
|
||||
#define PCM3168A_ADC_ATSPAD_MASK 0x40
|
||||
#define PCM3168A_ADC_ATSPAD_SHIFT 6
|
||||
#define PCM3168A_ADC_OVFP_MASK 0x1
|
||||
#define PCM3168A_ADC_OVFP_SHIFT 0
|
||||
|
||||
#define PCM3168A_ADC_VOL_MASTER 0x58
|
||||
|
||||
#define PCM3168A_ADC_VOL_CHAN_START 0x59
|
||||
|
||||
#endif
|
||||
|
|
@ -1114,6 +1114,12 @@ static const struct dmi_system_id force_combo_jack_table[] = {
|
|||
DMI_MATCH(DMI_BOARD_NAME, "Wilson Beach SDS")
|
||||
}
|
||||
},
|
||||
{
|
||||
.ident = "Intel Skylake RVP",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Skylake Client platform")
|
||||
}
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -854,8 +854,6 @@ static int rt298_set_dai_sysclk(struct snd_soc_dai *dai,
|
|||
} else {
|
||||
snd_soc_update_bits(codec,
|
||||
RT298_I2S_CTRL2, 0x0100, 0x0100);
|
||||
snd_soc_update_bits(codec,
|
||||
RT298_PLL_CTRL, 0x4, 0x4);
|
||||
snd_soc_update_bits(codec,
|
||||
RT298_PLL_CTRL1, 0x20, 0x0);
|
||||
}
|
||||
|
|
|
|||
1381
sound/soc/codecs/rt5616.c
Normal file
1381
sound/soc/codecs/rt5616.c
Normal file
File diff suppressed because it is too large
Load diff
1819
sound/soc/codecs/rt5616.h
Normal file
1819
sound/soc/codecs/rt5616.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -226,6 +226,163 @@ static const struct reg_default rt5645_reg[] = {
|
|||
{ 0xff, 0x6308 },
|
||||
};
|
||||
|
||||
static const struct reg_default rt5650_reg[] = {
|
||||
{ 0x00, 0x0000 },
|
||||
{ 0x01, 0xc8c8 },
|
||||
{ 0x02, 0xc8c8 },
|
||||
{ 0x03, 0xc8c8 },
|
||||
{ 0x0a, 0x0002 },
|
||||
{ 0x0b, 0x2827 },
|
||||
{ 0x0c, 0xe000 },
|
||||
{ 0x0d, 0x0000 },
|
||||
{ 0x0e, 0x0000 },
|
||||
{ 0x0f, 0x0808 },
|
||||
{ 0x14, 0x3333 },
|
||||
{ 0x16, 0x4b00 },
|
||||
{ 0x18, 0x018b },
|
||||
{ 0x19, 0xafaf },
|
||||
{ 0x1a, 0xafaf },
|
||||
{ 0x1b, 0x0001 },
|
||||
{ 0x1c, 0x2f2f },
|
||||
{ 0x1d, 0x2f2f },
|
||||
{ 0x1e, 0x0000 },
|
||||
{ 0x20, 0x0000 },
|
||||
{ 0x27, 0x7060 },
|
||||
{ 0x28, 0x7070 },
|
||||
{ 0x29, 0x8080 },
|
||||
{ 0x2a, 0x5656 },
|
||||
{ 0x2b, 0x5454 },
|
||||
{ 0x2c, 0xaaa0 },
|
||||
{ 0x2d, 0x0000 },
|
||||
{ 0x2f, 0x1002 },
|
||||
{ 0x31, 0x5000 },
|
||||
{ 0x32, 0x0000 },
|
||||
{ 0x33, 0x0000 },
|
||||
{ 0x34, 0x0000 },
|
||||
{ 0x35, 0x0000 },
|
||||
{ 0x3b, 0x0000 },
|
||||
{ 0x3c, 0x007f },
|
||||
{ 0x3d, 0x0000 },
|
||||
{ 0x3e, 0x007f },
|
||||
{ 0x3f, 0x0000 },
|
||||
{ 0x40, 0x001f },
|
||||
{ 0x41, 0x0000 },
|
||||
{ 0x42, 0x001f },
|
||||
{ 0x45, 0x6000 },
|
||||
{ 0x46, 0x003e },
|
||||
{ 0x47, 0x003e },
|
||||
{ 0x48, 0xf807 },
|
||||
{ 0x4a, 0x0004 },
|
||||
{ 0x4d, 0x0000 },
|
||||
{ 0x4e, 0x0000 },
|
||||
{ 0x4f, 0x01ff },
|
||||
{ 0x50, 0x0000 },
|
||||
{ 0x51, 0x0000 },
|
||||
{ 0x52, 0x01ff },
|
||||
{ 0x53, 0xf000 },
|
||||
{ 0x56, 0x0111 },
|
||||
{ 0x57, 0x0064 },
|
||||
{ 0x58, 0xef0e },
|
||||
{ 0x59, 0xf0f0 },
|
||||
{ 0x5a, 0xef0e },
|
||||
{ 0x5b, 0xf0f0 },
|
||||
{ 0x5c, 0xef0e },
|
||||
{ 0x5d, 0xf0f0 },
|
||||
{ 0x5e, 0xf000 },
|
||||
{ 0x5f, 0x0000 },
|
||||
{ 0x61, 0x0300 },
|
||||
{ 0x62, 0x0000 },
|
||||
{ 0x63, 0x00c2 },
|
||||
{ 0x64, 0x0000 },
|
||||
{ 0x65, 0x0000 },
|
||||
{ 0x66, 0x0000 },
|
||||
{ 0x6a, 0x0000 },
|
||||
{ 0x6c, 0x0aaa },
|
||||
{ 0x70, 0x8000 },
|
||||
{ 0x71, 0x8000 },
|
||||
{ 0x72, 0x8000 },
|
||||
{ 0x73, 0x7770 },
|
||||
{ 0x74, 0x3e00 },
|
||||
{ 0x75, 0x2409 },
|
||||
{ 0x76, 0x000a },
|
||||
{ 0x77, 0x0c00 },
|
||||
{ 0x78, 0x0000 },
|
||||
{ 0x79, 0x0123 },
|
||||
{ 0x7a, 0x0123 },
|
||||
{ 0x80, 0x0000 },
|
||||
{ 0x81, 0x0000 },
|
||||
{ 0x82, 0x0000 },
|
||||
{ 0x83, 0x0000 },
|
||||
{ 0x84, 0x0000 },
|
||||
{ 0x85, 0x0000 },
|
||||
{ 0x8a, 0x0000 },
|
||||
{ 0x8e, 0x0004 },
|
||||
{ 0x8f, 0x1100 },
|
||||
{ 0x90, 0x0646 },
|
||||
{ 0x91, 0x0c06 },
|
||||
{ 0x93, 0x0000 },
|
||||
{ 0x94, 0x0200 },
|
||||
{ 0x95, 0x0000 },
|
||||
{ 0x9a, 0x2184 },
|
||||
{ 0x9b, 0x010a },
|
||||
{ 0x9c, 0x0aea },
|
||||
{ 0x9d, 0x000c },
|
||||
{ 0x9e, 0x0400 },
|
||||
{ 0xa0, 0xa0a8 },
|
||||
{ 0xa1, 0x0059 },
|
||||
{ 0xa2, 0x0001 },
|
||||
{ 0xae, 0x6000 },
|
||||
{ 0xaf, 0x0000 },
|
||||
{ 0xb0, 0x6000 },
|
||||
{ 0xb1, 0x0000 },
|
||||
{ 0xb2, 0x0000 },
|
||||
{ 0xb3, 0x001f },
|
||||
{ 0xb4, 0x020c },
|
||||
{ 0xb5, 0x1f00 },
|
||||
{ 0xb6, 0x0000 },
|
||||
{ 0xbb, 0x0000 },
|
||||
{ 0xbc, 0x0000 },
|
||||
{ 0xbd, 0x0000 },
|
||||
{ 0xbe, 0x0000 },
|
||||
{ 0xbf, 0x3100 },
|
||||
{ 0xc0, 0x0000 },
|
||||
{ 0xc1, 0x0000 },
|
||||
{ 0xc2, 0x0000 },
|
||||
{ 0xc3, 0x2000 },
|
||||
{ 0xcd, 0x0000 },
|
||||
{ 0xce, 0x0000 },
|
||||
{ 0xcf, 0x1813 },
|
||||
{ 0xd0, 0x0690 },
|
||||
{ 0xd1, 0x1c17 },
|
||||
{ 0xd3, 0xb320 },
|
||||
{ 0xd4, 0x0000 },
|
||||
{ 0xd6, 0x0400 },
|
||||
{ 0xd9, 0x0809 },
|
||||
{ 0xda, 0x0000 },
|
||||
{ 0xdb, 0x0003 },
|
||||
{ 0xdc, 0x0049 },
|
||||
{ 0xdd, 0x001b },
|
||||
{ 0xdf, 0x0008 },
|
||||
{ 0xe0, 0x4000 },
|
||||
{ 0xe6, 0x8000 },
|
||||
{ 0xe7, 0x0200 },
|
||||
{ 0xec, 0xb300 },
|
||||
{ 0xed, 0x0000 },
|
||||
{ 0xf0, 0x001f },
|
||||
{ 0xf1, 0x020c },
|
||||
{ 0xf2, 0x1f00 },
|
||||
{ 0xf3, 0x0000 },
|
||||
{ 0xf4, 0x4000 },
|
||||
{ 0xf8, 0x0000 },
|
||||
{ 0xf9, 0x0000 },
|
||||
{ 0xfa, 0x2060 },
|
||||
{ 0xfb, 0x4040 },
|
||||
{ 0xfc, 0x0000 },
|
||||
{ 0xfd, 0x0002 },
|
||||
{ 0xfe, 0x10ec },
|
||||
{ 0xff, 0x6308 },
|
||||
};
|
||||
|
||||
struct rt5645_eq_param_s {
|
||||
unsigned short reg;
|
||||
unsigned short val;
|
||||
|
|
@ -572,14 +729,12 @@ static int rt5645_spk_put_volsw(struct snd_kcontrol *kcontrol,
|
|||
struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
|
||||
cancel_delayed_work_sync(&rt5645->rcclock_work);
|
||||
|
||||
regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
|
||||
RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PU);
|
||||
|
||||
ret = snd_soc_put_volsw(kcontrol, ucontrol);
|
||||
|
||||
queue_delayed_work(system_power_efficient_wq, &rt5645->rcclock_work,
|
||||
mod_delayed_work(system_power_efficient_wq, &rt5645->rcclock_work,
|
||||
msecs_to_jiffies(200));
|
||||
|
||||
return ret;
|
||||
|
|
@ -3322,6 +3477,31 @@ static const struct regmap_config rt5645_regmap = {
|
|||
.num_ranges = ARRAY_SIZE(rt5645_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_config rt5650_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.use_single_rw = true,
|
||||
.max_register = RT5645_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5645_ranges) *
|
||||
RT5645_PR_SPACING),
|
||||
.volatile_reg = rt5645_volatile_register,
|
||||
.readable_reg = rt5645_readable_register,
|
||||
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.reg_defaults = rt5650_reg,
|
||||
.num_reg_defaults = ARRAY_SIZE(rt5650_reg),
|
||||
.ranges = rt5645_ranges,
|
||||
.num_ranges = ARRAY_SIZE(rt5645_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_config temp_regmap = {
|
||||
.name="nocache",
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.use_single_rw = true,
|
||||
.max_register = RT5645_VENDOR_ID2 + 1,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
};
|
||||
|
||||
static const struct i2c_device_id rt5645_i2c_id[] = {
|
||||
{ "rt5645", 0 },
|
||||
{ "rt5650", 0 },
|
||||
|
|
@ -3338,69 +3518,23 @@ static struct acpi_device_id rt5645_acpi_match[] = {
|
|||
MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match);
|
||||
#endif
|
||||
|
||||
static struct rt5645_platform_data *rt5645_pdata;
|
||||
|
||||
static struct rt5645_platform_data strago_platform_data = {
|
||||
static struct rt5645_platform_data general_platform_data = {
|
||||
.dmic1_data_pin = RT5645_DMIC1_DISABLE,
|
||||
.dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
|
||||
.jd_mode = 3,
|
||||
};
|
||||
|
||||
static int strago_quirk_cb(const struct dmi_system_id *id)
|
||||
{
|
||||
rt5645_pdata = &strago_platform_data;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id dmi_platform_intel_braswell[] = {
|
||||
{
|
||||
.ident = "Intel Strago",
|
||||
.callback = strago_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Strago"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Google Celes",
|
||||
.callback = strago_quirk_cb,
|
||||
.ident = "Google Chrome",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Celes"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Google Ultima",
|
||||
.callback = strago_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Ultima"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Google Reks",
|
||||
.callback = strago_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Reks"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Google Edgar",
|
||||
.callback = strago_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Edgar"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Google Wizpig",
|
||||
.callback = strago_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Wizpig"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Google Terra",
|
||||
.callback = strago_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Terra"),
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
|
|
@ -3413,17 +3547,9 @@ static struct rt5645_platform_data buddy_platform_data = {
|
|||
.jd_invert = true,
|
||||
};
|
||||
|
||||
static int buddy_quirk_cb(const struct dmi_system_id *id)
|
||||
{
|
||||
rt5645_pdata = &buddy_platform_data;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct dmi_system_id dmi_platform_intel_broadwell[] = {
|
||||
{
|
||||
.ident = "Chrome Buddy",
|
||||
.callback = buddy_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"),
|
||||
},
|
||||
|
|
@ -3431,6 +3557,16 @@ static struct dmi_system_id dmi_platform_intel_broadwell[] = {
|
|||
{ }
|
||||
};
|
||||
|
||||
static bool rt5645_check_dp(struct device *dev)
|
||||
{
|
||||
if (device_property_present(dev, "realtek,in2-differential") ||
|
||||
device_property_present(dev, "realtek,dmic1-data-pin") ||
|
||||
device_property_present(dev, "realtek,dmic2-data-pin") ||
|
||||
device_property_present(dev, "realtek,jd-mode"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
|
||||
{
|
||||
|
|
@ -3453,6 +3589,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
|
|||
struct rt5645_priv *rt5645;
|
||||
int ret, i;
|
||||
unsigned int val;
|
||||
struct regmap *regmap;
|
||||
|
||||
rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv),
|
||||
GFP_KERNEL);
|
||||
|
|
@ -3464,11 +3601,12 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
|
|||
|
||||
if (pdata)
|
||||
rt5645->pdata = *pdata;
|
||||
else if (dmi_check_system(dmi_platform_intel_braswell) ||
|
||||
dmi_check_system(dmi_platform_intel_broadwell))
|
||||
rt5645->pdata = *rt5645_pdata;
|
||||
else
|
||||
else if (dmi_check_system(dmi_platform_intel_broadwell))
|
||||
rt5645->pdata = buddy_platform_data;
|
||||
else if (rt5645_check_dp(&i2c->dev))
|
||||
rt5645_parse_dt(rt5645, &i2c->dev);
|
||||
else if (dmi_check_system(dmi_platform_intel_braswell))
|
||||
rt5645->pdata = general_platform_data;
|
||||
|
||||
rt5645->gpiod_hp_det = devm_gpiod_get_optional(&i2c->dev, "hp-detect",
|
||||
GPIOD_IN);
|
||||
|
|
@ -3478,14 +3616,6 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
|
|||
return PTR_ERR(rt5645->gpiod_hp_det);
|
||||
}
|
||||
|
||||
rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap);
|
||||
if (IS_ERR(rt5645->regmap)) {
|
||||
ret = PTR_ERR(rt5645->regmap);
|
||||
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rt5645->supplies); i++)
|
||||
rt5645->supplies[i].supply = rt5645_supply_names[i];
|
||||
|
||||
|
|
@ -3504,13 +3634,22 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
|
|||
return ret;
|
||||
}
|
||||
|
||||
regmap_read(rt5645->regmap, RT5645_VENDOR_ID2, &val);
|
||||
regmap = devm_regmap_init_i2c(i2c, &temp_regmap);
|
||||
if (IS_ERR(regmap)) {
|
||||
ret = PTR_ERR(regmap);
|
||||
dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
regmap_read(regmap, RT5645_VENDOR_ID2, &val);
|
||||
|
||||
switch (val) {
|
||||
case RT5645_DEVICE_ID:
|
||||
rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap);
|
||||
rt5645->codec_type = CODEC_TYPE_RT5645;
|
||||
break;
|
||||
case RT5650_DEVICE_ID:
|
||||
rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5650_regmap);
|
||||
rt5645->codec_type = CODEC_TYPE_RT5650;
|
||||
break;
|
||||
default:
|
||||
|
|
@ -3521,6 +3660,13 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
|
|||
goto err_enable;
|
||||
}
|
||||
|
||||
if (IS_ERR(rt5645->regmap)) {
|
||||
ret = PTR_ERR(rt5645->regmap);
|
||||
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmap_write(rt5645->regmap, RT5645_RESET, 0);
|
||||
|
||||
ret = regmap_register_patch(rt5645->regmap, init_list,
|
||||
|
|
|
|||
4223
sound/soc/codecs/rt5659.c
Normal file
4223
sound/soc/codecs/rt5659.c
Normal file
File diff suppressed because it is too large
Load diff
1819
sound/soc/codecs/rt5659.h
Normal file
1819
sound/soc/codecs/rt5659.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -297,8 +297,6 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
|
|||
case RT5677_HAP_GENE_CTRL2:
|
||||
case RT5677_PWR_DSP_ST:
|
||||
case RT5677_PRIV_DATA:
|
||||
case RT5677_PLL1_CTRL2:
|
||||
case RT5677_PLL2_CTRL2:
|
||||
case RT5677_ASRC_22:
|
||||
case RT5677_ASRC_23:
|
||||
case RT5677_VAD_CTRL5:
|
||||
|
|
@ -4788,7 +4786,7 @@ static int rt5677_remove(struct snd_soc_codec *codec)
|
|||
|
||||
regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
|
||||
gpiod_set_value_cansleep(rt5677->pow_ldo2, 0);
|
||||
gpiod_set_value_cansleep(rt5677->reset_pin, 0);
|
||||
gpiod_set_value_cansleep(rt5677->reset_pin, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -4803,7 +4801,7 @@ static int rt5677_suspend(struct snd_soc_codec *codec)
|
|||
regcache_mark_dirty(rt5677->regmap);
|
||||
|
||||
gpiod_set_value_cansleep(rt5677->pow_ldo2, 0);
|
||||
gpiod_set_value_cansleep(rt5677->reset_pin, 0);
|
||||
gpiod_set_value_cansleep(rt5677->reset_pin, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -4814,8 +4812,11 @@ static int rt5677_resume(struct snd_soc_codec *codec)
|
|||
struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
if (!rt5677->dsp_vad_en) {
|
||||
rt5677->pll_src = 0;
|
||||
rt5677->pll_in = 0;
|
||||
rt5677->pll_out = 0;
|
||||
gpiod_set_value_cansleep(rt5677->pow_ldo2, 1);
|
||||
gpiod_set_value_cansleep(rt5677->reset_pin, 1);
|
||||
gpiod_set_value_cansleep(rt5677->reset_pin, 0);
|
||||
if (rt5677->pow_ldo2 || rt5677->reset_pin)
|
||||
msleep(10);
|
||||
|
||||
|
|
@ -5160,7 +5161,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
|
|||
return ret;
|
||||
}
|
||||
rt5677->reset_pin = devm_gpiod_get_optional(&i2c->dev,
|
||||
"realtek,reset", GPIOD_OUT_HIGH);
|
||||
"realtek,reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(rt5677->reset_pin)) {
|
||||
ret = PTR_ERR(rt5677->reset_pin);
|
||||
dev_err(&i2c->dev, "Failed to request RESET: %d\n", ret);
|
||||
|
|
|
|||
|
|
@ -309,7 +309,7 @@ static const struct snd_pcm_hw_constraint_list ssm2518_constraints_12288000 = {
|
|||
.count = ARRAY_SIZE(ssm2518_rates_12288000),
|
||||
};
|
||||
|
||||
static unsigned int ssm2518_lookup_mcs(struct ssm2518 *ssm2518,
|
||||
static int ssm2518_lookup_mcs(struct ssm2518 *ssm2518,
|
||||
unsigned int rate)
|
||||
{
|
||||
const unsigned int *sysclks = NULL;
|
||||
|
|
|
|||
|
|
@ -573,6 +573,33 @@ static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
|
|||
SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \
|
||||
SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0)
|
||||
|
||||
#define WM5110_RXANC_INPUT_ROUTES(widget, name) \
|
||||
{ widget, NULL, name " NG Mux" }, \
|
||||
{ name " NG Internal", NULL, "RXANC NG Clock" }, \
|
||||
{ name " NG Internal", NULL, name " Channel" }, \
|
||||
{ name " NG External", NULL, "RXANC NG External Clock" }, \
|
||||
{ name " NG External", NULL, name " Channel" }, \
|
||||
{ name " NG Mux", "None", name " Channel" }, \
|
||||
{ name " NG Mux", "Internal", name " NG Internal" }, \
|
||||
{ name " NG Mux", "External", name " NG External" }, \
|
||||
{ name " Channel", "Left", name " Left Input" }, \
|
||||
{ name " Channel", "Combine", name " Left Input" }, \
|
||||
{ name " Channel", "Right", name " Right Input" }, \
|
||||
{ name " Channel", "Combine", name " Right Input" }, \
|
||||
{ name " Left Input", "IN1", "IN1L PGA" }, \
|
||||
{ name " Right Input", "IN1", "IN1R PGA" }, \
|
||||
{ name " Left Input", "IN2", "IN2L PGA" }, \
|
||||
{ name " Right Input", "IN2", "IN2R PGA" }, \
|
||||
{ name " Left Input", "IN3", "IN3L PGA" }, \
|
||||
{ name " Right Input", "IN3", "IN3R PGA" }, \
|
||||
{ name " Left Input", "IN4", "IN4L PGA" }, \
|
||||
{ name " Right Input", "IN4", "IN4R PGA" }
|
||||
|
||||
#define WM5110_RXANC_OUTPUT_ROUTES(widget, name) \
|
||||
{ widget, NULL, name " ANC Source" }, \
|
||||
{ name " ANC Source", "RXANCL", "RXANCL" }, \
|
||||
{ name " ANC Source", "RXANCR", "RXANCR" }
|
||||
|
||||
static const struct snd_kcontrol_new wm5110_snd_controls[] = {
|
||||
SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]),
|
||||
SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
|
||||
|
|
@ -637,6 +664,15 @@ SOC_SINGLE_TLV("IN4R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4R,
|
|||
SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
|
||||
SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
|
||||
|
||||
SND_SOC_BYTES("RXANC Coefficients", ARIZONA_ANC_COEFF_START,
|
||||
ARIZONA_ANC_COEFF_END - ARIZONA_ANC_COEFF_START + 1),
|
||||
SND_SOC_BYTES("RXANCL Config", ARIZONA_FCL_FILTER_CONTROL, 1),
|
||||
SND_SOC_BYTES("RXANCL Coefficients", ARIZONA_FCL_COEFF_START,
|
||||
ARIZONA_FCL_COEFF_END - ARIZONA_FCL_COEFF_START + 1),
|
||||
SND_SOC_BYTES("RXANCR Config", ARIZONA_FCR_FILTER_CONTROL, 1),
|
||||
SND_SOC_BYTES("RXANCR Coefficients", ARIZONA_FCR_COEFF_START,
|
||||
ARIZONA_FCR_COEFF_END - ARIZONA_FCR_COEFF_START + 1),
|
||||
|
||||
ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
|
||||
ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
|
||||
ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
|
||||
|
|
@ -993,6 +1029,31 @@ static const struct soc_enum wm5110_aec_loopback =
|
|||
static const struct snd_kcontrol_new wm5110_aec_loopback_mux =
|
||||
SOC_DAPM_ENUM("AEC Loopback", wm5110_aec_loopback);
|
||||
|
||||
static const struct snd_kcontrol_new wm5110_anc_input_mux[] = {
|
||||
SOC_DAPM_ENUM("RXANCL Input", arizona_anc_input_src[0]),
|
||||
SOC_DAPM_ENUM("RXANCL Channel", arizona_anc_input_src[1]),
|
||||
SOC_DAPM_ENUM("RXANCR Input", arizona_anc_input_src[2]),
|
||||
SOC_DAPM_ENUM("RXANCR Channel", arizona_anc_input_src[3]),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new wm5110_anc_ng_mux =
|
||||
SOC_DAPM_ENUM("RXANC NG Source", arizona_anc_ng_enum);
|
||||
|
||||
static const struct snd_kcontrol_new wm5110_output_anc_src[] = {
|
||||
SOC_DAPM_ENUM("HPOUT1L ANC Source", arizona_output_anc_src[0]),
|
||||
SOC_DAPM_ENUM("HPOUT1R ANC Source", arizona_output_anc_src[1]),
|
||||
SOC_DAPM_ENUM("HPOUT2L ANC Source", arizona_output_anc_src[2]),
|
||||
SOC_DAPM_ENUM("HPOUT2R ANC Source", arizona_output_anc_src[3]),
|
||||
SOC_DAPM_ENUM("HPOUT3L ANC Source", arizona_output_anc_src[4]),
|
||||
SOC_DAPM_ENUM("HPOUT3R ANC Source", arizona_output_anc_src[5]),
|
||||
SOC_DAPM_ENUM("SPKOUTL ANC Source", arizona_output_anc_src[6]),
|
||||
SOC_DAPM_ENUM("SPKOUTR ANC Source", arizona_output_anc_src[7]),
|
||||
SOC_DAPM_ENUM("SPKDAT1L ANC Source", arizona_output_anc_src[8]),
|
||||
SOC_DAPM_ENUM("SPKDAT1R ANC Source", arizona_output_anc_src[9]),
|
||||
SOC_DAPM_ENUM("SPKDAT2L ANC Source", arizona_output_anc_src[10]),
|
||||
SOC_DAPM_ENUM("SPKDAT2R ANC Source", arizona_output_anc_src[11]),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
|
||||
0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU),
|
||||
|
|
@ -1183,6 +1244,65 @@ SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
|
|||
ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
|
||||
&wm5110_aec_loopback_mux),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("RXANC NG External Clock", SND_SOC_NOPM,
|
||||
ARIZONA_EXT_NG_SEL_SET_SHIFT, 0, arizona_anc_ev,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_PGA("RXANCL NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("RXANCR NG External", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("RXANC NG Clock", SND_SOC_NOPM,
|
||||
ARIZONA_CLK_NG_ENA_SET_SHIFT, 0, arizona_anc_ev,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_PGA("RXANCL NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("RXANCR NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_MUX("RXANCL Left Input", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_anc_input_mux[0]),
|
||||
SND_SOC_DAPM_MUX("RXANCL Right Input", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_anc_input_mux[0]),
|
||||
SND_SOC_DAPM_MUX("RXANCL Channel", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_anc_input_mux[1]),
|
||||
SND_SOC_DAPM_MUX("RXANCL NG Mux", SND_SOC_NOPM, 0, 0, &wm5110_anc_ng_mux),
|
||||
SND_SOC_DAPM_MUX("RXANCR Left Input", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_anc_input_mux[2]),
|
||||
SND_SOC_DAPM_MUX("RXANCR Right Input", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_anc_input_mux[2]),
|
||||
SND_SOC_DAPM_MUX("RXANCR Channel", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_anc_input_mux[3]),
|
||||
SND_SOC_DAPM_MUX("RXANCR NG Mux", SND_SOC_NOPM, 0, 0, &wm5110_anc_ng_mux),
|
||||
|
||||
SND_SOC_DAPM_PGA_E("RXANCL", SND_SOC_NOPM, ARIZONA_CLK_L_ENA_SET_SHIFT,
|
||||
0, NULL, 0, arizona_anc_ev,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_PGA_E("RXANCR", SND_SOC_NOPM, ARIZONA_CLK_R_ENA_SET_SHIFT,
|
||||
0, NULL, 0, arizona_anc_ev,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
|
||||
SND_SOC_DAPM_MUX("HPOUT1L ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[0]),
|
||||
SND_SOC_DAPM_MUX("HPOUT1R ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[1]),
|
||||
SND_SOC_DAPM_MUX("HPOUT2L ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[2]),
|
||||
SND_SOC_DAPM_MUX("HPOUT2R ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[3]),
|
||||
SND_SOC_DAPM_MUX("HPOUT3L ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[4]),
|
||||
SND_SOC_DAPM_MUX("HPOUT3R ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[5]),
|
||||
SND_SOC_DAPM_MUX("SPKOUTL ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[6]),
|
||||
SND_SOC_DAPM_MUX("SPKOUTR ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[7]),
|
||||
SND_SOC_DAPM_MUX("SPKDAT1L ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[8]),
|
||||
SND_SOC_DAPM_MUX("SPKDAT1R ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[9]),
|
||||
SND_SOC_DAPM_MUX("SPKDAT2L ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[10]),
|
||||
SND_SOC_DAPM_MUX("SPKDAT2R ANC Source", SND_SOC_NOPM, 0, 0,
|
||||
&wm5110_output_anc_src[11]),
|
||||
|
||||
SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
|
||||
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
|
||||
SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
|
||||
|
|
@ -1688,6 +1808,9 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
|
|||
{ "Slim2 Capture", NULL, "SYSCLK" },
|
||||
{ "Slim3 Capture", NULL, "SYSCLK" },
|
||||
|
||||
{ "Voice Control DSP", NULL, "DSP3" },
|
||||
{ "Voice Control DSP", NULL, "SYSCLK" },
|
||||
|
||||
{ "IN1L PGA", NULL, "IN1L" },
|
||||
{ "IN1R PGA", NULL, "IN1R" },
|
||||
|
||||
|
|
@ -1836,6 +1959,22 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
|
|||
{ "SPKDAT2L", NULL, "OUT6L" },
|
||||
{ "SPKDAT2R", NULL, "OUT6R" },
|
||||
|
||||
WM5110_RXANC_INPUT_ROUTES("RXANCL", "RXANCL"),
|
||||
WM5110_RXANC_INPUT_ROUTES("RXANCR", "RXANCR"),
|
||||
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT1L", "HPOUT1L"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT1R", "HPOUT1R"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT2L", "HPOUT2L"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT2R", "HPOUT2R"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT3L", "HPOUT3L"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT3R", "HPOUT3R"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT4L", "SPKOUTL"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT4R", "SPKOUTR"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT5L", "SPKDAT1L"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT5R", "SPKDAT1R"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT6L", "SPKDAT2L"),
|
||||
WM5110_RXANC_OUTPUT_ROUTES("OUT6R", "SPKDAT2R"),
|
||||
|
||||
{ "MICSUPP", NULL, "SYSCLK" },
|
||||
|
||||
{ "DRC1 Signal Activity", NULL, "DRC1L" },
|
||||
|
|
@ -1994,8 +2133,48 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
|
|||
},
|
||||
.ops = &arizona_simple_dai_ops,
|
||||
},
|
||||
{
|
||||
.name = "wm5110-cpu-voicectrl",
|
||||
.capture = {
|
||||
.stream_name = "Voice Control CPU",
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.rates = WM5110_RATES,
|
||||
.formats = WM5110_FORMATS,
|
||||
},
|
||||
.compress_new = snd_soc_new_compress,
|
||||
},
|
||||
{
|
||||
.name = "wm5110-dsp-voicectrl",
|
||||
.capture = {
|
||||
.stream_name = "Voice Control DSP",
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.rates = WM5110_RATES,
|
||||
.formats = WM5110_FORMATS,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int wm5110_open(struct snd_compr_stream *stream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = stream->private_data;
|
||||
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
|
||||
struct arizona *arizona = priv->core.arizona;
|
||||
int n_adsp;
|
||||
|
||||
if (strcmp(rtd->codec_dai->name, "wm5110-dsp-voicectrl") == 0) {
|
||||
n_adsp = 2;
|
||||
} else {
|
||||
dev_err(arizona->dev,
|
||||
"No suitable compressed stream for DAI '%s'\n",
|
||||
rtd->codec_dai->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream);
|
||||
}
|
||||
|
||||
static int wm5110_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
||||
|
|
@ -2086,6 +2265,18 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5110 = {
|
|||
.num_dapm_routes = ARRAY_SIZE(wm5110_dapm_routes),
|
||||
};
|
||||
|
||||
static struct snd_compr_ops wm5110_compr_ops = {
|
||||
.open = wm5110_open,
|
||||
.free = wm_adsp_compr_free,
|
||||
.set_params = wm_adsp_compr_set_params,
|
||||
.get_caps = wm_adsp_compr_get_caps,
|
||||
.trigger = wm_adsp_compr_trigger,
|
||||
};
|
||||
|
||||
static struct snd_soc_platform_driver wm5110_compr_platform = {
|
||||
.compr_ops = &wm5110_compr_ops,
|
||||
};
|
||||
|
||||
static int wm5110_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
|
||||
|
|
@ -2146,8 +2337,21 @@ static int wm5110_probe(struct platform_device *pdev)
|
|||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_idle(&pdev->dev);
|
||||
|
||||
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
|
||||
ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
|
||||
wm5110_dai, ARRAY_SIZE(wm5110_dai));
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
}
|
||||
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm5110_remove(struct platform_device *pdev)
|
||||
|
|
|
|||
|
|
@ -1804,7 +1804,7 @@ static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset)
|
|||
|
||||
regmap_read(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset, ®);
|
||||
|
||||
return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT;
|
||||
return !!((reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT);
|
||||
}
|
||||
|
||||
static int wm8903_gpio_direction_out(struct gpio_chip *chip,
|
||||
|
|
|
|||
|
|
@ -312,7 +312,7 @@ static bool wm8904_readable_register(struct device *dev, unsigned int reg)
|
|||
case WM8904_FLL_NCO_TEST_1:
|
||||
return true;
|
||||
default:
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ static const struct reg_default wm8962_reg[] = {
|
|||
{ 15, 0x6243 }, /* R15 - Software Reset */
|
||||
|
||||
{ 17, 0x007B }, /* R17 - ALC1 */
|
||||
|
||||
{ 18, 0x0000 }, /* R18 - ALC2 */
|
||||
{ 19, 0x1C32 }, /* R19 - ALC3 */
|
||||
{ 20, 0x3200 }, /* R20 - Noise Gate */
|
||||
{ 21, 0x00C0 }, /* R21 - Left ADC volume */
|
||||
|
|
@ -794,7 +794,6 @@ static bool wm8962_volatile_register(struct device *dev, unsigned int reg)
|
|||
case WM8962_CLOCKING1:
|
||||
case WM8962_CLOCKING2:
|
||||
case WM8962_SOFTWARE_RESET:
|
||||
case WM8962_ALC2:
|
||||
case WM8962_THERMAL_SHUTDOWN_STATUS:
|
||||
case WM8962_ADDITIONAL_CONTROL_4:
|
||||
case WM8962_DC_SERVO_6:
|
||||
|
|
|
|||
|
|
@ -632,9 +632,16 @@ static const struct i2c_device_id wm8974_i2c_id[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
|
||||
|
||||
static const struct of_device_id wm8974_of_match[] = {
|
||||
{ .compatible = "wlf,wm8974", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, wm8974_of_match);
|
||||
|
||||
static struct i2c_driver wm8974_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm8974",
|
||||
.of_match_table = wm8974_of_match,
|
||||
},
|
||||
.probe = wm8974_i2c_probe,
|
||||
.remove = wm8974_i2c_remove,
|
||||
|
|
|
|||
|
|
@ -199,20 +199,20 @@ static const char * const wm8998_inmux_texts[] = {
|
|||
"B",
|
||||
};
|
||||
|
||||
static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum,
|
||||
ARIZONA_ADC_DIGITAL_VOLUME_1L,
|
||||
ARIZONA_IN1L_SRC_SHIFT,
|
||||
wm8998_inmux_texts);
|
||||
static SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum,
|
||||
ARIZONA_ADC_DIGITAL_VOLUME_1L,
|
||||
ARIZONA_IN1L_SRC_SHIFT,
|
||||
wm8998_inmux_texts);
|
||||
|
||||
static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxr_enum,
|
||||
ARIZONA_ADC_DIGITAL_VOLUME_1R,
|
||||
ARIZONA_IN1R_SRC_SHIFT,
|
||||
wm8998_inmux_texts);
|
||||
static SOC_ENUM_SINGLE_DECL(wm8998_in1muxr_enum,
|
||||
ARIZONA_ADC_DIGITAL_VOLUME_1R,
|
||||
ARIZONA_IN1R_SRC_SHIFT,
|
||||
wm8998_inmux_texts);
|
||||
|
||||
static const SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum,
|
||||
ARIZONA_ADC_DIGITAL_VOLUME_2L,
|
||||
ARIZONA_IN2L_SRC_SHIFT,
|
||||
wm8998_inmux_texts);
|
||||
static SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum,
|
||||
ARIZONA_ADC_DIGITAL_VOLUME_2L,
|
||||
ARIZONA_IN2L_SRC_SHIFT,
|
||||
wm8998_inmux_texts);
|
||||
|
||||
static const struct snd_kcontrol_new wm8998_in1mux[2] = {
|
||||
SOC_DAPM_ENUM_EXT("IN1L Mux", wm8998_in1muxl_enum,
|
||||
|
|
@ -522,17 +522,17 @@ static const unsigned int wm8998_aec_loopback_values[] = {
|
|||
0, 1, 2, 3, 4, 6, 7, 8, 9,
|
||||
};
|
||||
|
||||
static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec1_loopback,
|
||||
ARIZONA_DAC_AEC_CONTROL_1,
|
||||
ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
|
||||
wm8998_aec_loopback_texts,
|
||||
wm8998_aec_loopback_values);
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec1_loopback,
|
||||
ARIZONA_DAC_AEC_CONTROL_1,
|
||||
ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
|
||||
wm8998_aec_loopback_texts,
|
||||
wm8998_aec_loopback_values);
|
||||
|
||||
static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec2_loopback,
|
||||
ARIZONA_DAC_AEC_CONTROL_2,
|
||||
ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
|
||||
wm8998_aec_loopback_texts,
|
||||
wm8998_aec_loopback_values);
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec2_loopback,
|
||||
ARIZONA_DAC_AEC_CONTROL_2,
|
||||
ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
|
||||
wm8998_aec_loopback_texts,
|
||||
wm8998_aec_loopback_values);
|
||||
|
||||
static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = {
|
||||
SOC_DAPM_ENUM("AEC1 Loopback", wm8998_aec1_loopback),
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
|
|
@ -39,34 +40,6 @@ struct wm9713_priv {
|
|||
struct mutex lock;
|
||||
};
|
||||
|
||||
static unsigned int ac97_read(struct snd_soc_codec *codec,
|
||||
unsigned int reg);
|
||||
static int ac97_write(struct snd_soc_codec *codec,
|
||||
unsigned int reg, unsigned int val);
|
||||
|
||||
/*
|
||||
* WM9713 register cache
|
||||
* Reg 0x3c bit 15 is used by touch driver.
|
||||
*/
|
||||
static const u16 wm9713_reg[] = {
|
||||
0x6174, 0x8080, 0x8080, 0x8080,
|
||||
0xc880, 0xe808, 0xe808, 0x0808,
|
||||
0x00da, 0x8000, 0xd600, 0xaaa0,
|
||||
0xaaa0, 0xaaa0, 0x0000, 0x0000,
|
||||
0x0f0f, 0x0040, 0x0000, 0x7f00,
|
||||
0x0405, 0x0410, 0xbb80, 0xbb80,
|
||||
0x0000, 0xbb80, 0x0000, 0x4523,
|
||||
0x0000, 0x2000, 0x7eff, 0xffff,
|
||||
0x0000, 0x0000, 0x0080, 0x0000,
|
||||
0x0000, 0x0000, 0xfffe, 0xffff,
|
||||
0x0000, 0x0000, 0x0000, 0xfffe,
|
||||
0x4000, 0x0000, 0x0000, 0x0000,
|
||||
0xb032, 0x3e00, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0006,
|
||||
0x0001, 0x0000, 0x574d, 0x4c13,
|
||||
};
|
||||
|
||||
#define HPL_MIXER 0
|
||||
#define HPR_MIXER 1
|
||||
|
||||
|
|
@ -220,18 +193,15 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
|
|||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
|
||||
u16 status, rate;
|
||||
|
||||
if (WARN_ON(event != SND_SOC_DAPM_PRE_PMD))
|
||||
return -EINVAL;
|
||||
|
||||
/* Gracefully shut down the voice interface. */
|
||||
status = ac97_read(codec, AC97_EXTENDED_MID) | 0x1000;
|
||||
rate = ac97_read(codec, AC97_HANDSET_RATE) & 0xF0FF;
|
||||
ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0200);
|
||||
snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, 0x0200);
|
||||
schedule_timeout_interruptible(msecs_to_jiffies(1));
|
||||
ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0F00);
|
||||
ac97_write(codec, AC97_EXTENDED_MID, status);
|
||||
snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, 0x0f00);
|
||||
snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x1000, 0x1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -674,40 +644,98 @@ static const struct snd_soc_dapm_route wm9713_audio_map[] = {
|
|||
{"Capture Mono Mux", "Right", "Right Capture Source"},
|
||||
};
|
||||
|
||||
static unsigned int ac97_read(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
static bool wm9713_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
|
||||
u16 *cache = codec->reg_cache;
|
||||
|
||||
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
|
||||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
|
||||
reg == AC97_CD)
|
||||
return soc_ac97_ops->read(wm9713->ac97, reg);
|
||||
else {
|
||||
reg = reg >> 1;
|
||||
|
||||
if (reg >= (ARRAY_SIZE(wm9713_reg)))
|
||||
return -EIO;
|
||||
|
||||
return cache[reg];
|
||||
switch (reg) {
|
||||
case AC97_RESET ... AC97_PCM_SURR_DAC_RATE:
|
||||
case AC97_PCM_LR_ADC_RATE:
|
||||
case AC97_CENTER_LFE_MASTER:
|
||||
case AC97_SPDIF ... AC97_LINE1_LEVEL:
|
||||
case AC97_GPIO_CFG ... 0x5c:
|
||||
case AC97_CODEC_CLASS_REV ... AC97_PCI_SID:
|
||||
case 0x74 ... AC97_VENDOR_ID2:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
|
||||
unsigned int val)
|
||||
static bool wm9713_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
u16 *cache = codec->reg_cache;
|
||||
soc_ac97_ops->write(wm9713->ac97, reg, val);
|
||||
reg = reg >> 1;
|
||||
if (reg < (ARRAY_SIZE(wm9713_reg)))
|
||||
cache[reg] = val;
|
||||
|
||||
return 0;
|
||||
switch (reg) {
|
||||
case AC97_VENDOR_ID1:
|
||||
case AC97_VENDOR_ID2:
|
||||
return false;
|
||||
default:
|
||||
return wm9713_readable_reg(dev, reg);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct reg_default wm9713_reg_defaults[] = {
|
||||
{ 0x02, 0x8080 }, /* Speaker Output Volume */
|
||||
{ 0x04, 0x8080 }, /* Headphone Output Volume */
|
||||
{ 0x06, 0x8080 }, /* Out3/OUT4 Volume */
|
||||
{ 0x08, 0xc880 }, /* Mono Volume */
|
||||
{ 0x0a, 0xe808 }, /* LINEIN Volume */
|
||||
{ 0x0c, 0xe808 }, /* DAC PGA Volume */
|
||||
{ 0x0e, 0x0808 }, /* MIC PGA Volume */
|
||||
{ 0x10, 0x00da }, /* MIC Routing Control */
|
||||
{ 0x12, 0x8000 }, /* Record PGA Volume */
|
||||
{ 0x14, 0xd600 }, /* Record Routing */
|
||||
{ 0x16, 0xaaa0 }, /* PCBEEP Volume */
|
||||
{ 0x18, 0xaaa0 }, /* VxDAC Volume */
|
||||
{ 0x1a, 0xaaa0 }, /* AUXDAC Volume */
|
||||
{ 0x1c, 0x0000 }, /* Output PGA Mux */
|
||||
{ 0x1e, 0x0000 }, /* DAC 3D control */
|
||||
{ 0x20, 0x0f0f }, /* DAC Tone Control*/
|
||||
{ 0x22, 0x0040 }, /* MIC Input Select & Bias */
|
||||
{ 0x24, 0x0000 }, /* Output Volume Mapping & Jack */
|
||||
{ 0x26, 0x7f00 }, /* Powerdown Ctrl/Stat*/
|
||||
{ 0x28, 0x0405 }, /* Extended Audio ID */
|
||||
{ 0x2a, 0x0410 }, /* Extended Audio Start/Ctrl */
|
||||
{ 0x2c, 0xbb80 }, /* Audio DACs Sample Rate */
|
||||
{ 0x2e, 0xbb80 }, /* AUXDAC Sample Rate */
|
||||
{ 0x32, 0xbb80 }, /* Audio ADCs Sample Rate */
|
||||
{ 0x36, 0x4523 }, /* PCM codec control */
|
||||
{ 0x3a, 0x2000 }, /* SPDIF control */
|
||||
{ 0x3c, 0xfdff }, /* Powerdown 1 */
|
||||
{ 0x3e, 0xffff }, /* Powerdown 2 */
|
||||
{ 0x40, 0x0000 }, /* General Purpose */
|
||||
{ 0x42, 0x0000 }, /* Fast Power-Up Control */
|
||||
{ 0x44, 0x0080 }, /* MCLK/PLL Control */
|
||||
{ 0x46, 0x0000 }, /* MCLK/PLL Control */
|
||||
{ 0x4c, 0xfffe }, /* GPIO Pin Configuration */
|
||||
{ 0x4e, 0xffff }, /* GPIO Pin Polarity / Type */
|
||||
{ 0x50, 0x0000 }, /* GPIO Pin Sticky */
|
||||
{ 0x52, 0x0000 }, /* GPIO Pin Wake-Up */
|
||||
/* GPIO Pin Status */
|
||||
{ 0x56, 0xfffe }, /* GPIO Pin Sharing */
|
||||
{ 0x58, 0x4000 }, /* GPIO PullUp/PullDown */
|
||||
{ 0x5a, 0x0000 }, /* Additional Functions 1 */
|
||||
{ 0x5c, 0x0000 }, /* Additional Functions 2 */
|
||||
{ 0x60, 0xb032 }, /* ALC Control */
|
||||
{ 0x62, 0x3e00 }, /* ALC / Noise Gate Control */
|
||||
{ 0x64, 0x0000 }, /* AUXDAC input control */
|
||||
{ 0x74, 0x0000 }, /* Digitiser Reg 1 */
|
||||
{ 0x76, 0x0006 }, /* Digitiser Reg 2 */
|
||||
{ 0x78, 0x0001 }, /* Digitiser Reg 3 */
|
||||
{ 0x7a, 0x0000 }, /* Digitiser Read Back */
|
||||
};
|
||||
|
||||
static const struct regmap_config wm9713_regmap_config = {
|
||||
.reg_bits = 16,
|
||||
.reg_stride = 2,
|
||||
.val_bits = 16,
|
||||
.max_register = 0x7e,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
|
||||
.reg_defaults = wm9713_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(wm9713_reg_defaults),
|
||||
.volatile_reg = regmap_ac97_default_volatile,
|
||||
.readable_reg = wm9713_readable_reg,
|
||||
.writeable_reg = wm9713_writeable_reg,
|
||||
};
|
||||
|
||||
/* PLL divisors */
|
||||
struct _pll_div {
|
||||
u32 divsel:1;
|
||||
|
|
@ -793,10 +821,8 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
|
|||
/* turn PLL off ? */
|
||||
if (freq_in == 0) {
|
||||
/* disable PLL power and select ext source */
|
||||
reg = ac97_read(codec, AC97_HANDSET_RATE);
|
||||
ac97_write(codec, AC97_HANDSET_RATE, reg | 0x0080);
|
||||
reg = ac97_read(codec, AC97_EXTENDED_MID);
|
||||
ac97_write(codec, AC97_EXTENDED_MID, reg | 0x0200);
|
||||
snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0080, 0x0080);
|
||||
snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x0200, 0x0200);
|
||||
wm9713->pll_in = 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -806,7 +832,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
|
|||
if (pll_div.k == 0) {
|
||||
reg = (pll_div.n << 12) | (pll_div.lf << 11) |
|
||||
(pll_div.divsel << 9) | (pll_div.divctl << 8);
|
||||
ac97_write(codec, AC97_LINE1_LEVEL, reg);
|
||||
snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
|
||||
} else {
|
||||
/* write the fractional k to the reg 0x46 pages */
|
||||
reg2 = (pll_div.n << 12) | (pll_div.lf << 11) | (1 << 10) |
|
||||
|
|
@ -814,33 +840,31 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
|
|||
|
||||
/* K [21:20] */
|
||||
reg = reg2 | (0x5 << 4) | (pll_div.k >> 20);
|
||||
ac97_write(codec, AC97_LINE1_LEVEL, reg);
|
||||
snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
|
||||
|
||||
/* K [19:16] */
|
||||
reg = reg2 | (0x4 << 4) | ((pll_div.k >> 16) & 0xf);
|
||||
ac97_write(codec, AC97_LINE1_LEVEL, reg);
|
||||
snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
|
||||
|
||||
/* K [15:12] */
|
||||
reg = reg2 | (0x3 << 4) | ((pll_div.k >> 12) & 0xf);
|
||||
ac97_write(codec, AC97_LINE1_LEVEL, reg);
|
||||
snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
|
||||
|
||||
/* K [11:8] */
|
||||
reg = reg2 | (0x2 << 4) | ((pll_div.k >> 8) & 0xf);
|
||||
ac97_write(codec, AC97_LINE1_LEVEL, reg);
|
||||
snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
|
||||
|
||||
/* K [7:4] */
|
||||
reg = reg2 | (0x1 << 4) | ((pll_div.k >> 4) & 0xf);
|
||||
ac97_write(codec, AC97_LINE1_LEVEL, reg);
|
||||
snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
|
||||
|
||||
reg = reg2 | (0x0 << 4) | (pll_div.k & 0xf); /* K [3:0] */
|
||||
ac97_write(codec, AC97_LINE1_LEVEL, reg);
|
||||
snd_soc_write(codec, AC97_LINE1_LEVEL, reg);
|
||||
}
|
||||
|
||||
/* turn PLL on and select as source */
|
||||
reg = ac97_read(codec, AC97_EXTENDED_MID);
|
||||
ac97_write(codec, AC97_EXTENDED_MID, reg & 0xfdff);
|
||||
reg = ac97_read(codec, AC97_HANDSET_RATE);
|
||||
ac97_write(codec, AC97_HANDSET_RATE, reg & 0xff7f);
|
||||
snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x0200, 0x0000);
|
||||
snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0080, 0x0000);
|
||||
wm9713->pll_in = freq_in;
|
||||
|
||||
/* wait 10ms AC97 link frames for the link to stabilise */
|
||||
|
|
@ -863,10 +887,10 @@ static int wm9713_set_dai_tristate(struct snd_soc_dai *codec_dai,
|
|||
int tristate)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0x9fff;
|
||||
|
||||
if (tristate)
|
||||
ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
|
||||
snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER,
|
||||
0x6000, 0x0000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -879,36 +903,30 @@ static int wm9713_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
|
|||
int div_id, int div)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
u16 reg;
|
||||
|
||||
switch (div_id) {
|
||||
case WM9713_PCMCLK_DIV:
|
||||
reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xf0ff;
|
||||
ac97_write(codec, AC97_HANDSET_RATE, reg | div);
|
||||
snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, div);
|
||||
break;
|
||||
case WM9713_CLKA_MULT:
|
||||
reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffd;
|
||||
ac97_write(codec, AC97_HANDSET_RATE, reg | div);
|
||||
snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0002, div);
|
||||
break;
|
||||
case WM9713_CLKB_MULT:
|
||||
reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffb;
|
||||
ac97_write(codec, AC97_HANDSET_RATE, reg | div);
|
||||
snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0004, div);
|
||||
break;
|
||||
case WM9713_HIFI_DIV:
|
||||
reg = ac97_read(codec, AC97_HANDSET_RATE) & 0x8fff;
|
||||
ac97_write(codec, AC97_HANDSET_RATE, reg | div);
|
||||
snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x7000, div);
|
||||
break;
|
||||
case WM9713_PCMBCLK_DIV:
|
||||
reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xf1ff;
|
||||
ac97_write(codec, AC97_CENTER_LFE_MASTER, reg | div);
|
||||
snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, 0x0e00, div);
|
||||
break;
|
||||
case WM9713_PCMCLK_PLL_DIV:
|
||||
reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80;
|
||||
ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x60 | div);
|
||||
snd_soc_update_bits(codec, AC97_LINE1_LEVEL,
|
||||
0x007f, div | 0x60);
|
||||
break;
|
||||
case WM9713_HIFI_PLL_DIV:
|
||||
reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80;
|
||||
ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x70 | div);
|
||||
snd_soc_update_bits(codec, AC97_LINE1_LEVEL,
|
||||
0x007f, div | 0x70);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
|
@ -921,7 +939,7 @@ static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
u16 gpio = ac97_read(codec, AC97_GPIO_CFG) & 0xffc5;
|
||||
u16 gpio = snd_soc_read(codec, AC97_GPIO_CFG) & 0xffc5;
|
||||
u16 reg = 0x8000;
|
||||
|
||||
/* clock masters */
|
||||
|
|
@ -974,8 +992,8 @@ static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai,
|
|||
break;
|
||||
}
|
||||
|
||||
ac97_write(codec, AC97_GPIO_CFG, gpio);
|
||||
ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
|
||||
snd_soc_write(codec, AC97_GPIO_CFG, gpio);
|
||||
snd_soc_write(codec, AC97_CENTER_LFE_MASTER, reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -984,24 +1002,24 @@ static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3;
|
||||
|
||||
/* enable PCM interface in master mode */
|
||||
switch (params_width(params)) {
|
||||
case 16:
|
||||
break;
|
||||
case 20:
|
||||
reg |= 0x0004;
|
||||
snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER,
|
||||
0x000c, 0x0004);
|
||||
break;
|
||||
case 24:
|
||||
reg |= 0x0008;
|
||||
snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER,
|
||||
0x000c, 0x0008);
|
||||
break;
|
||||
case 32:
|
||||
reg |= 0x000c;
|
||||
snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER,
|
||||
0x000c, 0x000c);
|
||||
break;
|
||||
}
|
||||
|
||||
/* enable PCM interface in master mode */
|
||||
ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1011,17 +1029,15 @@ static int ac97_hifi_prepare(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int reg;
|
||||
u16 vra;
|
||||
|
||||
vra = ac97_read(codec, AC97_EXTENDED_STATUS);
|
||||
ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
|
||||
snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x0001, 0x0001);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
reg = AC97_PCM_FRONT_DAC_RATE;
|
||||
else
|
||||
reg = AC97_PCM_LR_ADC_RATE;
|
||||
|
||||
return ac97_write(codec, reg, runtime->rate);
|
||||
return snd_soc_write(codec, reg, runtime->rate);
|
||||
}
|
||||
|
||||
static int ac97_aux_prepare(struct snd_pcm_substream *substream,
|
||||
|
|
@ -1029,17 +1045,14 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream,
|
|||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
u16 vra, xsle;
|
||||
|
||||
vra = ac97_read(codec, AC97_EXTENDED_STATUS);
|
||||
ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
|
||||
xsle = ac97_read(codec, AC97_PCI_SID);
|
||||
ac97_write(codec, AC97_PCI_SID, xsle | 0x8000);
|
||||
snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x0001, 0x0001);
|
||||
snd_soc_update_bits(codec, AC97_PCI_SID, 0x8000, 0x8000);
|
||||
|
||||
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
|
||||
return -ENODEV;
|
||||
|
||||
return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
|
||||
return snd_soc_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
|
||||
}
|
||||
|
||||
#define WM9713_RATES (SNDRV_PCM_RATE_8000 | \
|
||||
|
|
@ -1128,27 +1141,23 @@ static struct snd_soc_dai_driver wm9713_dai[] = {
|
|||
static int wm9713_set_bias_level(struct snd_soc_codec *codec,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
u16 reg;
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
/* enable thermal shutdown */
|
||||
reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff;
|
||||
ac97_write(codec, AC97_EXTENDED_MID, reg);
|
||||
snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0xe400, 0x0000);
|
||||
break;
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
break;
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
/* enable master bias and vmid */
|
||||
reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff;
|
||||
ac97_write(codec, AC97_EXTENDED_MID, reg);
|
||||
ac97_write(codec, AC97_POWERDOWN, 0x0000);
|
||||
snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0xc400, 0x0000);
|
||||
snd_soc_write(codec, AC97_POWERDOWN, 0x0000);
|
||||
break;
|
||||
case SND_SOC_BIAS_OFF:
|
||||
/* disable everything including AC link */
|
||||
ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
|
||||
ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
|
||||
ac97_write(codec, AC97_POWERDOWN, 0xffff);
|
||||
snd_soc_write(codec, AC97_EXTENDED_MID, 0xffff);
|
||||
snd_soc_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
|
||||
snd_soc_write(codec, AC97_POWERDOWN, 0xffff);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -1156,16 +1165,14 @@ static int wm9713_set_bias_level(struct snd_soc_codec *codec,
|
|||
|
||||
static int wm9713_soc_suspend(struct snd_soc_codec *codec)
|
||||
{
|
||||
u16 reg;
|
||||
|
||||
/* Disable everything except touchpanel - that will be handled
|
||||
* by the touch driver and left disabled if touch is not in
|
||||
* use. */
|
||||
reg = ac97_read(codec, AC97_EXTENDED_MID);
|
||||
ac97_write(codec, AC97_EXTENDED_MID, reg | 0x7fff);
|
||||
ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
|
||||
ac97_write(codec, AC97_POWERDOWN, 0x6f00);
|
||||
ac97_write(codec, AC97_POWERDOWN, 0xffff);
|
||||
snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x7fff,
|
||||
0x7fff);
|
||||
snd_soc_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
|
||||
snd_soc_write(codec, AC97_POWERDOWN, 0x6f00);
|
||||
snd_soc_write(codec, AC97_POWERDOWN, 0xffff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1173,8 +1180,7 @@ static int wm9713_soc_suspend(struct snd_soc_codec *codec)
|
|||
static int wm9713_soc_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
|
||||
int i, ret;
|
||||
u16 *cache = codec->reg_cache;
|
||||
int ret;
|
||||
|
||||
ret = snd_ac97_reset(wm9713->ac97, true, WM9713_VENDOR_ID,
|
||||
WM9713_VENDOR_ID_MASK);
|
||||
|
|
@ -1189,12 +1195,8 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
|
|||
|
||||
/* only synchronise the codec if warm reset failed */
|
||||
if (ret == 0) {
|
||||
for (i = 2; i < ARRAY_SIZE(wm9713_reg) << 1; i += 2) {
|
||||
if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
|
||||
i == AC97_EXTENDED_MSTATUS || i > 0x66)
|
||||
continue;
|
||||
soc_ac97_ops->write(wm9713->ac97, i, cache[i>>1]);
|
||||
}
|
||||
regcache_mark_dirty(codec->component.regmap);
|
||||
snd_soc_cache_sync(codec);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -1203,16 +1205,23 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
|
|||
static int wm9713_soc_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
|
||||
int reg;
|
||||
struct regmap *regmap;
|
||||
|
||||
wm9713->ac97 = snd_soc_new_ac97_codec(codec, WM9713_VENDOR_ID,
|
||||
WM9713_VENDOR_ID_MASK);
|
||||
if (IS_ERR(wm9713->ac97))
|
||||
return PTR_ERR(wm9713->ac97);
|
||||
|
||||
regmap = devm_regmap_init_ac97(wm9713->ac97, &wm9713_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
snd_soc_free_ac97_codec(wm9713->ac97);
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
snd_soc_codec_init_regmap(codec, regmap);
|
||||
|
||||
/* unmute the adc - move to kcontrol */
|
||||
reg = ac97_read(codec, AC97_CD) & 0x7fff;
|
||||
ac97_write(codec, AC97_CD, reg);
|
||||
snd_soc_update_bits(codec, AC97_CD, 0x7fff, 0x0000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1221,6 +1230,7 @@ static int wm9713_soc_remove(struct snd_soc_codec *codec)
|
|||
{
|
||||
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
snd_soc_codec_exit_regmap(codec);
|
||||
snd_soc_free_ac97_codec(wm9713->ac97);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1230,13 +1240,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
|
|||
.remove = wm9713_soc_remove,
|
||||
.suspend = wm9713_soc_suspend,
|
||||
.resume = wm9713_soc_resume,
|
||||
.read = ac97_read,
|
||||
.write = ac97_write,
|
||||
.set_bias_level = wm9713_set_bias_level,
|
||||
.reg_cache_size = ARRAY_SIZE(wm9713_reg),
|
||||
.reg_word_size = sizeof(u16),
|
||||
.reg_cache_step = 2,
|
||||
.reg_cache_default = wm9713_reg,
|
||||
|
||||
.controls = wm9713_snd_ac97_controls,
|
||||
.num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls),
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/compress_driver.h>
|
||||
|
||||
#include "wmfw.h"
|
||||
|
||||
|
|
@ -30,6 +31,9 @@ struct wm_adsp_alg_region {
|
|||
unsigned int base;
|
||||
};
|
||||
|
||||
struct wm_adsp_compr;
|
||||
struct wm_adsp_compr_buf;
|
||||
|
||||
struct wm_adsp {
|
||||
const char *part;
|
||||
int num;
|
||||
|
|
@ -45,8 +49,8 @@ struct wm_adsp {
|
|||
|
||||
struct list_head alg_regions;
|
||||
|
||||
int fw_id;
|
||||
int fw_id_version;
|
||||
unsigned int fw_id;
|
||||
unsigned int fw_id_version;
|
||||
|
||||
const struct wm_adsp_region *mem;
|
||||
int num_mems;
|
||||
|
|
@ -59,9 +63,13 @@ struct wm_adsp {
|
|||
|
||||
struct work_struct boot_work;
|
||||
|
||||
struct wm_adsp_compr *compr;
|
||||
struct wm_adsp_compr_buf *buffer;
|
||||
|
||||
struct mutex pwr_lock;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs_root;
|
||||
struct mutex debugfs_lock;
|
||||
char *wmfw_file_name;
|
||||
char *bin_file_name;
|
||||
#endif
|
||||
|
|
@ -96,4 +104,13 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
|
|||
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event);
|
||||
|
||||
extern int wm_adsp_compr_open(struct wm_adsp *dsp,
|
||||
struct snd_compr_stream *stream);
|
||||
extern int wm_adsp_compr_free(struct snd_compr_stream *stream);
|
||||
extern int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
|
||||
struct snd_compr_params *params);
|
||||
extern int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
|
||||
struct snd_compr_caps *caps);
|
||||
extern int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <sound/designware_i2s.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
|
@ -93,7 +94,12 @@ struct dw_i2s_dev {
|
|||
struct clk *clk;
|
||||
int active;
|
||||
unsigned int capability;
|
||||
unsigned int quirks;
|
||||
unsigned int i2s_reg_comp1;
|
||||
unsigned int i2s_reg_comp2;
|
||||
struct device *dev;
|
||||
u32 ccr;
|
||||
u32 xfer_resolution;
|
||||
|
||||
/* data related to DMA transfers b/w i2s and DMAC */
|
||||
union dw_i2s_snd_dma_data play_dma_data;
|
||||
|
|
@ -213,31 +219,58 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
|
||||
{
|
||||
u32 ch_reg, irq;
|
||||
struct i2s_clk_config_data *config = &dev->config;
|
||||
|
||||
|
||||
i2s_disable_channels(dev, stream);
|
||||
|
||||
for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) {
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
i2s_write_reg(dev->i2s_base, TCR(ch_reg),
|
||||
dev->xfer_resolution);
|
||||
i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02);
|
||||
irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
|
||||
i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
|
||||
i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
|
||||
} else {
|
||||
i2s_write_reg(dev->i2s_base, RCR(ch_reg),
|
||||
dev->xfer_resolution);
|
||||
i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07);
|
||||
irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
|
||||
i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
|
||||
i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
struct i2s_clk_config_data *config = &dev->config;
|
||||
u32 ccr, xfer_resolution, ch_reg, irq;
|
||||
int ret;
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
config->data_width = 16;
|
||||
ccr = 0x00;
|
||||
xfer_resolution = 0x02;
|
||||
dev->ccr = 0x00;
|
||||
dev->xfer_resolution = 0x02;
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
config->data_width = 24;
|
||||
ccr = 0x08;
|
||||
xfer_resolution = 0x04;
|
||||
dev->ccr = 0x08;
|
||||
dev->xfer_resolution = 0x04;
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
config->data_width = 32;
|
||||
ccr = 0x10;
|
||||
xfer_resolution = 0x05;
|
||||
dev->ccr = 0x10;
|
||||
dev->xfer_resolution = 0x05;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -258,27 +291,9 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
i2s_disable_channels(dev, substream->stream);
|
||||
dw_i2s_config(dev, substream->stream);
|
||||
|
||||
for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) {
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
i2s_write_reg(dev->i2s_base, TCR(ch_reg),
|
||||
xfer_resolution);
|
||||
i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02);
|
||||
irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
|
||||
i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
|
||||
i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
|
||||
} else {
|
||||
i2s_write_reg(dev->i2s_base, RCR(ch_reg),
|
||||
xfer_resolution);
|
||||
i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07);
|
||||
irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
|
||||
i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
|
||||
i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
|
||||
}
|
||||
}
|
||||
|
||||
i2s_write_reg(dev->i2s_base, CCR, ccr);
|
||||
i2s_write_reg(dev->i2s_base, CCR, dev->ccr);
|
||||
|
||||
config->sample_rate = params_rate(params);
|
||||
|
||||
|
|
@ -394,6 +409,23 @@ static const struct snd_soc_component_driver dw_i2s_component = {
|
|||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int dw_i2s_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
|
||||
|
||||
if (dw_dev->capability & DW_I2S_MASTER)
|
||||
clk_disable(dw_dev->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_i2s_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
|
||||
|
||||
if (dw_dev->capability & DW_I2S_MASTER)
|
||||
clk_enable(dw_dev->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_i2s_suspend(struct snd_soc_dai *dai)
|
||||
{
|
||||
|
|
@ -410,6 +442,11 @@ static int dw_i2s_resume(struct snd_soc_dai *dai)
|
|||
|
||||
if (dev->capability & DW_I2S_MASTER)
|
||||
clk_enable(dev->clk);
|
||||
|
||||
if (dai->playback_active)
|
||||
dw_i2s_config(dev, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
if (dai->capture_active)
|
||||
dw_i2s_config(dev, SNDRV_PCM_STREAM_CAPTURE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -459,8 +496,8 @@ static int dw_configure_dai(struct dw_i2s_dev *dev,
|
|||
* Read component parameter registers to extract
|
||||
* the I2S block's configuration.
|
||||
*/
|
||||
u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1);
|
||||
u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2);
|
||||
u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
|
||||
u32 comp2 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp2);
|
||||
u32 idx;
|
||||
|
||||
if (COMP1_TX_ENABLED(comp1)) {
|
||||
|
|
@ -503,7 +540,7 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev,
|
|||
struct resource *res,
|
||||
const struct i2s_platform_data *pdata)
|
||||
{
|
||||
u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1);
|
||||
u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
|
||||
u32 idx = COMP1_APB_DATA_WIDTH(comp1);
|
||||
int ret;
|
||||
|
||||
|
|
@ -607,6 +644,14 @@ static int dw_i2s_probe(struct platform_device *pdev)
|
|||
if (pdata) {
|
||||
dev->capability = pdata->cap;
|
||||
clk_id = NULL;
|
||||
dev->quirks = pdata->quirks;
|
||||
if (dev->quirks & DW_I2S_QUIRK_COMP_REG_OFFSET) {
|
||||
dev->i2s_reg_comp1 = pdata->i2s_reg_comp1;
|
||||
dev->i2s_reg_comp2 = pdata->i2s_reg_comp2;
|
||||
} else {
|
||||
dev->i2s_reg_comp1 = I2S_COMP_PARAM_1;
|
||||
dev->i2s_reg_comp2 = I2S_COMP_PARAM_2;
|
||||
}
|
||||
ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata);
|
||||
} else {
|
||||
clk_id = "i2sclk";
|
||||
|
|
@ -649,7 +694,7 @@ static int dw_i2s_probe(struct platform_device *pdev)
|
|||
goto err_clk_disable;
|
||||
}
|
||||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
return 0;
|
||||
|
||||
err_clk_disable:
|
||||
|
|
@ -665,6 +710,7 @@ static int dw_i2s_remove(struct platform_device *pdev)
|
|||
if (dev->capability & DW_I2S_MASTER)
|
||||
clk_disable_unprepare(dev->clk);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -677,12 +723,17 @@ static const struct of_device_id dw_i2s_of_match[] = {
|
|||
MODULE_DEVICE_TABLE(of, dw_i2s_of_match);
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops dwc_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(dw_i2s_runtime_suspend, dw_i2s_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver dw_i2s_driver = {
|
||||
.probe = dw_i2s_probe,
|
||||
.remove = dw_i2s_remove,
|
||||
.driver = {
|
||||
.name = "designware-i2s",
|
||||
.of_match_table = of_match_ptr(dw_i2s_of_match),
|
||||
.pm = &dwc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -107,6 +107,13 @@ static const struct snd_soc_dapm_route audio_map[] = {
|
|||
{"CPU-Capture", NULL, "Capture"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route audio_map_ac97[] = {
|
||||
{"AC97 Playback", NULL, "ASRC-Playback"},
|
||||
{"Playback", NULL, "AC97 Playback"},
|
||||
{"ASRC-Capture", NULL, "AC97 Capture"},
|
||||
{"AC97 Capture", NULL, "Capture"},
|
||||
};
|
||||
|
||||
/* Add all possible widgets into here without being redundant */
|
||||
static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_LINE("Line Out Jack", NULL),
|
||||
|
|
@ -417,14 +424,16 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
|
|||
static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
|
||||
{
|
||||
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
|
||||
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
|
||||
struct snd_soc_pcm_runtime *rtd = list_first_entry(
|
||||
&card->rtd_list, struct snd_soc_pcm_runtime, list);
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct codec_priv *codec_priv = &priv->codec_priv;
|
||||
struct device *dev = card->dev;
|
||||
int ret;
|
||||
|
||||
if (fsl_asoc_card_is_ac97(priv)) {
|
||||
#if IS_ENABLED(CONFIG_SND_AC97_CODEC)
|
||||
struct snd_soc_codec *codec = card->rtd[0].codec;
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
/*
|
||||
|
|
@ -577,7 +586,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
|
|||
priv->card.dev = &pdev->dev;
|
||||
priv->card.name = priv->name;
|
||||
priv->card.dai_link = priv->dai_link;
|
||||
priv->card.dapm_routes = audio_map;
|
||||
priv->card.dapm_routes = fsl_asoc_card_is_ac97(priv) ?
|
||||
audio_map_ac97 : audio_map;
|
||||
priv->card.late_probe = fsl_asoc_card_late_probe;
|
||||
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map);
|
||||
priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets;
|
||||
|
|
|
|||
|
|
@ -31,21 +31,21 @@
|
|||
dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
|
||||
|
||||
/* Sample rates are aligned with that defined in pcm.h file */
|
||||
static const u8 process_option[][8][2] = {
|
||||
/* 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */
|
||||
{{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */
|
||||
{{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */
|
||||
{{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */
|
||||
{{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */
|
||||
{{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */
|
||||
{{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */
|
||||
{{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */
|
||||
{{0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */
|
||||
{{1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */
|
||||
{{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */
|
||||
{{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */
|
||||
{{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */
|
||||
{{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */
|
||||
static const u8 process_option[][12][2] = {
|
||||
/* 8kHz 11.025kHz 16kHz 22.05kHz 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */
|
||||
{{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */
|
||||
{{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */
|
||||
{{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */
|
||||
{{1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */
|
||||
{{1, 2}, {1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */
|
||||
{{1, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */
|
||||
{{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */
|
||||
{{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */
|
||||
{{2, 2}, {2, 2}, {2, 2}, {2, 1}, {1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */
|
||||
{{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */
|
||||
{{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */
|
||||
{{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */
|
||||
{{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */
|
||||
};
|
||||
|
||||
/* Corresponding to process_option */
|
||||
|
|
@ -55,7 +55,7 @@ static int supported_input_rate[] = {
|
|||
};
|
||||
|
||||
static int supported_asrc_rate[] = {
|
||||
32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000,
|
||||
8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -286,6 +286,13 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((outrate > 8000 && outrate < 30000) &&
|
||||
(outrate/inrate > 24 || inrate/outrate > 8)) {
|
||||
pair_err("exceed supported ratio range [1/24, 8] for \
|
||||
inrate/outrate: %d/%d\n", inrate, outrate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Validate input and output clock sources */
|
||||
clk_index[IN] = clk_map[IN][config->inclk];
|
||||
clk_index[OUT] = clk_map[OUT][config->outclk];
|
||||
|
|
@ -447,7 +454,7 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
|
||||
int width = snd_pcm_format_width(params_format(params));
|
||||
int width = params_width(params);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
unsigned int channels = params_channels(params);
|
||||
|
|
@ -859,6 +866,10 @@ static int fsl_asrc_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(asrc_priv->ipg_clk);
|
||||
}
|
||||
|
||||
asrc_priv->spba_clk = devm_clk_get(&pdev->dev, "spba");
|
||||
if (IS_ERR(asrc_priv->spba_clk))
|
||||
dev_warn(&pdev->dev, "failed to get spba clock\n");
|
||||
|
||||
for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
|
||||
sprintf(tmp, "asrck_%x", i);
|
||||
asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
|
||||
|
|
@ -939,6 +950,11 @@ static int fsl_asrc_runtime_resume(struct device *dev)
|
|||
ret = clk_prepare_enable(asrc_priv->ipg_clk);
|
||||
if (ret)
|
||||
goto disable_mem_clk;
|
||||
if (!IS_ERR(asrc_priv->spba_clk)) {
|
||||
ret = clk_prepare_enable(asrc_priv->spba_clk);
|
||||
if (ret)
|
||||
goto disable_ipg_clk;
|
||||
}
|
||||
for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
|
||||
ret = clk_prepare_enable(asrc_priv->asrck_clk[i]);
|
||||
if (ret)
|
||||
|
|
@ -950,6 +966,9 @@ static int fsl_asrc_runtime_resume(struct device *dev)
|
|||
disable_asrck_clk:
|
||||
for (i--; i >= 0; i--)
|
||||
clk_disable_unprepare(asrc_priv->asrck_clk[i]);
|
||||
if (!IS_ERR(asrc_priv->spba_clk))
|
||||
clk_disable_unprepare(asrc_priv->spba_clk);
|
||||
disable_ipg_clk:
|
||||
clk_disable_unprepare(asrc_priv->ipg_clk);
|
||||
disable_mem_clk:
|
||||
clk_disable_unprepare(asrc_priv->mem_clk);
|
||||
|
|
@ -963,6 +982,8 @@ static int fsl_asrc_runtime_suspend(struct device *dev)
|
|||
|
||||
for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
|
||||
clk_disable_unprepare(asrc_priv->asrck_clk[i]);
|
||||
if (!IS_ERR(asrc_priv->spba_clk))
|
||||
clk_disable_unprepare(asrc_priv->spba_clk);
|
||||
clk_disable_unprepare(asrc_priv->ipg_clk);
|
||||
clk_disable_unprepare(asrc_priv->mem_clk);
|
||||
|
||||
|
|
|
|||
|
|
@ -426,6 +426,7 @@ struct fsl_asrc_pair {
|
|||
* @paddr: physical address to the base address of registers
|
||||
* @mem_clk: clock source to access register
|
||||
* @ipg_clk: clock source to drive peripheral
|
||||
* @spba_clk: SPBA clock (optional, depending on SoC design)
|
||||
* @asrck_clk: clock sources to driver ASRC internal logic
|
||||
* @lock: spin lock for resource protection
|
||||
* @pair: pair pointers
|
||||
|
|
@ -442,6 +443,7 @@ struct fsl_asrc {
|
|||
unsigned long paddr;
|
||||
struct clk *mem_clk;
|
||||
struct clk *ipg_clk;
|
||||
struct clk *spba_clk;
|
||||
struct clk *asrck_clk[ASRC_CLK_MAX_NUM];
|
||||
spinlock_t lock;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
* @coreclk: clock source to access register
|
||||
* @extalclk: esai clock source to derive HCK, SCK and FS
|
||||
* @fsysclk: system clock source to derive HCK, SCK and FS
|
||||
* @spbaclk: SPBA clock (optional, depending on SoC design)
|
||||
* @fifo_depth: depth of tx/rx FIFO
|
||||
* @slot_width: width of each DAI slot
|
||||
* @slots: number of slots
|
||||
|
|
@ -54,6 +55,7 @@ struct fsl_esai {
|
|||
struct clk *coreclk;
|
||||
struct clk *extalclk;
|
||||
struct clk *fsysclk;
|
||||
struct clk *spbaclk;
|
||||
u32 fifo_depth;
|
||||
u32 slot_width;
|
||||
u32 slots;
|
||||
|
|
@ -469,6 +471,11 @@ static int fsl_esai_startup(struct snd_pcm_substream *substream,
|
|||
ret = clk_prepare_enable(esai_priv->coreclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!IS_ERR(esai_priv->spbaclk)) {
|
||||
ret = clk_prepare_enable(esai_priv->spbaclk);
|
||||
if (ret)
|
||||
goto err_spbaclk;
|
||||
}
|
||||
if (!IS_ERR(esai_priv->extalclk)) {
|
||||
ret = clk_prepare_enable(esai_priv->extalclk);
|
||||
if (ret)
|
||||
|
|
@ -499,6 +506,9 @@ err_fsysclk:
|
|||
if (!IS_ERR(esai_priv->extalclk))
|
||||
clk_disable_unprepare(esai_priv->extalclk);
|
||||
err_extalck:
|
||||
if (!IS_ERR(esai_priv->spbaclk))
|
||||
clk_disable_unprepare(esai_priv->spbaclk);
|
||||
err_spbaclk:
|
||||
clk_disable_unprepare(esai_priv->coreclk);
|
||||
|
||||
return ret;
|
||||
|
|
@ -510,7 +520,7 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
|
|||
{
|
||||
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
u32 width = snd_pcm_format_width(params_format(params));
|
||||
u32 width = params_width(params);
|
||||
u32 channels = params_channels(params);
|
||||
u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
|
||||
u32 slot_width = width;
|
||||
|
|
@ -564,6 +574,8 @@ static void fsl_esai_shutdown(struct snd_pcm_substream *substream,
|
|||
clk_disable_unprepare(esai_priv->fsysclk);
|
||||
if (!IS_ERR(esai_priv->extalclk))
|
||||
clk_disable_unprepare(esai_priv->extalclk);
|
||||
if (!IS_ERR(esai_priv->spbaclk))
|
||||
clk_disable_unprepare(esai_priv->spbaclk);
|
||||
clk_disable_unprepare(esai_priv->coreclk);
|
||||
}
|
||||
|
||||
|
|
@ -653,21 +665,28 @@ static const struct snd_soc_component_driver fsl_esai_component = {
|
|||
};
|
||||
|
||||
static const struct reg_default fsl_esai_reg_defaults[] = {
|
||||
{0x8, 0x00000000},
|
||||
{0x10, 0x00000000},
|
||||
{0x18, 0x00000000},
|
||||
{0x98, 0x00000000},
|
||||
{0xd0, 0x00000000},
|
||||
{0xd4, 0x00000000},
|
||||
{0xd8, 0x00000000},
|
||||
{0xdc, 0x00000000},
|
||||
{0xe0, 0x00000000},
|
||||
{0xe4, 0x0000ffff},
|
||||
{0xe8, 0x0000ffff},
|
||||
{0xec, 0x0000ffff},
|
||||
{0xf0, 0x0000ffff},
|
||||
{0xf8, 0x00000000},
|
||||
{0xfc, 0x00000000},
|
||||
{REG_ESAI_ETDR, 0x00000000},
|
||||
{REG_ESAI_ECR, 0x00000000},
|
||||
{REG_ESAI_TFCR, 0x00000000},
|
||||
{REG_ESAI_RFCR, 0x00000000},
|
||||
{REG_ESAI_TX0, 0x00000000},
|
||||
{REG_ESAI_TX1, 0x00000000},
|
||||
{REG_ESAI_TX2, 0x00000000},
|
||||
{REG_ESAI_TX3, 0x00000000},
|
||||
{REG_ESAI_TX4, 0x00000000},
|
||||
{REG_ESAI_TX5, 0x00000000},
|
||||
{REG_ESAI_TSR, 0x00000000},
|
||||
{REG_ESAI_SAICR, 0x00000000},
|
||||
{REG_ESAI_TCR, 0x00000000},
|
||||
{REG_ESAI_TCCR, 0x00000000},
|
||||
{REG_ESAI_RCR, 0x00000000},
|
||||
{REG_ESAI_RCCR, 0x00000000},
|
||||
{REG_ESAI_TSMA, 0x0000ffff},
|
||||
{REG_ESAI_TSMB, 0x0000ffff},
|
||||
{REG_ESAI_RSMA, 0x0000ffff},
|
||||
{REG_ESAI_RSMB, 0x0000ffff},
|
||||
{REG_ESAI_PRRC, 0x00000000},
|
||||
{REG_ESAI_PCRC, 0x00000000},
|
||||
};
|
||||
|
||||
static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
|
||||
|
|
@ -705,17 +724,10 @@ static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
|
|||
static bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case REG_ESAI_ETDR:
|
||||
case REG_ESAI_ERDR:
|
||||
case REG_ESAI_ESR:
|
||||
case REG_ESAI_TFSR:
|
||||
case REG_ESAI_RFSR:
|
||||
case REG_ESAI_TX0:
|
||||
case REG_ESAI_TX1:
|
||||
case REG_ESAI_TX2:
|
||||
case REG_ESAI_TX3:
|
||||
case REG_ESAI_TX4:
|
||||
case REG_ESAI_TX5:
|
||||
case REG_ESAI_RX0:
|
||||
case REG_ESAI_RX1:
|
||||
case REG_ESAI_RX2:
|
||||
|
|
@ -819,6 +831,11 @@ static int fsl_esai_probe(struct platform_device *pdev)
|
|||
dev_warn(&pdev->dev, "failed to get fsys clock: %ld\n",
|
||||
PTR_ERR(esai_priv->fsysclk));
|
||||
|
||||
esai_priv->spbaclk = devm_clk_get(&pdev->dev, "spba");
|
||||
if (IS_ERR(esai_priv->spbaclk))
|
||||
dev_warn(&pdev->dev, "failed to get spba clock: %ld\n",
|
||||
PTR_ERR(esai_priv->spbaclk));
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
|
||||
|
|
|
|||
|
|
@ -126,6 +126,17 @@ out:
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
|
||||
u32 rx_mask, int slots, int slot_width)
|
||||
{
|
||||
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
sai->slots = slots;
|
||||
sai->slot_width = slot_width;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
|
||||
int clk_id, unsigned int freq, int fsl_dir)
|
||||
{
|
||||
|
|
@ -354,13 +365,25 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((tx && sai->synchronous[TX]) || (!tx && !sai->synchronous[RX])) {
|
||||
/*
|
||||
* 1) For Asynchronous mode, we must set RCR2 register for capture, and
|
||||
* set TCR2 register for playback.
|
||||
* 2) For Tx sync with Rx clock, we must set RCR2 register for playback
|
||||
* and capture.
|
||||
* 3) For Rx sync with Tx clock, we must set TCR2 register for playback
|
||||
* and capture.
|
||||
* 4) For Tx and Rx are both Synchronous with another SAI, we just
|
||||
* ignore it.
|
||||
*/
|
||||
if ((sai->synchronous[TX] && !sai->synchronous[RX]) ||
|
||||
(!tx && !sai->synchronous[RX])) {
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
|
||||
FSL_SAI_CR2_MSEL_MASK,
|
||||
FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
|
||||
FSL_SAI_CR2_DIV_MASK, savediv - 1);
|
||||
} else {
|
||||
} else if ((sai->synchronous[RX] && !sai->synchronous[TX]) ||
|
||||
(tx && !sai->synchronous[TX])) {
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_TCR2,
|
||||
FSL_SAI_CR2_MSEL_MASK,
|
||||
FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
|
||||
|
|
@ -381,13 +404,21 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
|
|||
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
unsigned int channels = params_channels(params);
|
||||
u32 word_width = snd_pcm_format_width(params_format(params));
|
||||
u32 word_width = params_width(params);
|
||||
u32 val_cr4 = 0, val_cr5 = 0;
|
||||
u32 slots = (channels == 1) ? 2 : channels;
|
||||
u32 slot_width = word_width;
|
||||
int ret;
|
||||
|
||||
if (sai->slots)
|
||||
slots = sai->slots;
|
||||
|
||||
if (sai->slot_width)
|
||||
slot_width = sai->slot_width;
|
||||
|
||||
if (!sai->is_slave_mode) {
|
||||
ret = fsl_sai_set_bclk(cpu_dai, tx,
|
||||
2 * word_width * params_rate(params));
|
||||
slots * slot_width * params_rate(params));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -399,21 +430,49 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
sai->mclk_streams |= BIT(substream->stream);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!sai->is_dsp_mode)
|
||||
val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
|
||||
val_cr4 |= FSL_SAI_CR4_SYWD(slot_width);
|
||||
|
||||
val_cr5 |= FSL_SAI_CR5_WNW(word_width);
|
||||
val_cr5 |= FSL_SAI_CR5_W0W(word_width);
|
||||
val_cr5 |= FSL_SAI_CR5_WNW(slot_width);
|
||||
val_cr5 |= FSL_SAI_CR5_W0W(slot_width);
|
||||
|
||||
if (sai->is_lsb_first)
|
||||
val_cr5 |= FSL_SAI_CR5_FBT(0);
|
||||
else
|
||||
val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
|
||||
|
||||
val_cr4 |= FSL_SAI_CR4_FRSZ(channels);
|
||||
val_cr4 |= FSL_SAI_CR4_FRSZ(slots);
|
||||
|
||||
/*
|
||||
* For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will
|
||||
* generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4),
|
||||
* RCR5(TCR5) and RMR(TMR) for playback(capture), or there will be sync
|
||||
* error.
|
||||
*/
|
||||
|
||||
if (!sai->is_slave_mode) {
|
||||
if (!sai->synchronous[TX] && sai->synchronous[RX] && !tx) {
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_TCR4,
|
||||
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
|
||||
val_cr4);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_TCR5,
|
||||
FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
|
||||
FSL_SAI_CR5_FBT_MASK, val_cr5);
|
||||
regmap_write(sai->regmap, FSL_SAI_TMR,
|
||||
~0UL - ((1 << channels) - 1));
|
||||
} else if (!sai->synchronous[RX] && sai->synchronous[TX] && tx) {
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_RCR4,
|
||||
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
|
||||
val_cr4);
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_RCR5,
|
||||
FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
|
||||
FSL_SAI_CR5_FBT_MASK, val_cr5);
|
||||
regmap_write(sai->regmap, FSL_SAI_RMR,
|
||||
~0UL - ((1 << channels) - 1));
|
||||
}
|
||||
}
|
||||
|
||||
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
|
||||
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
|
||||
|
|
@ -569,6 +628,7 @@ static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
|
|||
static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
|
||||
.set_sysclk = fsl_sai_set_dai_sysclk,
|
||||
.set_fmt = fsl_sai_set_dai_fmt,
|
||||
.set_tdm_slot = fsl_sai_set_dai_tdm_slot,
|
||||
.hw_params = fsl_sai_hw_params,
|
||||
.hw_free = fsl_sai_hw_free,
|
||||
.trigger = fsl_sai_trigger,
|
||||
|
|
@ -627,6 +687,22 @@ static const struct snd_soc_component_driver fsl_component = {
|
|||
.name = "fsl-sai",
|
||||
};
|
||||
|
||||
static struct reg_default fsl_sai_reg_defaults[] = {
|
||||
{FSL_SAI_TCR1, 0},
|
||||
{FSL_SAI_TCR2, 0},
|
||||
{FSL_SAI_TCR3, 0},
|
||||
{FSL_SAI_TCR4, 0},
|
||||
{FSL_SAI_TCR5, 0},
|
||||
{FSL_SAI_TDR, 0},
|
||||
{FSL_SAI_TMR, 0},
|
||||
{FSL_SAI_RCR1, 0},
|
||||
{FSL_SAI_RCR2, 0},
|
||||
{FSL_SAI_RCR3, 0},
|
||||
{FSL_SAI_RCR4, 0},
|
||||
{FSL_SAI_RCR5, 0},
|
||||
{FSL_SAI_RMR, 0},
|
||||
};
|
||||
|
||||
static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
|
|
@ -660,13 +736,11 @@ static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
|
|||
case FSL_SAI_RCSR:
|
||||
case FSL_SAI_TFR:
|
||||
case FSL_SAI_RFR:
|
||||
case FSL_SAI_TDR:
|
||||
case FSL_SAI_RDR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg)
|
||||
|
|
@ -699,6 +773,8 @@ static const struct regmap_config fsl_sai_regmap_config = {
|
|||
.val_bits = 32,
|
||||
|
||||
.max_register = FSL_SAI_RMR,
|
||||
.reg_defaults = fsl_sai_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(fsl_sai_reg_defaults),
|
||||
.readable_reg = fsl_sai_readable_reg,
|
||||
.volatile_reg = fsl_sai_volatile_reg,
|
||||
.writeable_reg = fsl_sai_writeable_reg,
|
||||
|
|
|
|||
|
|
@ -143,6 +143,9 @@ struct fsl_sai {
|
|||
|
||||
unsigned int mclk_id[2];
|
||||
unsigned int mclk_streams;
|
||||
unsigned int slots;
|
||||
unsigned int slot_width;
|
||||
|
||||
struct snd_dmaengine_dai_dma_data dma_params_rx;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_tx;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ struct spdif_mixer_control {
|
|||
* @rxclk: rx clock sources for capture
|
||||
* @coreclk: core clock for register access via DMA
|
||||
* @sysclk: system clock for rx clock rate measurement
|
||||
* @spbaclk: SPBA clock (optional, depending on SoC design)
|
||||
* @dma_params_tx: DMA parameters for transmit channel
|
||||
* @dma_params_rx: DMA parameters for receive channel
|
||||
*/
|
||||
|
|
@ -106,6 +107,7 @@ struct fsl_spdif_priv {
|
|||
struct clk *rxclk;
|
||||
struct clk *coreclk;
|
||||
struct clk *sysclk;
|
||||
struct clk *spbaclk;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_tx;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_rx;
|
||||
/* regcache for SRPC */
|
||||
|
|
@ -474,6 +476,14 @@ static int fsl_spdif_startup(struct snd_pcm_substream *substream,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (!IS_ERR(spdif_priv->spbaclk)) {
|
||||
ret = clk_prepare_enable(spdif_priv->spbaclk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable spba clock\n");
|
||||
goto err_spbaclk;
|
||||
}
|
||||
}
|
||||
|
||||
ret = spdif_softreset(spdif_priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to soft reset\n");
|
||||
|
|
@ -515,6 +525,9 @@ disable_txclk:
|
|||
for (i--; i >= 0; i--)
|
||||
clk_disable_unprepare(spdif_priv->txclk[i]);
|
||||
err:
|
||||
if (!IS_ERR(spdif_priv->spbaclk))
|
||||
clk_disable_unprepare(spdif_priv->spbaclk);
|
||||
err_spbaclk:
|
||||
clk_disable_unprepare(spdif_priv->coreclk);
|
||||
|
||||
return ret;
|
||||
|
|
@ -548,6 +561,8 @@ static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
|
|||
spdif_intr_status_clear(spdif_priv);
|
||||
regmap_update_bits(regmap, REG_SPDIF_SCR,
|
||||
SCR_LOW_POWER, SCR_LOW_POWER);
|
||||
if (!IS_ERR(spdif_priv->spbaclk))
|
||||
clk_disable_unprepare(spdif_priv->spbaclk);
|
||||
clk_disable_unprepare(spdif_priv->coreclk);
|
||||
}
|
||||
}
|
||||
|
|
@ -1006,12 +1021,14 @@ static const struct snd_soc_component_driver fsl_spdif_component = {
|
|||
|
||||
/* FSL SPDIF REGMAP */
|
||||
static const struct reg_default fsl_spdif_reg_defaults[] = {
|
||||
{0x0, 0x00000400},
|
||||
{0x4, 0x00000000},
|
||||
{0xc, 0x00000000},
|
||||
{0x34, 0x00000000},
|
||||
{0x38, 0x00000000},
|
||||
{0x50, 0x00020f00},
|
||||
{REG_SPDIF_SCR, 0x00000400},
|
||||
{REG_SPDIF_SRCD, 0x00000000},
|
||||
{REG_SPDIF_SIE, 0x00000000},
|
||||
{REG_SPDIF_STL, 0x00000000},
|
||||
{REG_SPDIF_STR, 0x00000000},
|
||||
{REG_SPDIF_STCSCH, 0x00000000},
|
||||
{REG_SPDIF_STCSCL, 0x00000000},
|
||||
{REG_SPDIF_STC, 0x00020f00},
|
||||
};
|
||||
|
||||
static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
|
||||
|
|
@ -1049,8 +1066,6 @@ static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg)
|
|||
case REG_SPDIF_SRCSL:
|
||||
case REG_SPDIF_SRU:
|
||||
case REG_SPDIF_SRQ:
|
||||
case REG_SPDIF_STL:
|
||||
case REG_SPDIF_STR:
|
||||
case REG_SPDIF_SRFM:
|
||||
return true;
|
||||
default:
|
||||
|
|
@ -1261,6 +1276,10 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(spdif_priv->coreclk);
|
||||
}
|
||||
|
||||
spdif_priv->spbaclk = devm_clk_get(&pdev->dev, "spba");
|
||||
if (IS_ERR(spdif_priv->spbaclk))
|
||||
dev_warn(&pdev->dev, "no spba clock in devicetree\n");
|
||||
|
||||
/* Select clock source for rx/tx clock */
|
||||
spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
|
||||
if (IS_ERR(spdif_priv->rxclk)) {
|
||||
|
|
|
|||
|
|
@ -113,17 +113,17 @@ struct fsl_ssi_rxtx_reg_val {
|
|||
};
|
||||
|
||||
static const struct reg_default fsl_ssi_reg_defaults[] = {
|
||||
{0x10, 0x00000000},
|
||||
{0x18, 0x00003003},
|
||||
{0x1c, 0x00000200},
|
||||
{0x20, 0x00000200},
|
||||
{0x24, 0x00040000},
|
||||
{0x28, 0x00040000},
|
||||
{0x38, 0x00000000},
|
||||
{0x48, 0x00000000},
|
||||
{0x4c, 0x00000000},
|
||||
{0x54, 0x00000000},
|
||||
{0x58, 0x00000000},
|
||||
{CCSR_SSI_SCR, 0x00000000},
|
||||
{CCSR_SSI_SIER, 0x00003003},
|
||||
{CCSR_SSI_STCR, 0x00000200},
|
||||
{CCSR_SSI_SRCR, 0x00000200},
|
||||
{CCSR_SSI_STCCR, 0x00040000},
|
||||
{CCSR_SSI_SRCCR, 0x00040000},
|
||||
{CCSR_SSI_SACNT, 0x00000000},
|
||||
{CCSR_SSI_STMSK, 0x00000000},
|
||||
{CCSR_SSI_SRMSK, 0x00000000},
|
||||
{CCSR_SSI_SACCEN, 0x00000000},
|
||||
{CCSR_SSI_SACCDIS, 0x00000000},
|
||||
};
|
||||
|
||||
static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg)
|
||||
|
|
@ -767,8 +767,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
|
|||
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct regmap *regs = ssi_private->regs;
|
||||
unsigned int channels = params_channels(hw_params);
|
||||
unsigned int sample_size =
|
||||
snd_pcm_format_width(params_format(hw_params));
|
||||
unsigned int sample_size = params_width(hw_params);
|
||||
u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
|
||||
int ret;
|
||||
u32 scr_val;
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ int imx_pcm_dma_init(struct platform_device *pdev, size_t size)
|
|||
|
||||
config = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct snd_dmaengine_pcm_config), GFP_KERNEL);
|
||||
if (!config)
|
||||
return -ENOMEM;
|
||||
*config = imx_dmaengine_pcm_config;
|
||||
if (size)
|
||||
config->prealloc_buffer_size = size;
|
||||
|
|
|
|||
|
|
@ -220,9 +220,9 @@ static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
|
|||
ret = dma_mmap_writecombine(substream->pcm->card->dev, vma,
|
||||
runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
|
||||
|
||||
pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret,
|
||||
pr_debug("%s: ret: %d %p %pad 0x%08x\n", __func__, ret,
|
||||
runtime->dma_area,
|
||||
runtime->dma_addr,
|
||||
&runtime->dma_addr,
|
||||
runtime->dma_bytes);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
52
sound/soc/img/Kconfig
Normal file
52
sound/soc/img/Kconfig
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
config SND_SOC_IMG
|
||||
bool "Audio support for Imagination Technologies designs"
|
||||
help
|
||||
Audio support for Imagination Technologies audio hardware
|
||||
|
||||
config SND_SOC_IMG_I2S_IN
|
||||
tristate "Imagination I2S Input Device Driver"
|
||||
depends on SND_SOC_IMG
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
Say Y or M if you want to add support for I2S in driver for
|
||||
Imagination Technologies I2S in device.
|
||||
|
||||
config SND_SOC_IMG_I2S_OUT
|
||||
tristate "Imagination I2S Output Device Driver"
|
||||
depends on SND_SOC_IMG
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
Say Y or M if you want to add support for I2S out driver for
|
||||
Imagination Technologies I2S out device.
|
||||
|
||||
config SND_SOC_IMG_PARALLEL_OUT
|
||||
tristate "Imagination Parallel Output Device Driver"
|
||||
depends on SND_SOC_IMG
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
Say Y or M if you want to add support for parallel out driver for
|
||||
Imagination Technologies parallel out device.
|
||||
|
||||
config SND_SOC_IMG_SPDIF_IN
|
||||
tristate "Imagination SPDIF Input Device Driver"
|
||||
depends on SND_SOC_IMG
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
Say Y or M if you want to add support for SPDIF input driver for
|
||||
Imagination Technologies SPDIF input device.
|
||||
|
||||
config SND_SOC_IMG_SPDIF_OUT
|
||||
tristate "Imagination SPDIF Output Device Driver"
|
||||
depends on SND_SOC_IMG
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
Say Y or M if you want to add support for SPDIF out driver for
|
||||
Imagination Technologies SPDIF out device.
|
||||
|
||||
|
||||
config SND_SOC_IMG_PISTACHIO_INTERNAL_DAC
|
||||
tristate "Support for Pistachio SoC Internal DAC Driver"
|
||||
depends on SND_SOC_IMG
|
||||
help
|
||||
Say Y or M if you want to add support for Pistachio internal DAC
|
||||
driver for Imagination Technologies Pistachio internal DAC device.
|
||||
7
sound/soc/img/Makefile
Normal file
7
sound/soc/img/Makefile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
obj-$(CONFIG_SND_SOC_IMG_I2S_IN) += img-i2s-in.o
|
||||
obj-$(CONFIG_SND_SOC_IMG_I2S_OUT) += img-i2s-out.o
|
||||
obj-$(CONFIG_SND_SOC_IMG_PARALLEL_OUT) += img-parallel-out.o
|
||||
obj-$(CONFIG_SND_SOC_IMG_SPDIF_IN) += img-spdif-in.o
|
||||
obj-$(CONFIG_SND_SOC_IMG_SPDIF_OUT) += img-spdif-out.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_IMG_PISTACHIO_INTERNAL_DAC) += pistachio-internal-dac.o
|
||||
516
sound/soc/img/img-i2s-in.c
Normal file
516
sound/soc/img/img-i2s-in.c
Normal file
|
|
@ -0,0 +1,516 @@
|
|||
/*
|
||||
* IMG I2S input controller driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define IMG_I2S_IN_RX_FIFO 0x0
|
||||
|
||||
#define IMG_I2S_IN_CTL 0x4
|
||||
#define IMG_I2S_IN_CTL_ACTIVE_CHAN_MASK 0xfffffffc
|
||||
#define IMG_I2S_IN_CTL_ACTIVE_CH_SHIFT 2
|
||||
#define IMG_I2S_IN_CTL_16PACK_MASK BIT(1)
|
||||
#define IMG_I2S_IN_CTL_ME_MASK BIT(0)
|
||||
|
||||
#define IMG_I2S_IN_CH_CTL 0x4
|
||||
#define IMG_I2S_IN_CH_CTL_CCDEL_MASK 0x38000
|
||||
#define IMG_I2S_IN_CH_CTL_CCDEL_SHIFT 15
|
||||
#define IMG_I2S_IN_CH_CTL_FEN_MASK BIT(14)
|
||||
#define IMG_I2S_IN_CH_CTL_FMODE_MASK BIT(13)
|
||||
#define IMG_I2S_IN_CH_CTL_16PACK_MASK BIT(12)
|
||||
#define IMG_I2S_IN_CH_CTL_JUST_MASK BIT(10)
|
||||
#define IMG_I2S_IN_CH_CTL_PACKH_MASK BIT(9)
|
||||
#define IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK BIT(8)
|
||||
#define IMG_I2S_IN_CH_CTL_BLKP_MASK BIT(7)
|
||||
#define IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK BIT(6)
|
||||
#define IMG_I2S_IN_CH_CTL_LRD_MASK BIT(3)
|
||||
#define IMG_I2S_IN_CH_CTL_FW_MASK BIT(2)
|
||||
#define IMG_I2S_IN_CH_CTL_SW_MASK BIT(1)
|
||||
#define IMG_I2S_IN_CH_CTL_ME_MASK BIT(0)
|
||||
|
||||
#define IMG_I2S_IN_CH_STRIDE 0x20
|
||||
|
||||
struct img_i2s_in {
|
||||
void __iomem *base;
|
||||
struct clk *clk_sys;
|
||||
struct snd_dmaengine_dai_dma_data dma_data;
|
||||
struct device *dev;
|
||||
unsigned int max_i2s_chan;
|
||||
void __iomem *channel_base;
|
||||
unsigned int active_channels;
|
||||
struct snd_soc_dai_driver dai_driver;
|
||||
};
|
||||
|
||||
static inline void img_i2s_in_writel(struct img_i2s_in *i2s, u32 val, u32 reg)
|
||||
{
|
||||
writel(val, i2s->base + reg);
|
||||
}
|
||||
|
||||
static inline u32 img_i2s_in_readl(struct img_i2s_in *i2s, u32 reg)
|
||||
{
|
||||
return readl(i2s->base + reg);
|
||||
}
|
||||
|
||||
static inline void img_i2s_in_ch_writel(struct img_i2s_in *i2s, u32 chan,
|
||||
u32 val, u32 reg)
|
||||
{
|
||||
writel(val, i2s->channel_base + (chan * IMG_I2S_IN_CH_STRIDE) + reg);
|
||||
}
|
||||
|
||||
static inline u32 img_i2s_in_ch_readl(struct img_i2s_in *i2s, u32 chan,
|
||||
u32 reg)
|
||||
{
|
||||
return readl(i2s->channel_base + (chan * IMG_I2S_IN_CH_STRIDE) + reg);
|
||||
}
|
||||
|
||||
static inline void img_i2s_in_ch_disable(struct img_i2s_in *i2s, u32 chan)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = img_i2s_in_ch_readl(i2s, chan, IMG_I2S_IN_CH_CTL);
|
||||
reg &= ~IMG_I2S_IN_CH_CTL_ME_MASK;
|
||||
img_i2s_in_ch_writel(i2s, chan, reg, IMG_I2S_IN_CH_CTL);
|
||||
}
|
||||
|
||||
static inline void img_i2s_in_ch_enable(struct img_i2s_in *i2s, u32 chan)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = img_i2s_in_ch_readl(i2s, chan, IMG_I2S_IN_CH_CTL);
|
||||
reg |= IMG_I2S_IN_CH_CTL_ME_MASK;
|
||||
img_i2s_in_ch_writel(i2s, chan, reg, IMG_I2S_IN_CH_CTL);
|
||||
}
|
||||
|
||||
static inline void img_i2s_in_disable(struct img_i2s_in *i2s)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
|
||||
reg &= ~IMG_I2S_IN_CTL_ME_MASK;
|
||||
img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
|
||||
}
|
||||
|
||||
static inline void img_i2s_in_enable(struct img_i2s_in *i2s)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
|
||||
reg |= IMG_I2S_IN_CTL_ME_MASK;
|
||||
img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
|
||||
}
|
||||
|
||||
static inline void img_i2s_in_flush(struct img_i2s_in *i2s)
|
||||
{
|
||||
int i;
|
||||
u32 reg;
|
||||
|
||||
for (i = 0; i < i2s->active_channels; i++) {
|
||||
reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
|
||||
reg |= IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK;
|
||||
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
|
||||
reg &= ~IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK;
|
||||
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
|
||||
}
|
||||
}
|
||||
|
||||
static int img_i2s_in_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
img_i2s_in_enable(i2s);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
img_i2s_in_disable(i2s);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_i2s_in_check_rate(struct img_i2s_in *i2s,
|
||||
unsigned int sample_rate, unsigned int frame_size,
|
||||
unsigned int *bclk_filter_enable,
|
||||
unsigned int *bclk_filter_value)
|
||||
{
|
||||
unsigned int bclk_freq, cur_freq;
|
||||
|
||||
bclk_freq = sample_rate * frame_size;
|
||||
|
||||
cur_freq = clk_get_rate(i2s->clk_sys);
|
||||
|
||||
if (cur_freq >= bclk_freq * 8) {
|
||||
*bclk_filter_enable = 1;
|
||||
*bclk_filter_value = 0;
|
||||
} else if (cur_freq >= bclk_freq * 7) {
|
||||
*bclk_filter_enable = 1;
|
||||
*bclk_filter_value = 1;
|
||||
} else if (cur_freq >= bclk_freq * 6) {
|
||||
*bclk_filter_enable = 0;
|
||||
*bclk_filter_value = 0;
|
||||
} else {
|
||||
dev_err(i2s->dev,
|
||||
"Sys clock rate %u insufficient for sample rate %u\n",
|
||||
cur_freq, sample_rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_i2s_in_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int rate, channels, i2s_channels, frame_size;
|
||||
unsigned int bclk_filter_enable, bclk_filter_value;
|
||||
int i, ret = 0;
|
||||
u32 reg, control_mask, chan_control_mask;
|
||||
u32 control_set = 0, chan_control_set = 0;
|
||||
snd_pcm_format_t format;
|
||||
|
||||
rate = params_rate(params);
|
||||
format = params_format(params);
|
||||
channels = params_channels(params);
|
||||
i2s_channels = channels / 2;
|
||||
|
||||
switch (format) {
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
frame_size = 64;
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_SW_MASK;
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_FW_MASK;
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_PACKH_MASK;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
frame_size = 64;
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_SW_MASK;
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_FW_MASK;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
frame_size = 32;
|
||||
control_set |= IMG_I2S_IN_CTL_16PACK_MASK;
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_16PACK_MASK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((channels < 2) ||
|
||||
(channels > (i2s->max_i2s_chan * 2)) ||
|
||||
(channels % 2))
|
||||
return -EINVAL;
|
||||
|
||||
control_set |= ((i2s_channels - 1) << IMG_I2S_IN_CTL_ACTIVE_CH_SHIFT);
|
||||
|
||||
ret = img_i2s_in_check_rate(i2s, rate, frame_size,
|
||||
&bclk_filter_enable, &bclk_filter_value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (bclk_filter_enable)
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_FEN_MASK;
|
||||
|
||||
if (bclk_filter_value)
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_FMODE_MASK;
|
||||
|
||||
control_mask = IMG_I2S_IN_CTL_16PACK_MASK |
|
||||
IMG_I2S_IN_CTL_ACTIVE_CHAN_MASK;
|
||||
|
||||
chan_control_mask = IMG_I2S_IN_CH_CTL_16PACK_MASK |
|
||||
IMG_I2S_IN_CH_CTL_FEN_MASK |
|
||||
IMG_I2S_IN_CH_CTL_FMODE_MASK |
|
||||
IMG_I2S_IN_CH_CTL_SW_MASK |
|
||||
IMG_I2S_IN_CH_CTL_FW_MASK |
|
||||
IMG_I2S_IN_CH_CTL_PACKH_MASK;
|
||||
|
||||
reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL);
|
||||
reg = (reg & ~control_mask) | control_set;
|
||||
img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL);
|
||||
|
||||
for (i = 0; i < i2s->active_channels; i++)
|
||||
img_i2s_in_ch_disable(i2s, i);
|
||||
|
||||
for (i = 0; i < i2s->max_i2s_chan; i++) {
|
||||
reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
|
||||
reg = (reg & ~chan_control_mask) | chan_control_set;
|
||||
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
|
||||
}
|
||||
|
||||
i2s->active_channels = i2s_channels;
|
||||
|
||||
img_i2s_in_flush(i2s);
|
||||
|
||||
for (i = 0; i < i2s->active_channels; i++)
|
||||
img_i2s_in_ch_enable(i2s, i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
int i;
|
||||
u32 chan_control_mask, lrd_set = 0, blkp_set = 0, chan_control_set = 0;
|
||||
u32 reg;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
lrd_set |= IMG_I2S_IN_CH_CTL_LRD_MASK;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
lrd_set |= IMG_I2S_IN_CH_CTL_LRD_MASK;
|
||||
blkp_set |= IMG_I2S_IN_CH_CTL_BLKP_MASK;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
blkp_set |= IMG_I2S_IN_CH_CTL_BLKP_MASK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
chan_control_set |= IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chan_control_mask = IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK;
|
||||
|
||||
for (i = 0; i < i2s->active_channels; i++)
|
||||
img_i2s_in_ch_disable(i2s, i);
|
||||
|
||||
/*
|
||||
* BLKP and LRD must be set during separate register writes
|
||||
*/
|
||||
for (i = 0; i < i2s->max_i2s_chan; i++) {
|
||||
reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL);
|
||||
reg = (reg & ~chan_control_mask) | chan_control_set;
|
||||
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
|
||||
reg = (reg & ~IMG_I2S_IN_CH_CTL_BLKP_MASK) | blkp_set;
|
||||
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
|
||||
reg = (reg & ~IMG_I2S_IN_CH_CTL_LRD_MASK) | lrd_set;
|
||||
img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL);
|
||||
}
|
||||
|
||||
for (i = 0; i < i2s->active_channels; i++)
|
||||
img_i2s_in_ch_enable(i2s, i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops img_i2s_in_dai_ops = {
|
||||
.trigger = img_i2s_in_trigger,
|
||||
.hw_params = img_i2s_in_hw_params,
|
||||
.set_fmt = img_i2s_in_set_fmt
|
||||
};
|
||||
|
||||
static int img_i2s_in_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, NULL, &i2s->dma_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver img_i2s_in_component = {
|
||||
.name = "img-i2s-in"
|
||||
};
|
||||
|
||||
static int img_i2s_in_dma_prepare_slave_config(struct snd_pcm_substream *st,
|
||||
struct snd_pcm_hw_params *params, struct dma_slave_config *sc)
|
||||
{
|
||||
unsigned int i2s_channels = params_channels(params) / 2;
|
||||
struct snd_soc_pcm_runtime *rtd = st->private_data;
|
||||
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
int ret;
|
||||
|
||||
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st);
|
||||
|
||||
ret = snd_hwparams_to_dma_slave_config(st, params, sc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sc->src_addr = dma_data->addr;
|
||||
sc->src_addr_width = dma_data->addr_width;
|
||||
sc->src_maxburst = 4 * i2s_channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_dmaengine_pcm_config img_i2s_in_dma_config = {
|
||||
.prepare_slave_config = img_i2s_in_dma_prepare_slave_config
|
||||
};
|
||||
|
||||
static int img_i2s_in_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct img_i2s_in *i2s;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int ret, i;
|
||||
struct reset_control *rst;
|
||||
unsigned int max_i2s_chan_pow_2;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL);
|
||||
if (!i2s)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, i2s);
|
||||
|
||||
i2s->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
i2s->base = base;
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels",
|
||||
&i2s->max_i2s_chan)) {
|
||||
dev_err(dev, "No img,i2s-channels property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan);
|
||||
|
||||
i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
|
||||
|
||||
i2s->clk_sys = devm_clk_get(dev, "sys");
|
||||
if (IS_ERR(i2s->clk_sys)) {
|
||||
if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to acquire clock 'sys'\n");
|
||||
return PTR_ERR(i2s->clk_sys);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(i2s->clk_sys);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
i2s->active_channels = 1;
|
||||
i2s->dma_data.addr = res->start + IMG_I2S_IN_RX_FIFO;
|
||||
i2s->dma_data.addr_width = 4;
|
||||
|
||||
i2s->dai_driver.probe = img_i2s_in_dai_probe;
|
||||
i2s->dai_driver.capture.channels_min = 2;
|
||||
i2s->dai_driver.capture.channels_max = i2s->max_i2s_chan * 2;
|
||||
i2s->dai_driver.capture.rates = SNDRV_PCM_RATE_8000_192000;
|
||||
i2s->dai_driver.capture.formats = SNDRV_PCM_FMTBIT_S32_LE |
|
||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE;
|
||||
i2s->dai_driver.ops = &img_i2s_in_dai_ops;
|
||||
|
||||
rst = devm_reset_control_get(dev, "rst");
|
||||
if (IS_ERR(rst)) {
|
||||
if (PTR_ERR(rst) == -EPROBE_DEFER) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto err_clk_disable;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "No top level reset found\n");
|
||||
|
||||
img_i2s_in_disable(i2s);
|
||||
|
||||
for (i = 0; i < i2s->max_i2s_chan; i++)
|
||||
img_i2s_in_ch_disable(i2s, i);
|
||||
} else {
|
||||
reset_control_assert(rst);
|
||||
reset_control_deassert(rst);
|
||||
}
|
||||
|
||||
img_i2s_in_writel(i2s, 0, IMG_I2S_IN_CTL);
|
||||
|
||||
for (i = 0; i < i2s->max_i2s_chan; i++)
|
||||
img_i2s_in_ch_writel(i2s, i,
|
||||
(4 << IMG_I2S_IN_CH_CTL_CCDEL_SHIFT) |
|
||||
IMG_I2S_IN_CH_CTL_JUST_MASK |
|
||||
IMG_I2S_IN_CH_CTL_FW_MASK, IMG_I2S_IN_CH_CTL);
|
||||
|
||||
ret = devm_snd_soc_register_component(dev, &img_i2s_in_component,
|
||||
&i2s->dai_driver, 1);
|
||||
if (ret)
|
||||
goto err_clk_disable;
|
||||
|
||||
ret = devm_snd_dmaengine_pcm_register(dev, &img_i2s_in_dma_config, 0);
|
||||
if (ret)
|
||||
goto err_clk_disable;
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(i2s->clk_sys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int img_i2s_in_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct img_i2s_in *i2s = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(i2s->clk_sys);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id img_i2s_in_of_match[] = {
|
||||
{ .compatible = "img,i2s-in" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, img_i2s_in_of_match);
|
||||
|
||||
static struct platform_driver img_i2s_in_driver = {
|
||||
.driver = {
|
||||
.name = "img-i2s-in",
|
||||
.of_match_table = img_i2s_in_of_match
|
||||
},
|
||||
.probe = img_i2s_in_probe,
|
||||
.remove = img_i2s_in_dev_remove
|
||||
};
|
||||
module_platform_driver(img_i2s_in_driver);
|
||||
|
||||
MODULE_AUTHOR("Damien Horsley <[email protected]>");
|
||||
MODULE_DESCRIPTION("IMG I2S Input Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
565
sound/soc/img/img-i2s-out.c
Normal file
565
sound/soc/img/img-i2s-out.c
Normal file
|
|
@ -0,0 +1,565 @@
|
|||
/*
|
||||
* IMG I2S output controller driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define IMG_I2S_OUT_TX_FIFO 0x0
|
||||
|
||||
#define IMG_I2S_OUT_CTL 0x4
|
||||
#define IMG_I2S_OUT_CTL_DATA_EN_MASK BIT(24)
|
||||
#define IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK 0xffe000
|
||||
#define IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT 13
|
||||
#define IMG_I2S_OUT_CTL_FRM_SIZE_MASK BIT(8)
|
||||
#define IMG_I2S_OUT_CTL_MASTER_MASK BIT(6)
|
||||
#define IMG_I2S_OUT_CTL_CLK_MASK BIT(5)
|
||||
#define IMG_I2S_OUT_CTL_CLK_EN_MASK BIT(4)
|
||||
#define IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK BIT(3)
|
||||
#define IMG_I2S_OUT_CTL_BCLK_POL_MASK BIT(2)
|
||||
#define IMG_I2S_OUT_CTL_ME_MASK BIT(0)
|
||||
|
||||
#define IMG_I2S_OUT_CH_CTL 0x4
|
||||
#define IMG_I2S_OUT_CHAN_CTL_CH_MASK BIT(11)
|
||||
#define IMG_I2S_OUT_CHAN_CTL_LT_MASK BIT(10)
|
||||
#define IMG_I2S_OUT_CHAN_CTL_FMT_MASK 0xf0
|
||||
#define IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT 4
|
||||
#define IMG_I2S_OUT_CHAN_CTL_JUST_MASK BIT(3)
|
||||
#define IMG_I2S_OUT_CHAN_CTL_CLKT_MASK BIT(1)
|
||||
#define IMG_I2S_OUT_CHAN_CTL_ME_MASK BIT(0)
|
||||
|
||||
#define IMG_I2S_OUT_CH_STRIDE 0x20
|
||||
|
||||
struct img_i2s_out {
|
||||
void __iomem *base;
|
||||
struct clk *clk_sys;
|
||||
struct clk *clk_ref;
|
||||
struct snd_dmaengine_dai_dma_data dma_data;
|
||||
struct device *dev;
|
||||
unsigned int max_i2s_chan;
|
||||
void __iomem *channel_base;
|
||||
bool force_clk_active;
|
||||
unsigned int active_channels;
|
||||
struct reset_control *rst;
|
||||
struct snd_soc_dai_driver dai_driver;
|
||||
};
|
||||
|
||||
static int img_i2s_out_suspend(struct device *dev)
|
||||
{
|
||||
struct img_i2s_out *i2s = dev_get_drvdata(dev);
|
||||
|
||||
if (!i2s->force_clk_active)
|
||||
clk_disable_unprepare(i2s->clk_ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_i2s_out_resume(struct device *dev)
|
||||
{
|
||||
struct img_i2s_out *i2s = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (!i2s->force_clk_active) {
|
||||
ret = clk_prepare_enable(i2s->clk_ref);
|
||||
if (ret) {
|
||||
dev_err(dev, "clk_enable failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void img_i2s_out_writel(struct img_i2s_out *i2s, u32 val,
|
||||
u32 reg)
|
||||
{
|
||||
writel(val, i2s->base + reg);
|
||||
}
|
||||
|
||||
static inline u32 img_i2s_out_readl(struct img_i2s_out *i2s, u32 reg)
|
||||
{
|
||||
return readl(i2s->base + reg);
|
||||
}
|
||||
|
||||
static inline void img_i2s_out_ch_writel(struct img_i2s_out *i2s,
|
||||
u32 chan, u32 val, u32 reg)
|
||||
{
|
||||
writel(val, i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg);
|
||||
}
|
||||
|
||||
static inline u32 img_i2s_out_ch_readl(struct img_i2s_out *i2s, u32 chan,
|
||||
u32 reg)
|
||||
{
|
||||
return readl(i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg);
|
||||
}
|
||||
|
||||
static inline void img_i2s_out_ch_disable(struct img_i2s_out *i2s, u32 chan)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL);
|
||||
reg &= ~IMG_I2S_OUT_CHAN_CTL_ME_MASK;
|
||||
img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL);
|
||||
}
|
||||
|
||||
static inline void img_i2s_out_ch_enable(struct img_i2s_out *i2s, u32 chan)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL);
|
||||
reg |= IMG_I2S_OUT_CHAN_CTL_ME_MASK;
|
||||
img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL);
|
||||
}
|
||||
|
||||
static inline void img_i2s_out_disable(struct img_i2s_out *i2s)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
|
||||
reg &= ~IMG_I2S_OUT_CTL_ME_MASK;
|
||||
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
|
||||
}
|
||||
|
||||
static inline void img_i2s_out_enable(struct img_i2s_out *i2s)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
|
||||
reg |= IMG_I2S_OUT_CTL_ME_MASK;
|
||||
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
|
||||
}
|
||||
|
||||
static void img_i2s_out_reset(struct img_i2s_out *i2s)
|
||||
{
|
||||
int i;
|
||||
u32 core_ctl, chan_ctl;
|
||||
|
||||
core_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL) &
|
||||
~IMG_I2S_OUT_CTL_ME_MASK &
|
||||
~IMG_I2S_OUT_CTL_DATA_EN_MASK;
|
||||
|
||||
if (!i2s->force_clk_active)
|
||||
core_ctl &= ~IMG_I2S_OUT_CTL_CLK_EN_MASK;
|
||||
|
||||
chan_ctl = img_i2s_out_ch_readl(i2s, 0, IMG_I2S_OUT_CH_CTL) &
|
||||
~IMG_I2S_OUT_CHAN_CTL_ME_MASK;
|
||||
|
||||
reset_control_assert(i2s->rst);
|
||||
reset_control_deassert(i2s->rst);
|
||||
|
||||
for (i = 0; i < i2s->max_i2s_chan; i++)
|
||||
img_i2s_out_ch_writel(i2s, i, chan_ctl, IMG_I2S_OUT_CH_CTL);
|
||||
|
||||
for (i = 0; i < i2s->active_channels; i++)
|
||||
img_i2s_out_ch_enable(i2s, i);
|
||||
|
||||
img_i2s_out_writel(i2s, core_ctl, IMG_I2S_OUT_CTL);
|
||||
img_i2s_out_enable(i2s);
|
||||
}
|
||||
|
||||
static int img_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
u32 reg;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
|
||||
if (!i2s->force_clk_active)
|
||||
reg |= IMG_I2S_OUT_CTL_CLK_EN_MASK;
|
||||
reg |= IMG_I2S_OUT_CTL_DATA_EN_MASK;
|
||||
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
img_i2s_out_reset(i2s);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_i2s_out_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int channels, i2s_channels;
|
||||
long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate;
|
||||
int i;
|
||||
u32 reg, control_mask, control_set = 0;
|
||||
snd_pcm_format_t format;
|
||||
|
||||
rate = params_rate(params);
|
||||
format = params_format(params);
|
||||
channels = params_channels(params);
|
||||
i2s_channels = channels / 2;
|
||||
|
||||
if (format != SNDRV_PCM_FORMAT_S32_LE)
|
||||
return -EINVAL;
|
||||
|
||||
if ((channels < 2) ||
|
||||
(channels > (i2s->max_i2s_chan * 2)) ||
|
||||
(channels % 2))
|
||||
return -EINVAL;
|
||||
|
||||
pre_div_a = clk_round_rate(i2s->clk_ref, rate * 256);
|
||||
if (pre_div_a < 0)
|
||||
return pre_div_a;
|
||||
pre_div_b = clk_round_rate(i2s->clk_ref, rate * 384);
|
||||
if (pre_div_b < 0)
|
||||
return pre_div_b;
|
||||
|
||||
diff_a = abs((pre_div_a / 256) - rate);
|
||||
diff_b = abs((pre_div_b / 384) - rate);
|
||||
|
||||
/* If diffs are equal, use lower clock rate */
|
||||
if (diff_a > diff_b)
|
||||
clk_set_rate(i2s->clk_ref, pre_div_b);
|
||||
else
|
||||
clk_set_rate(i2s->clk_ref, pre_div_a);
|
||||
|
||||
/*
|
||||
* Another driver (eg alsa machine driver) may have rejected the above
|
||||
* change. Get the current rate and set the register bit according to
|
||||
* the new minimum diff
|
||||
*/
|
||||
clk_rate = clk_get_rate(i2s->clk_ref);
|
||||
|
||||
diff_a = abs((clk_rate / 256) - rate);
|
||||
diff_b = abs((clk_rate / 384) - rate);
|
||||
|
||||
if (diff_a > diff_b)
|
||||
control_set |= IMG_I2S_OUT_CTL_CLK_MASK;
|
||||
|
||||
control_set |= ((i2s_channels - 1) <<
|
||||
IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT) &
|
||||
IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK;
|
||||
|
||||
control_mask = IMG_I2S_OUT_CTL_CLK_MASK |
|
||||
IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK;
|
||||
|
||||
img_i2s_out_disable(i2s);
|
||||
|
||||
reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
|
||||
reg = (reg & ~control_mask) | control_set;
|
||||
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
|
||||
|
||||
for (i = 0; i < i2s_channels; i++)
|
||||
img_i2s_out_ch_enable(i2s, i);
|
||||
|
||||
for (; i < i2s->max_i2s_chan; i++)
|
||||
img_i2s_out_ch_disable(i2s, i);
|
||||
|
||||
img_i2s_out_enable(i2s);
|
||||
|
||||
i2s->active_channels = i2s_channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
int i;
|
||||
bool force_clk_active;
|
||||
u32 chan_control_mask, control_mask, chan_control_set = 0;
|
||||
u32 reg, control_set = 0;
|
||||
|
||||
force_clk_active = ((fmt & SND_SOC_DAIFMT_CLOCK_MASK) ==
|
||||
SND_SOC_DAIFMT_CONT);
|
||||
|
||||
if (force_clk_active)
|
||||
control_set |= IMG_I2S_OUT_CTL_CLK_EN_MASK;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
control_set |= IMG_I2S_OUT_CTL_MASTER_MASK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK;
|
||||
control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
chan_control_set |= IMG_I2S_OUT_CHAN_CTL_CLKT_MASK;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
control_mask = IMG_I2S_OUT_CTL_CLK_EN_MASK |
|
||||
IMG_I2S_OUT_CTL_MASTER_MASK |
|
||||
IMG_I2S_OUT_CTL_BCLK_POL_MASK |
|
||||
IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
|
||||
|
||||
chan_control_mask = IMG_I2S_OUT_CHAN_CTL_CLKT_MASK;
|
||||
|
||||
img_i2s_out_disable(i2s);
|
||||
|
||||
reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
|
||||
reg = (reg & ~control_mask) | control_set;
|
||||
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
|
||||
|
||||
for (i = 0; i < i2s->active_channels; i++)
|
||||
img_i2s_out_ch_disable(i2s, i);
|
||||
|
||||
for (i = 0; i < i2s->max_i2s_chan; i++) {
|
||||
reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL);
|
||||
reg = (reg & ~chan_control_mask) | chan_control_set;
|
||||
img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
|
||||
}
|
||||
|
||||
for (i = 0; i < i2s->active_channels; i++)
|
||||
img_i2s_out_ch_enable(i2s, i);
|
||||
|
||||
img_i2s_out_enable(i2s);
|
||||
|
||||
i2s->force_clk_active = force_clk_active;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops img_i2s_out_dai_ops = {
|
||||
.trigger = img_i2s_out_trigger,
|
||||
.hw_params = img_i2s_out_hw_params,
|
||||
.set_fmt = img_i2s_out_set_fmt
|
||||
};
|
||||
|
||||
static int img_i2s_out_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, &i2s->dma_data, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver img_i2s_out_component = {
|
||||
.name = "img-i2s-out"
|
||||
};
|
||||
|
||||
static int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream *st,
|
||||
struct snd_pcm_hw_params *params, struct dma_slave_config *sc)
|
||||
{
|
||||
unsigned int i2s_channels = params_channels(params) / 2;
|
||||
struct snd_soc_pcm_runtime *rtd = st->private_data;
|
||||
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
int ret;
|
||||
|
||||
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st);
|
||||
|
||||
ret = snd_hwparams_to_dma_slave_config(st, params, sc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sc->dst_addr = dma_data->addr;
|
||||
sc->dst_addr_width = dma_data->addr_width;
|
||||
sc->dst_maxburst = 4 * i2s_channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_dmaengine_pcm_config img_i2s_out_dma_config = {
|
||||
.prepare_slave_config = img_i2s_out_dma_prepare_slave_config
|
||||
};
|
||||
|
||||
static int img_i2s_out_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct img_i2s_out *i2s;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int i, ret;
|
||||
unsigned int max_i2s_chan_pow_2;
|
||||
u32 reg;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
|
||||
if (!i2s)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, i2s);
|
||||
|
||||
i2s->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
i2s->base = base;
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels",
|
||||
&i2s->max_i2s_chan)) {
|
||||
dev_err(&pdev->dev, "No img,i2s-channels property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan);
|
||||
|
||||
i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
|
||||
|
||||
i2s->rst = devm_reset_control_get(&pdev->dev, "rst");
|
||||
if (IS_ERR(i2s->rst)) {
|
||||
if (PTR_ERR(i2s->rst) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "No top level reset found\n");
|
||||
return PTR_ERR(i2s->rst);
|
||||
}
|
||||
|
||||
i2s->clk_sys = devm_clk_get(&pdev->dev, "sys");
|
||||
if (IS_ERR(i2s->clk_sys)) {
|
||||
if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to acquire clock 'sys'\n");
|
||||
return PTR_ERR(i2s->clk_sys);
|
||||
}
|
||||
|
||||
i2s->clk_ref = devm_clk_get(&pdev->dev, "ref");
|
||||
if (IS_ERR(i2s->clk_ref)) {
|
||||
if (PTR_ERR(i2s->clk_ref) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to acquire clock 'ref'\n");
|
||||
return PTR_ERR(i2s->clk_ref);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(i2s->clk_sys);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK;
|
||||
img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
|
||||
|
||||
reg = IMG_I2S_OUT_CHAN_CTL_JUST_MASK |
|
||||
IMG_I2S_OUT_CHAN_CTL_LT_MASK |
|
||||
IMG_I2S_OUT_CHAN_CTL_CH_MASK |
|
||||
(8 << IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT);
|
||||
|
||||
for (i = 0; i < i2s->max_i2s_chan; i++)
|
||||
img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
|
||||
|
||||
img_i2s_out_reset(i2s);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
if (!pm_runtime_enabled(&pdev->dev)) {
|
||||
ret = img_i2s_out_resume(&pdev->dev);
|
||||
if (ret)
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
i2s->active_channels = 1;
|
||||
i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO;
|
||||
i2s->dma_data.addr_width = 4;
|
||||
i2s->dma_data.maxburst = 4;
|
||||
|
||||
i2s->dai_driver.probe = img_i2s_out_dai_probe;
|
||||
i2s->dai_driver.playback.channels_min = 2;
|
||||
i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2;
|
||||
i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000;
|
||||
i2s->dai_driver.playback.formats = SNDRV_PCM_FMTBIT_S32_LE;
|
||||
i2s->dai_driver.ops = &img_i2s_out_dai_ops;
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||
&img_i2s_out_component, &i2s->dai_driver, 1);
|
||||
if (ret)
|
||||
goto err_suspend;
|
||||
|
||||
ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
|
||||
&img_i2s_out_dma_config, 0);
|
||||
if (ret)
|
||||
goto err_suspend;
|
||||
|
||||
return 0;
|
||||
|
||||
err_suspend:
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
img_i2s_out_suspend(&pdev->dev);
|
||||
err_pm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
clk_disable_unprepare(i2s->clk_sys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int img_i2s_out_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct img_i2s_out *i2s = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
img_i2s_out_suspend(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(i2s->clk_sys);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id img_i2s_out_of_match[] = {
|
||||
{ .compatible = "img,i2s-out" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, img_i2s_out_of_match);
|
||||
|
||||
static const struct dev_pm_ops img_i2s_out_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(img_i2s_out_suspend,
|
||||
img_i2s_out_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver img_i2s_out_driver = {
|
||||
.driver = {
|
||||
.name = "img-i2s-out",
|
||||
.of_match_table = img_i2s_out_of_match,
|
||||
.pm = &img_i2s_out_pm_ops
|
||||
},
|
||||
.probe = img_i2s_out_probe,
|
||||
.remove = img_i2s_out_dev_remove
|
||||
};
|
||||
module_platform_driver(img_i2s_out_driver);
|
||||
|
||||
MODULE_AUTHOR("Damien Horsley <[email protected]>");
|
||||
MODULE_DESCRIPTION("IMG I2S Output Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
327
sound/soc/img/img-parallel-out.c
Normal file
327
sound/soc/img/img-parallel-out.c
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
/*
|
||||
* IMG parallel output controller driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define IMG_PRL_OUT_TX_FIFO 0
|
||||
|
||||
#define IMG_PRL_OUT_CTL 0x4
|
||||
#define IMG_PRL_OUT_CTL_CH_MASK BIT(4)
|
||||
#define IMG_PRL_OUT_CTL_PACKH_MASK BIT(3)
|
||||
#define IMG_PRL_OUT_CTL_EDGE_MASK BIT(2)
|
||||
#define IMG_PRL_OUT_CTL_ME_MASK BIT(1)
|
||||
#define IMG_PRL_OUT_CTL_SRST_MASK BIT(0)
|
||||
|
||||
struct img_prl_out {
|
||||
void __iomem *base;
|
||||
struct clk *clk_sys;
|
||||
struct clk *clk_ref;
|
||||
struct snd_dmaengine_dai_dma_data dma_data;
|
||||
struct device *dev;
|
||||
struct reset_control *rst;
|
||||
};
|
||||
|
||||
static int img_prl_out_suspend(struct device *dev)
|
||||
{
|
||||
struct img_prl_out *prl = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(prl->clk_ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_prl_out_resume(struct device *dev)
|
||||
{
|
||||
struct img_prl_out *prl = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(prl->clk_ref);
|
||||
if (ret) {
|
||||
dev_err(dev, "clk_enable failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void img_prl_out_writel(struct img_prl_out *prl,
|
||||
u32 val, u32 reg)
|
||||
{
|
||||
writel(val, prl->base + reg);
|
||||
}
|
||||
|
||||
static inline u32 img_prl_out_readl(struct img_prl_out *prl, u32 reg)
|
||||
{
|
||||
return readl(prl->base + reg);
|
||||
}
|
||||
|
||||
static void img_prl_out_reset(struct img_prl_out *prl)
|
||||
{
|
||||
u32 ctl;
|
||||
|
||||
ctl = img_prl_out_readl(prl, IMG_PRL_OUT_CTL) &
|
||||
~IMG_PRL_OUT_CTL_ME_MASK;
|
||||
|
||||
reset_control_assert(prl->rst);
|
||||
reset_control_deassert(prl->rst);
|
||||
|
||||
img_prl_out_writel(prl, ctl, IMG_PRL_OUT_CTL);
|
||||
}
|
||||
|
||||
static int img_prl_out_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
|
||||
u32 reg;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);
|
||||
reg |= IMG_PRL_OUT_CTL_ME_MASK;
|
||||
img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
img_prl_out_reset(prl);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_prl_out_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int rate, channels;
|
||||
u32 reg, control_set = 0;
|
||||
snd_pcm_format_t format;
|
||||
|
||||
rate = params_rate(params);
|
||||
format = params_format(params);
|
||||
channels = params_channels(params);
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
control_set |= IMG_PRL_OUT_CTL_PACKH_MASK;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (channels != 2)
|
||||
return -EINVAL;
|
||||
|
||||
clk_set_rate(prl->clk_ref, rate * 256);
|
||||
|
||||
reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);
|
||||
reg = (reg & ~IMG_PRL_OUT_CTL_PACKH_MASK) | control_set;
|
||||
img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
|
||||
u32 reg, control_set = 0;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
control_set |= IMG_PRL_OUT_CTL_EDGE_MASK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL);
|
||||
reg = (reg & ~IMG_PRL_OUT_CTL_EDGE_MASK) | control_set;
|
||||
img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops img_prl_out_dai_ops = {
|
||||
.trigger = img_prl_out_trigger,
|
||||
.hw_params = img_prl_out_hw_params,
|
||||
.set_fmt = img_prl_out_set_fmt
|
||||
};
|
||||
|
||||
static int img_prl_out_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, &prl->dma_data, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_driver img_prl_out_dai = {
|
||||
.probe = img_prl_out_dai_probe,
|
||||
.playback = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE
|
||||
},
|
||||
.ops = &img_prl_out_dai_ops
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver img_prl_out_component = {
|
||||
.name = "img-prl-out"
|
||||
};
|
||||
|
||||
static int img_prl_out_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct img_prl_out *prl;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
prl = devm_kzalloc(&pdev->dev, sizeof(*prl), GFP_KERNEL);
|
||||
if (!prl)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, prl);
|
||||
|
||||
prl->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
prl->base = base;
|
||||
|
||||
prl->rst = devm_reset_control_get(&pdev->dev, "rst");
|
||||
if (IS_ERR(prl->rst)) {
|
||||
if (PTR_ERR(prl->rst) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "No top level reset found\n");
|
||||
return PTR_ERR(prl->rst);
|
||||
}
|
||||
|
||||
prl->clk_sys = devm_clk_get(&pdev->dev, "sys");
|
||||
if (IS_ERR(prl->clk_sys)) {
|
||||
if (PTR_ERR(prl->clk_sys) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to acquire clock 'sys'\n");
|
||||
return PTR_ERR(prl->clk_sys);
|
||||
}
|
||||
|
||||
prl->clk_ref = devm_clk_get(&pdev->dev, "ref");
|
||||
if (IS_ERR(prl->clk_ref)) {
|
||||
if (PTR_ERR(prl->clk_ref) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to acquire clock 'ref'\n");
|
||||
return PTR_ERR(prl->clk_ref);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(prl->clk_sys);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
img_prl_out_writel(prl, IMG_PRL_OUT_CTL_EDGE_MASK, IMG_PRL_OUT_CTL);
|
||||
img_prl_out_reset(prl);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
if (!pm_runtime_enabled(&pdev->dev)) {
|
||||
ret = img_prl_out_resume(&pdev->dev);
|
||||
if (ret)
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
prl->dma_data.addr = res->start + IMG_PRL_OUT_TX_FIFO;
|
||||
prl->dma_data.addr_width = 4;
|
||||
prl->dma_data.maxburst = 4;
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||
&img_prl_out_component,
|
||||
&img_prl_out_dai, 1);
|
||||
if (ret)
|
||||
goto err_suspend;
|
||||
|
||||
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
|
||||
if (ret)
|
||||
goto err_suspend;
|
||||
|
||||
return 0;
|
||||
|
||||
err_suspend:
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
img_prl_out_suspend(&pdev->dev);
|
||||
err_pm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
clk_disable_unprepare(prl->clk_sys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int img_prl_out_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct img_prl_out *prl = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
img_prl_out_suspend(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(prl->clk_sys);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id img_prl_out_of_match[] = {
|
||||
{ .compatible = "img,parallel-out" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, img_prl_out_of_match);
|
||||
|
||||
static const struct dev_pm_ops img_prl_out_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(img_prl_out_suspend,
|
||||
img_prl_out_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver img_prl_out_driver = {
|
||||
.driver = {
|
||||
.name = "img-parallel-out",
|
||||
.of_match_table = img_prl_out_of_match,
|
||||
.pm = &img_prl_out_pm_ops
|
||||
},
|
||||
.probe = img_prl_out_probe,
|
||||
.remove = img_prl_out_dev_remove
|
||||
};
|
||||
module_platform_driver(img_prl_out_driver);
|
||||
|
||||
MODULE_AUTHOR("Damien Horsley <[email protected]>");
|
||||
MODULE_DESCRIPTION("IMG Parallel Output Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
806
sound/soc/img/img-spdif-in.c
Normal file
806
sound/soc/img/img-spdif-in.c
Normal file
|
|
@ -0,0 +1,806 @@
|
|||
/*
|
||||
* IMG SPDIF input controller driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define IMG_SPDIF_IN_RX_FIFO_OFFSET 0
|
||||
|
||||
#define IMG_SPDIF_IN_CTL 0x4
|
||||
#define IMG_SPDIF_IN_CTL_LOCKLO_MASK 0xff
|
||||
#define IMG_SPDIF_IN_CTL_LOCKLO_SHIFT 0
|
||||
#define IMG_SPDIF_IN_CTL_LOCKHI_MASK 0xff00
|
||||
#define IMG_SPDIF_IN_CTL_LOCKHI_SHIFT 8
|
||||
#define IMG_SPDIF_IN_CTL_TRK_MASK 0xff0000
|
||||
#define IMG_SPDIF_IN_CTL_TRK_SHIFT 16
|
||||
#define IMG_SPDIF_IN_CTL_SRD_MASK 0x70000000
|
||||
#define IMG_SPDIF_IN_CTL_SRD_SHIFT 28
|
||||
#define IMG_SPDIF_IN_CTL_SRT_MASK BIT(31)
|
||||
|
||||
#define IMG_SPDIF_IN_STATUS 0x8
|
||||
#define IMG_SPDIF_IN_STATUS_SAM_MASK 0x7000
|
||||
#define IMG_SPDIF_IN_STATUS_SAM_SHIFT 12
|
||||
#define IMG_SPDIF_IN_STATUS_LOCK_MASK BIT(15)
|
||||
#define IMG_SPDIF_IN_STATUS_LOCK_SHIFT 15
|
||||
|
||||
#define IMG_SPDIF_IN_CLKGEN 0x1c
|
||||
#define IMG_SPDIF_IN_CLKGEN_NOM_MASK 0x3ff
|
||||
#define IMG_SPDIF_IN_CLKGEN_NOM_SHIFT 0
|
||||
#define IMG_SPDIF_IN_CLKGEN_HLD_MASK 0x3ff0000
|
||||
#define IMG_SPDIF_IN_CLKGEN_HLD_SHIFT 16
|
||||
|
||||
#define IMG_SPDIF_IN_CSL 0x20
|
||||
|
||||
#define IMG_SPDIF_IN_CSH 0x24
|
||||
#define IMG_SPDIF_IN_CSH_MASK 0xff
|
||||
#define IMG_SPDIF_IN_CSH_SHIFT 0
|
||||
|
||||
#define IMG_SPDIF_IN_SOFT_RESET 0x28
|
||||
#define IMG_SPDIF_IN_SOFT_RESET_MASK BIT(0)
|
||||
|
||||
#define IMG_SPDIF_IN_ACLKGEN_START 0x2c
|
||||
#define IMG_SPDIF_IN_ACLKGEN_NOM_MASK 0x3ff
|
||||
#define IMG_SPDIF_IN_ACLKGEN_NOM_SHIFT 0
|
||||
#define IMG_SPDIF_IN_ACLKGEN_HLD_MASK 0xffc00
|
||||
#define IMG_SPDIF_IN_ACLKGEN_HLD_SHIFT 10
|
||||
#define IMG_SPDIF_IN_ACLKGEN_TRK_MASK 0xff00000
|
||||
#define IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT 20
|
||||
|
||||
#define IMG_SPDIF_IN_NUM_ACLKGEN 4
|
||||
|
||||
struct img_spdif_in {
|
||||
spinlock_t lock;
|
||||
void __iomem *base;
|
||||
struct clk *clk_sys;
|
||||
struct snd_dmaengine_dai_dma_data dma_data;
|
||||
struct device *dev;
|
||||
unsigned int trk;
|
||||
bool multi_freq;
|
||||
int lock_acquire;
|
||||
int lock_release;
|
||||
unsigned int single_freq;
|
||||
unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN];
|
||||
bool active;
|
||||
|
||||
/* Write-only registers */
|
||||
unsigned int aclkgen_regs[IMG_SPDIF_IN_NUM_ACLKGEN];
|
||||
};
|
||||
|
||||
static inline void img_spdif_in_writel(struct img_spdif_in *spdif,
|
||||
u32 val, u32 reg)
|
||||
{
|
||||
writel(val, spdif->base + reg);
|
||||
}
|
||||
|
||||
static inline u32 img_spdif_in_readl(struct img_spdif_in *spdif, u32 reg)
|
||||
{
|
||||
return readl(spdif->base + reg);
|
||||
}
|
||||
|
||||
static inline void img_spdif_in_aclkgen_writel(struct img_spdif_in *spdif,
|
||||
u32 index)
|
||||
{
|
||||
img_spdif_in_writel(spdif, spdif->aclkgen_regs[index],
|
||||
IMG_SPDIF_IN_ACLKGEN_START + (index * 0x4));
|
||||
}
|
||||
|
||||
static int img_spdif_in_check_max_rate(struct img_spdif_in *spdif,
|
||||
unsigned int sample_rate, unsigned long *actual_freq)
|
||||
{
|
||||
unsigned long min_freq, freq_t;
|
||||
|
||||
/* Clock rate must be at least 24x the bit rate */
|
||||
min_freq = sample_rate * 2 * 32 * 24;
|
||||
|
||||
freq_t = clk_get_rate(spdif->clk_sys);
|
||||
|
||||
if (freq_t < min_freq)
|
||||
return -EINVAL;
|
||||
|
||||
*actual_freq = freq_t;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_do_clkgen_calc(unsigned int rate, unsigned int *pnom,
|
||||
unsigned int *phld, unsigned long clk_rate)
|
||||
{
|
||||
unsigned int ori, nom, hld;
|
||||
|
||||
/*
|
||||
* Calculate oversampling ratio, nominal phase increment and hold
|
||||
* increment for the given rate / frequency
|
||||
*/
|
||||
|
||||
if (!rate)
|
||||
return -EINVAL;
|
||||
|
||||
ori = clk_rate / (rate * 64);
|
||||
|
||||
if (!ori)
|
||||
return -EINVAL;
|
||||
|
||||
nom = (4096 / ori) + 1;
|
||||
do
|
||||
hld = 4096 - (--nom * (ori - 1));
|
||||
while (hld < 120);
|
||||
|
||||
*pnom = nom;
|
||||
*phld = hld;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_do_clkgen_single(struct img_spdif_in *spdif,
|
||||
unsigned int rate)
|
||||
{
|
||||
unsigned int nom, hld;
|
||||
unsigned long flags, clk_rate;
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
ret = img_spdif_in_check_max_rate(spdif, rate, &clk_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = img_spdif_in_do_clkgen_calc(rate, &nom, &hld, clk_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = (nom << IMG_SPDIF_IN_CLKGEN_NOM_SHIFT) &
|
||||
IMG_SPDIF_IN_CLKGEN_NOM_MASK;
|
||||
reg |= (hld << IMG_SPDIF_IN_CLKGEN_HLD_SHIFT) &
|
||||
IMG_SPDIF_IN_CLKGEN_HLD_MASK;
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
if (spdif->active) {
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CLKGEN);
|
||||
|
||||
spdif->single_freq = rate;
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_do_clkgen_multi(struct img_spdif_in *spdif,
|
||||
unsigned int multi_freqs[])
|
||||
{
|
||||
unsigned int nom, hld, rate, max_rate = 0;
|
||||
unsigned long flags, clk_rate;
|
||||
int i, ret = 0;
|
||||
u32 reg, trk_reg, temp_regs[IMG_SPDIF_IN_NUM_ACLKGEN];
|
||||
|
||||
for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++)
|
||||
if (multi_freqs[i] > max_rate)
|
||||
max_rate = multi_freqs[i];
|
||||
|
||||
ret = img_spdif_in_check_max_rate(spdif, max_rate, &clk_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) {
|
||||
rate = multi_freqs[i];
|
||||
|
||||
ret = img_spdif_in_do_clkgen_calc(rate, &nom, &hld, clk_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = (nom << IMG_SPDIF_IN_ACLKGEN_NOM_SHIFT) &
|
||||
IMG_SPDIF_IN_ACLKGEN_NOM_MASK;
|
||||
reg |= (hld << IMG_SPDIF_IN_ACLKGEN_HLD_SHIFT) &
|
||||
IMG_SPDIF_IN_ACLKGEN_HLD_MASK;
|
||||
temp_regs[i] = reg;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
if (spdif->active) {
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
trk_reg = spdif->trk << IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT;
|
||||
|
||||
for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) {
|
||||
spdif->aclkgen_regs[i] = temp_regs[i] | trk_reg;
|
||||
img_spdif_in_aclkgen_writel(spdif, i);
|
||||
}
|
||||
|
||||
spdif->multi_freq = true;
|
||||
spdif->multi_freqs[0] = multi_freqs[0];
|
||||
spdif->multi_freqs[1] = multi_freqs[1];
|
||||
spdif->multi_freqs[2] = multi_freqs[2];
|
||||
spdif->multi_freqs[3] = multi_freqs[3];
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_iec958_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
||||
uinfo->count = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_get_status_mask(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.iec958.status[0] = 0xff;
|
||||
ucontrol->value.iec958.status[1] = 0xff;
|
||||
ucontrol->value.iec958.status[2] = 0xff;
|
||||
ucontrol->value.iec958.status[3] = 0xff;
|
||||
ucontrol->value.iec958.status[4] = 0xff;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_get_status(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
u32 reg;
|
||||
|
||||
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CSL);
|
||||
ucontrol->value.iec958.status[0] = reg & 0xff;
|
||||
ucontrol->value.iec958.status[1] = (reg >> 8) & 0xff;
|
||||
ucontrol->value.iec958.status[2] = (reg >> 16) & 0xff;
|
||||
ucontrol->value.iec958.status[3] = (reg >> 24) & 0xff;
|
||||
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CSH);
|
||||
ucontrol->value.iec958.status[4] = (reg & IMG_SPDIF_IN_CSH_MASK)
|
||||
>> IMG_SPDIF_IN_CSH_SHIFT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_info_multi_freq(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = IMG_SPDIF_IN_NUM_ACLKGEN;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = LONG_MAX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_get_multi_freq(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
if (spdif->multi_freq) {
|
||||
ucontrol->value.integer.value[0] = spdif->multi_freqs[0];
|
||||
ucontrol->value.integer.value[1] = spdif->multi_freqs[1];
|
||||
ucontrol->value.integer.value[2] = spdif->multi_freqs[2];
|
||||
ucontrol->value.integer.value[3] = spdif->multi_freqs[3];
|
||||
} else {
|
||||
ucontrol->value.integer.value[0] = 0;
|
||||
ucontrol->value.integer.value[1] = 0;
|
||||
ucontrol->value.integer.value[2] = 0;
|
||||
ucontrol->value.integer.value[3] = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_set_multi_freq(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN];
|
||||
bool multi_freq;
|
||||
unsigned long flags;
|
||||
|
||||
if ((ucontrol->value.integer.value[0] == 0) &&
|
||||
(ucontrol->value.integer.value[1] == 0) &&
|
||||
(ucontrol->value.integer.value[2] == 0) &&
|
||||
(ucontrol->value.integer.value[3] == 0)) {
|
||||
multi_freq = false;
|
||||
} else {
|
||||
multi_freqs[0] = ucontrol->value.integer.value[0];
|
||||
multi_freqs[1] = ucontrol->value.integer.value[1];
|
||||
multi_freqs[2] = ucontrol->value.integer.value[2];
|
||||
multi_freqs[3] = ucontrol->value.integer.value[3];
|
||||
multi_freq = true;
|
||||
}
|
||||
|
||||
if (multi_freq)
|
||||
return img_spdif_in_do_clkgen_multi(spdif, multi_freqs);
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
if (spdif->active) {
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
spdif->multi_freq = false;
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_info_lock_freq(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = LONG_MAX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_get_lock_freq(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *uc)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
u32 reg;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_STATUS);
|
||||
if (reg & IMG_SPDIF_IN_STATUS_LOCK_MASK) {
|
||||
if (spdif->multi_freq) {
|
||||
i = ((reg & IMG_SPDIF_IN_STATUS_SAM_MASK) >>
|
||||
IMG_SPDIF_IN_STATUS_SAM_SHIFT) - 1;
|
||||
uc->value.integer.value[0] = spdif->multi_freqs[i];
|
||||
} else {
|
||||
uc->value.integer.value[0] = spdif->single_freq;
|
||||
}
|
||||
} else {
|
||||
uc->value.integer.value[0] = 0;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_info_trk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 255;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_get_trk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
ucontrol->value.integer.value[0] = spdif->trk;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_set_trk(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
unsigned long flags;
|
||||
int i;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
if (spdif->active) {
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
spdif->trk = ucontrol->value.integer.value[0];
|
||||
|
||||
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
|
||||
reg &= ~IMG_SPDIF_IN_CTL_TRK_MASK;
|
||||
reg |= spdif->trk << IMG_SPDIF_IN_CTL_TRK_SHIFT;
|
||||
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
|
||||
|
||||
for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) {
|
||||
spdif->aclkgen_regs[i] = (spdif->aclkgen_regs[i] &
|
||||
~IMG_SPDIF_IN_ACLKGEN_TRK_MASK) |
|
||||
(spdif->trk << IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT);
|
||||
|
||||
img_spdif_in_aclkgen_writel(spdif, i);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_info_lock(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = -128;
|
||||
uinfo->value.integer.max = 127;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_get_lock_acquire(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
ucontrol->value.integer.value[0] = spdif->lock_acquire;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_set_lock_acquire(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
if (spdif->active) {
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
spdif->lock_acquire = ucontrol->value.integer.value[0];
|
||||
|
||||
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
|
||||
reg &= ~IMG_SPDIF_IN_CTL_LOCKHI_MASK;
|
||||
reg |= (spdif->lock_acquire << IMG_SPDIF_IN_CTL_LOCKHI_SHIFT) &
|
||||
IMG_SPDIF_IN_CTL_LOCKHI_MASK;
|
||||
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_get_lock_release(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
|
||||
ucontrol->value.integer.value[0] = spdif->lock_release;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_in_set_lock_release(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
if (spdif->active) {
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
spdif->lock_release = ucontrol->value.integer.value[0];
|
||||
|
||||
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
|
||||
reg &= ~IMG_SPDIF_IN_CTL_LOCKLO_MASK;
|
||||
reg |= (spdif->lock_release << IMG_SPDIF_IN_CTL_LOCKLO_SHIFT) &
|
||||
IMG_SPDIF_IN_CTL_LOCKLO_MASK;
|
||||
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new img_spdif_in_controls[] = {
|
||||
{
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
|
||||
.info = img_spdif_in_iec958_info,
|
||||
.get = img_spdif_in_get_status_mask
|
||||
},
|
||||
{
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
|
||||
.info = img_spdif_in_iec958_info,
|
||||
.get = img_spdif_in_get_status
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "SPDIF In Multi Frequency Acquire",
|
||||
.info = img_spdif_in_info_multi_freq,
|
||||
.get = img_spdif_in_get_multi_freq,
|
||||
.put = img_spdif_in_set_multi_freq
|
||||
},
|
||||
{
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "SPDIF In Lock Frequency",
|
||||
.info = img_spdif_in_info_lock_freq,
|
||||
.get = img_spdif_in_get_lock_freq
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "SPDIF In Lock TRK",
|
||||
.info = img_spdif_in_info_trk,
|
||||
.get = img_spdif_in_get_trk,
|
||||
.put = img_spdif_in_set_trk
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "SPDIF In Lock Acquire Threshold",
|
||||
.info = img_spdif_in_info_lock,
|
||||
.get = img_spdif_in_get_lock_acquire,
|
||||
.put = img_spdif_in_set_lock_acquire
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "SPDIF In Lock Release Threshold",
|
||||
.info = img_spdif_in_info_lock,
|
||||
.get = img_spdif_in_get_lock_release,
|
||||
.put = img_spdif_in_set_lock_release
|
||||
}
|
||||
};
|
||||
|
||||
static int img_spdif_in_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai);
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
|
||||
if (spdif->multi_freq)
|
||||
reg &= ~IMG_SPDIF_IN_CTL_SRD_MASK;
|
||||
else
|
||||
reg |= (1UL << IMG_SPDIF_IN_CTL_SRD_SHIFT);
|
||||
reg |= IMG_SPDIF_IN_CTL_SRT_MASK;
|
||||
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
|
||||
spdif->active = true;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL);
|
||||
reg &= ~IMG_SPDIF_IN_CTL_SRT_MASK;
|
||||
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
|
||||
spdif->active = false;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int img_spdif_in_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int rate, channels;
|
||||
snd_pcm_format_t format;
|
||||
|
||||
rate = params_rate(params);
|
||||
channels = params_channels(params);
|
||||
format = params_format(params);
|
||||
|
||||
if (format != SNDRV_PCM_FORMAT_S32_LE)
|
||||
return -EINVAL;
|
||||
|
||||
if (channels != 2)
|
||||
return -EINVAL;
|
||||
|
||||
return img_spdif_in_do_clkgen_single(spdif, rate);
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops img_spdif_in_dai_ops = {
|
||||
.trigger = img_spdif_in_trigger,
|
||||
.hw_params = img_spdif_in_hw_params
|
||||
};
|
||||
|
||||
static int img_spdif_in_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, NULL, &spdif->dma_data);
|
||||
|
||||
snd_soc_add_dai_controls(dai, img_spdif_in_controls,
|
||||
ARRAY_SIZE(img_spdif_in_controls));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_driver img_spdif_in_dai = {
|
||||
.probe = img_spdif_in_dai_probe,
|
||||
.capture = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE
|
||||
},
|
||||
.ops = &img_spdif_in_dai_ops
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver img_spdif_in_component = {
|
||||
.name = "img-spdif-in"
|
||||
};
|
||||
|
||||
static int img_spdif_in_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct img_spdif_in *spdif;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
struct reset_control *rst;
|
||||
u32 reg;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
|
||||
if (!spdif)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, spdif);
|
||||
|
||||
spdif->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
spdif->base = base;
|
||||
|
||||
spdif->clk_sys = devm_clk_get(dev, "sys");
|
||||
if (IS_ERR(spdif->clk_sys)) {
|
||||
if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to acquire clock 'sys'\n");
|
||||
return PTR_ERR(spdif->clk_sys);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(spdif->clk_sys);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rst = devm_reset_control_get(&pdev->dev, "rst");
|
||||
if (IS_ERR(rst)) {
|
||||
if (PTR_ERR(rst) == -EPROBE_DEFER) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto err_clk_disable;
|
||||
}
|
||||
dev_dbg(dev, "No top level reset found\n");
|
||||
img_spdif_in_writel(spdif, IMG_SPDIF_IN_SOFT_RESET_MASK,
|
||||
IMG_SPDIF_IN_SOFT_RESET);
|
||||
img_spdif_in_writel(spdif, 0, IMG_SPDIF_IN_SOFT_RESET);
|
||||
} else {
|
||||
reset_control_assert(rst);
|
||||
reset_control_deassert(rst);
|
||||
}
|
||||
|
||||
spin_lock_init(&spdif->lock);
|
||||
|
||||
spdif->dma_data.addr = res->start + IMG_SPDIF_IN_RX_FIFO_OFFSET;
|
||||
spdif->dma_data.addr_width = 4;
|
||||
spdif->dma_data.maxburst = 4;
|
||||
spdif->trk = 0x80;
|
||||
spdif->lock_acquire = 4;
|
||||
spdif->lock_release = -128;
|
||||
|
||||
reg = (spdif->lock_acquire << IMG_SPDIF_IN_CTL_LOCKHI_SHIFT) &
|
||||
IMG_SPDIF_IN_CTL_LOCKHI_MASK;
|
||||
reg |= (spdif->lock_release << IMG_SPDIF_IN_CTL_LOCKLO_SHIFT) &
|
||||
IMG_SPDIF_IN_CTL_LOCKLO_MASK;
|
||||
reg |= (spdif->trk << IMG_SPDIF_IN_CTL_TRK_SHIFT) &
|
||||
IMG_SPDIF_IN_CTL_TRK_MASK;
|
||||
img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL);
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||
&img_spdif_in_component, &img_spdif_in_dai, 1);
|
||||
if (ret)
|
||||
goto err_clk_disable;
|
||||
|
||||
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
|
||||
if (ret)
|
||||
goto err_clk_disable;
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(spdif->clk_sys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int img_spdif_in_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct img_spdif_in *spdif = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(spdif->clk_sys);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id img_spdif_in_of_match[] = {
|
||||
{ .compatible = "img,spdif-in" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, img_spdif_in_of_match);
|
||||
|
||||
static struct platform_driver img_spdif_in_driver = {
|
||||
.driver = {
|
||||
.name = "img-spdif-in",
|
||||
.of_match_table = img_spdif_in_of_match
|
||||
},
|
||||
.probe = img_spdif_in_probe,
|
||||
.remove = img_spdif_in_dev_remove
|
||||
};
|
||||
module_platform_driver(img_spdif_in_driver);
|
||||
|
||||
MODULE_AUTHOR("Damien Horsley <[email protected]>");
|
||||
MODULE_DESCRIPTION("IMG SPDIF Input driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
441
sound/soc/img/img-spdif-out.c
Normal file
441
sound/soc/img/img-spdif-out.c
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
/*
|
||||
* IMG SPDIF output controller driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define IMG_SPDIF_OUT_TX_FIFO 0x0
|
||||
|
||||
#define IMG_SPDIF_OUT_CTL 0x4
|
||||
#define IMG_SPDIF_OUT_CTL_FS_MASK BIT(4)
|
||||
#define IMG_SPDIF_OUT_CTL_CLK_MASK BIT(2)
|
||||
#define IMG_SPDIF_OUT_CTL_SRT_MASK BIT(0)
|
||||
|
||||
#define IMG_SPDIF_OUT_CSL 0x14
|
||||
|
||||
#define IMG_SPDIF_OUT_CSH_UV 0x18
|
||||
#define IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT 0
|
||||
#define IMG_SPDIF_OUT_CSH_UV_CSH_MASK 0xff
|
||||
|
||||
struct img_spdif_out {
|
||||
spinlock_t lock;
|
||||
void __iomem *base;
|
||||
struct clk *clk_sys;
|
||||
struct clk *clk_ref;
|
||||
struct snd_dmaengine_dai_dma_data dma_data;
|
||||
struct device *dev;
|
||||
struct reset_control *rst;
|
||||
};
|
||||
|
||||
static int img_spdif_out_suspend(struct device *dev)
|
||||
{
|
||||
struct img_spdif_out *spdif = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(spdif->clk_ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_out_resume(struct device *dev)
|
||||
{
|
||||
struct img_spdif_out *spdif = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(spdif->clk_ref);
|
||||
if (ret) {
|
||||
dev_err(dev, "clk_enable failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void img_spdif_out_writel(struct img_spdif_out *spdif, u32 val,
|
||||
u32 reg)
|
||||
{
|
||||
writel(val, spdif->base + reg);
|
||||
}
|
||||
|
||||
static inline u32 img_spdif_out_readl(struct img_spdif_out *spdif, u32 reg)
|
||||
{
|
||||
return readl(spdif->base + reg);
|
||||
}
|
||||
|
||||
static void img_spdif_out_reset(struct img_spdif_out *spdif)
|
||||
{
|
||||
u32 ctl, status_low, status_high;
|
||||
|
||||
ctl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL) &
|
||||
~IMG_SPDIF_OUT_CTL_SRT_MASK;
|
||||
status_low = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL);
|
||||
status_high = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV);
|
||||
|
||||
reset_control_assert(spdif->rst);
|
||||
reset_control_deassert(spdif->rst);
|
||||
|
||||
img_spdif_out_writel(spdif, ctl, IMG_SPDIF_OUT_CTL);
|
||||
img_spdif_out_writel(spdif, status_low, IMG_SPDIF_OUT_CSL);
|
||||
img_spdif_out_writel(spdif, status_high, IMG_SPDIF_OUT_CSH_UV);
|
||||
}
|
||||
|
||||
static int img_spdif_out_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
||||
uinfo->count = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_out_get_status_mask(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
ucontrol->value.iec958.status[0] = 0xff;
|
||||
ucontrol->value.iec958.status[1] = 0xff;
|
||||
ucontrol->value.iec958.status[2] = 0xff;
|
||||
ucontrol->value.iec958.status[3] = 0xff;
|
||||
ucontrol->value.iec958.status[4] = 0xff;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_out_get_status(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
u32 reg;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL);
|
||||
ucontrol->value.iec958.status[0] = reg & 0xff;
|
||||
ucontrol->value.iec958.status[1] = (reg >> 8) & 0xff;
|
||||
ucontrol->value.iec958.status[2] = (reg >> 16) & 0xff;
|
||||
ucontrol->value.iec958.status[3] = (reg >> 24) & 0xff;
|
||||
|
||||
reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV);
|
||||
ucontrol->value.iec958.status[4] =
|
||||
(reg & IMG_SPDIF_OUT_CSH_UV_CSH_MASK) >>
|
||||
IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT;
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_out_set_status(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
|
||||
struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
u32 reg;
|
||||
unsigned long flags;
|
||||
|
||||
reg = ((u32)ucontrol->value.iec958.status[3] << 24);
|
||||
reg |= ((u32)ucontrol->value.iec958.status[2] << 16);
|
||||
reg |= ((u32)ucontrol->value.iec958.status[1] << 8);
|
||||
reg |= (u32)ucontrol->value.iec958.status[0];
|
||||
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
|
||||
img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CSL);
|
||||
|
||||
reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV);
|
||||
reg &= ~IMG_SPDIF_OUT_CSH_UV_CSH_MASK;
|
||||
reg |= (u32)ucontrol->value.iec958.status[4] <<
|
||||
IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT;
|
||||
img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CSH_UV);
|
||||
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new img_spdif_out_controls[] = {
|
||||
{
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
|
||||
.info = img_spdif_out_info,
|
||||
.get = img_spdif_out_get_status_mask
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
|
||||
.info = img_spdif_out_info,
|
||||
.get = img_spdif_out_get_status,
|
||||
.put = img_spdif_out_set_status
|
||||
}
|
||||
};
|
||||
|
||||
static int img_spdif_out_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai);
|
||||
u32 reg;
|
||||
unsigned long flags;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL);
|
||||
reg |= IMG_SPDIF_OUT_CTL_SRT_MASK;
|
||||
img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CTL);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
spin_lock_irqsave(&spdif->lock, flags);
|
||||
img_spdif_out_reset(spdif);
|
||||
spin_unlock_irqrestore(&spdif->lock, flags);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_spdif_out_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int channels;
|
||||
long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate;
|
||||
u32 reg;
|
||||
snd_pcm_format_t format;
|
||||
|
||||
rate = params_rate(params);
|
||||
format = params_format(params);
|
||||
channels = params_channels(params);
|
||||
|
||||
dev_dbg(spdif->dev, "hw_params rate %ld channels %u format %u\n",
|
||||
rate, channels, format);
|
||||
|
||||
if (format != SNDRV_PCM_FORMAT_S32_LE)
|
||||
return -EINVAL;
|
||||
|
||||
if (channels != 2)
|
||||
return -EINVAL;
|
||||
|
||||
pre_div_a = clk_round_rate(spdif->clk_ref, rate * 256);
|
||||
if (pre_div_a < 0)
|
||||
return pre_div_a;
|
||||
pre_div_b = clk_round_rate(spdif->clk_ref, rate * 384);
|
||||
if (pre_div_b < 0)
|
||||
return pre_div_b;
|
||||
|
||||
diff_a = abs((pre_div_a / 256) - rate);
|
||||
diff_b = abs((pre_div_b / 384) - rate);
|
||||
|
||||
/* If diffs are equal, use lower clock rate */
|
||||
if (diff_a > diff_b)
|
||||
clk_set_rate(spdif->clk_ref, pre_div_b);
|
||||
else
|
||||
clk_set_rate(spdif->clk_ref, pre_div_a);
|
||||
|
||||
/*
|
||||
* Another driver (eg machine driver) may have rejected the above
|
||||
* change. Get the current rate and set the register bit according to
|
||||
* the new min diff
|
||||
*/
|
||||
clk_rate = clk_get_rate(spdif->clk_ref);
|
||||
|
||||
diff_a = abs((clk_rate / 256) - rate);
|
||||
diff_b = abs((clk_rate / 384) - rate);
|
||||
|
||||
reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL);
|
||||
if (diff_a <= diff_b)
|
||||
reg &= ~IMG_SPDIF_OUT_CTL_CLK_MASK;
|
||||
else
|
||||
reg |= IMG_SPDIF_OUT_CTL_CLK_MASK;
|
||||
img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops img_spdif_out_dai_ops = {
|
||||
.trigger = img_spdif_out_trigger,
|
||||
.hw_params = img_spdif_out_hw_params
|
||||
};
|
||||
|
||||
static int img_spdif_out_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, &spdif->dma_data, NULL);
|
||||
|
||||
snd_soc_add_dai_controls(dai, img_spdif_out_controls,
|
||||
ARRAY_SIZE(img_spdif_out_controls));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_driver img_spdif_out_dai = {
|
||||
.probe = img_spdif_out_dai_probe,
|
||||
.playback = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE
|
||||
},
|
||||
.ops = &img_spdif_out_dai_ops
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver img_spdif_out_component = {
|
||||
.name = "img-spdif-out"
|
||||
};
|
||||
|
||||
static int img_spdif_out_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct img_spdif_out *spdif;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
|
||||
if (!spdif)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, spdif);
|
||||
|
||||
spdif->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
spdif->base = base;
|
||||
|
||||
spdif->rst = devm_reset_control_get(&pdev->dev, "rst");
|
||||
if (IS_ERR(spdif->rst)) {
|
||||
if (PTR_ERR(spdif->rst) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "No top level reset found\n");
|
||||
return PTR_ERR(spdif->rst);
|
||||
}
|
||||
|
||||
spdif->clk_sys = devm_clk_get(&pdev->dev, "sys");
|
||||
if (IS_ERR(spdif->clk_sys)) {
|
||||
if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to acquire clock 'sys'\n");
|
||||
return PTR_ERR(spdif->clk_sys);
|
||||
}
|
||||
|
||||
spdif->clk_ref = devm_clk_get(&pdev->dev, "ref");
|
||||
if (IS_ERR(spdif->clk_ref)) {
|
||||
if (PTR_ERR(spdif->clk_ref) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to acquire clock 'ref'\n");
|
||||
return PTR_ERR(spdif->clk_ref);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(spdif->clk_sys);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK,
|
||||
IMG_SPDIF_OUT_CTL);
|
||||
|
||||
img_spdif_out_reset(spdif);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
if (!pm_runtime_enabled(&pdev->dev)) {
|
||||
ret = img_spdif_out_resume(&pdev->dev);
|
||||
if (ret)
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
spin_lock_init(&spdif->lock);
|
||||
|
||||
spdif->dma_data.addr = res->start + IMG_SPDIF_OUT_TX_FIFO;
|
||||
spdif->dma_data.addr_width = 4;
|
||||
spdif->dma_data.maxburst = 4;
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||
&img_spdif_out_component,
|
||||
&img_spdif_out_dai, 1);
|
||||
if (ret)
|
||||
goto err_suspend;
|
||||
|
||||
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
|
||||
if (ret)
|
||||
goto err_suspend;
|
||||
|
||||
dev_dbg(&pdev->dev, "Probe successful\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_suspend:
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
img_spdif_out_suspend(&pdev->dev);
|
||||
err_pm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
clk_disable_unprepare(spdif->clk_sys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int img_spdif_out_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct img_spdif_out *spdif = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
img_spdif_out_suspend(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(spdif->clk_sys);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id img_spdif_out_of_match[] = {
|
||||
{ .compatible = "img,spdif-out" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, img_spdif_out_of_match);
|
||||
|
||||
static const struct dev_pm_ops img_spdif_out_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(img_spdif_out_suspend,
|
||||
img_spdif_out_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver img_spdif_out_driver = {
|
||||
.driver = {
|
||||
.name = "img-spdif-out",
|
||||
.of_match_table = img_spdif_out_of_match,
|
||||
.pm = &img_spdif_out_pm_ops
|
||||
},
|
||||
.probe = img_spdif_out_probe,
|
||||
.remove = img_spdif_out_dev_remove
|
||||
};
|
||||
module_platform_driver(img_spdif_out_driver);
|
||||
|
||||
MODULE_AUTHOR("Damien Horsley <[email protected]>");
|
||||
MODULE_DESCRIPTION("IMG SPDIF Output driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
287
sound/soc/img/pistachio-internal-dac.c
Normal file
287
sound/soc/img/pistachio-internal-dac.c
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
* Pistachio internal dac driver
|
||||
*
|
||||
* Copyright (C) 2015 Imagination Technologies Ltd.
|
||||
*
|
||||
* Author: Damien Horsley <Damien.Horsley@imgtec.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define PISTACHIO_INTERNAL_DAC_CTRL 0x40
|
||||
#define PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK 0x2
|
||||
#define PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK 0x1
|
||||
|
||||
#define PISTACHIO_INTERNAL_DAC_SRST 0x44
|
||||
#define PISTACHIO_INTERNAL_DAC_SRST_MASK 0x1
|
||||
|
||||
#define PISTACHIO_INTERNAL_DAC_GTI_CTRL 0x48
|
||||
#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_SHIFT 0
|
||||
#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_MASK 0xFFF
|
||||
#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK 0x1000
|
||||
#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_SHIFT 13
|
||||
#define PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_MASK 0x1FE000
|
||||
|
||||
#define PISTACHIO_INTERNAL_DAC_PWR 0x1
|
||||
#define PISTACHIO_INTERNAL_DAC_PWR_MASK 0x1
|
||||
|
||||
#define PISTACHIO_INTERNAL_DAC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
/* codec private data */
|
||||
struct pistachio_internal_dac {
|
||||
struct regmap *regmap;
|
||||
struct regulator *supply;
|
||||
bool mute;
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new pistachio_internal_dac_snd_controls[] = {
|
||||
SOC_SINGLE("Playback Switch", PISTACHIO_INTERNAL_DAC_CTRL, 2, 1, 1)
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget pistachio_internal_dac_widgets[] = {
|
||||
SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_OUTPUT("AOUTL"),
|
||||
SND_SOC_DAPM_OUTPUT("AOUTR"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route pistachio_internal_dac_routes[] = {
|
||||
{ "AOUTL", NULL, "DAC" },
|
||||
{ "AOUTR", NULL, "DAC" },
|
||||
};
|
||||
|
||||
static void pistachio_internal_dac_reg_writel(struct regmap *top_regs,
|
||||
u32 val, u32 reg)
|
||||
{
|
||||
regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
|
||||
PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_MASK,
|
||||
reg << PISTACHIO_INTERNAL_DAC_GTI_CTRL_ADDR_SHIFT);
|
||||
|
||||
regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
|
||||
PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_MASK,
|
||||
val << PISTACHIO_INTERNAL_DAC_GTI_CTRL_WDATA_SHIFT);
|
||||
|
||||
regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
|
||||
PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK,
|
||||
PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK);
|
||||
|
||||
regmap_update_bits(top_regs, PISTACHIO_INTERNAL_DAC_GTI_CTRL,
|
||||
PISTACHIO_INTERNAL_DAC_GTI_CTRL_WE_MASK, 0);
|
||||
}
|
||||
|
||||
static void pistachio_internal_dac_pwr_off(struct pistachio_internal_dac *dac)
|
||||
{
|
||||
regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL,
|
||||
PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK,
|
||||
PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK);
|
||||
|
||||
pistachio_internal_dac_reg_writel(dac->regmap, 0,
|
||||
PISTACHIO_INTERNAL_DAC_PWR);
|
||||
}
|
||||
|
||||
static void pistachio_internal_dac_pwr_on(struct pistachio_internal_dac *dac)
|
||||
{
|
||||
regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_SRST,
|
||||
PISTACHIO_INTERNAL_DAC_SRST_MASK,
|
||||
PISTACHIO_INTERNAL_DAC_SRST_MASK);
|
||||
|
||||
regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_SRST,
|
||||
PISTACHIO_INTERNAL_DAC_SRST_MASK, 0);
|
||||
|
||||
pistachio_internal_dac_reg_writel(dac->regmap,
|
||||
PISTACHIO_INTERNAL_DAC_PWR_MASK,
|
||||
PISTACHIO_INTERNAL_DAC_PWR);
|
||||
|
||||
regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL,
|
||||
PISTACHIO_INTERNAL_DAC_CTRL_PWRDN_MASK, 0);
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_driver pistachio_internal_dac_dais[] = {
|
||||
{
|
||||
.name = "pistachio_internal_dac",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = PISTACHIO_INTERNAL_DAC_FORMATS,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static int pistachio_internal_dac_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct pistachio_internal_dac *dac = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
snd_soc_codec_init_regmap(codec, dac->regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_codec_driver pistachio_internal_dac_driver = {
|
||||
.probe = pistachio_internal_dac_codec_probe,
|
||||
.idle_bias_off = true,
|
||||
.controls = pistachio_internal_dac_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(pistachio_internal_dac_snd_controls),
|
||||
.dapm_widgets = pistachio_internal_dac_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(pistachio_internal_dac_widgets),
|
||||
.dapm_routes = pistachio_internal_dac_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(pistachio_internal_dac_routes),
|
||||
};
|
||||
|
||||
static int pistachio_internal_dac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pistachio_internal_dac *dac;
|
||||
int ret, voltage;
|
||||
struct device *dev = &pdev->dev;
|
||||
u32 reg;
|
||||
|
||||
dac = devm_kzalloc(dev, sizeof(*dac), GFP_KERNEL);
|
||||
|
||||
if (!dac)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, dac);
|
||||
|
||||
dac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
|
||||
"img,cr-top");
|
||||
if (IS_ERR(dac->regmap))
|
||||
return PTR_ERR(dac->regmap);
|
||||
|
||||
dac->supply = devm_regulator_get(dev, "VDD");
|
||||
if (IS_ERR(dac->supply)) {
|
||||
ret = PTR_ERR(dac->supply);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to acquire supply 'VDD-supply': %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_enable(dac->supply);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable supply: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
voltage = regulator_get_voltage(dac->supply);
|
||||
|
||||
switch (voltage) {
|
||||
case 1800000:
|
||||
reg = 0;
|
||||
break;
|
||||
case 3300000:
|
||||
reg = PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "invalid voltage: %d\n", voltage);
|
||||
ret = -EINVAL;
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
regmap_update_bits(dac->regmap, PISTACHIO_INTERNAL_DAC_CTRL,
|
||||
PISTACHIO_INTERNAL_DAC_CTRL_PWR_SEL_MASK, reg);
|
||||
|
||||
pistachio_internal_dac_pwr_off(dac);
|
||||
pistachio_internal_dac_pwr_on(dac);
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_idle(dev);
|
||||
|
||||
ret = snd_soc_register_codec(dev, &pistachio_internal_dac_driver,
|
||||
pistachio_internal_dac_dais,
|
||||
ARRAY_SIZE(pistachio_internal_dac_dais));
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register codec: %d\n", ret);
|
||||
goto err_pwr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_pwr:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pistachio_internal_dac_pwr_off(dac);
|
||||
err_regulator:
|
||||
regulator_disable(dac->supply);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pistachio_internal_dac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pistachio_internal_dac *dac = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
snd_soc_unregister_codec(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pistachio_internal_dac_pwr_off(dac);
|
||||
regulator_disable(dac->supply);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pistachio_internal_dac_rt_resume(struct device *dev)
|
||||
{
|
||||
struct pistachio_internal_dac *dac = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(dac->supply);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable supply: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pistachio_internal_dac_pwr_on(dac);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pistachio_internal_dac_rt_suspend(struct device *dev)
|
||||
{
|
||||
struct pistachio_internal_dac *dac = dev_get_drvdata(dev);
|
||||
|
||||
pistachio_internal_dac_pwr_off(dac);
|
||||
|
||||
regulator_disable(dac->supply);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops pistachio_internal_dac_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(pistachio_internal_dac_rt_suspend,
|
||||
pistachio_internal_dac_rt_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id pistachio_internal_dac_of_match[] = {
|
||||
{ .compatible = "img,pistachio-internal-dac" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pistachio_internal_dac_of_match);
|
||||
|
||||
static struct platform_driver pistachio_internal_dac_plat_driver = {
|
||||
.driver = {
|
||||
.name = "img-pistachio-internal-dac",
|
||||
.of_match_table = pistachio_internal_dac_of_match,
|
||||
.pm = &pistachio_internal_dac_pm_ops
|
||||
},
|
||||
.probe = pistachio_internal_dac_probe,
|
||||
.remove = pistachio_internal_dac_remove
|
||||
};
|
||||
module_platform_driver(pistachio_internal_dac_plat_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Pistachio Internal DAC driver");
|
||||
MODULE_AUTHOR("Damien Horsley <[email protected]>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -377,6 +377,8 @@ static int sst_byt_pcm_probe(struct snd_soc_platform *platform)
|
|||
|
||||
priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data),
|
||||
GFP_KERNEL);
|
||||
if (!priv_data)
|
||||
return -ENOMEM;
|
||||
priv_data->byt = plat_data->dsp;
|
||||
snd_soc_platform_set_drvdata(platform, priv_data);
|
||||
|
||||
|
|
|
|||
|
|
@ -92,7 +92,6 @@ struct mtk_afe_memif_data {
|
|||
struct mtk_afe_memif {
|
||||
unsigned int phys_buf_addr;
|
||||
int buffer_size;
|
||||
unsigned int hw_ptr; /* Previous IRQ's HW ptr */
|
||||
struct snd_pcm_substream *substream;
|
||||
const struct mtk_afe_memif_data *data;
|
||||
const struct mtk_afe_irq_data *irqdata;
|
||||
|
|
|
|||
|
|
@ -175,8 +175,17 @@ static snd_pcm_uframes_t mtk_afe_pcm_pointer
|
|||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
|
||||
struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
|
||||
unsigned int hw_ptr;
|
||||
int ret;
|
||||
|
||||
return bytes_to_frames(substream->runtime, memif->hw_ptr);
|
||||
ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur, &hw_ptr);
|
||||
if (ret || hw_ptr == 0) {
|
||||
dev_err(afe->dev, "%s hw_ptr err\n", __func__);
|
||||
hw_ptr = memif->phys_buf_addr;
|
||||
}
|
||||
|
||||
return bytes_to_frames(substream->runtime,
|
||||
hw_ptr - memif->phys_buf_addr);
|
||||
}
|
||||
|
||||
static const struct snd_pcm_ops mtk_afe_pcm_ops = {
|
||||
|
|
@ -299,8 +308,6 @@ static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
|
|||
dev_err(afe->dev, "Failed to enable m_ck\n");
|
||||
return ret;
|
||||
}
|
||||
regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
|
||||
AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0);
|
||||
}
|
||||
|
||||
if (b_ck) {
|
||||
|
|
@ -340,12 +347,8 @@ static int mtk_afe_dais_set_clks(struct mtk_afe *afe,
|
|||
static void mtk_afe_dais_disable_clks(struct mtk_afe *afe,
|
||||
struct clk *m_ck, struct clk *b_ck)
|
||||
{
|
||||
if (m_ck) {
|
||||
regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
|
||||
AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M,
|
||||
AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M);
|
||||
if (m_ck)
|
||||
clk_disable_unprepare(m_ck);
|
||||
}
|
||||
if (b_ck)
|
||||
clk_disable_unprepare(b_ck);
|
||||
}
|
||||
|
|
@ -360,6 +363,8 @@ static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
|
||||
mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL);
|
||||
regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
|
||||
AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -373,10 +378,10 @@ static void mtk_afe_i2s_shutdown(struct snd_pcm_substream *substream,
|
|||
return;
|
||||
|
||||
mtk_afe_set_i2s_enable(afe, false);
|
||||
regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
|
||||
AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M,
|
||||
AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M);
|
||||
mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL);
|
||||
|
||||
/* disable AFE */
|
||||
regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
|
||||
}
|
||||
|
||||
static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream,
|
||||
|
|
@ -425,9 +430,6 @@ static void mtk_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
|
|||
|
||||
mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
|
||||
afe->clocks[MTK_CLK_I2S3_B]);
|
||||
|
||||
/* disable AFE */
|
||||
regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
|
||||
}
|
||||
|
||||
static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
|
||||
|
|
@ -603,7 +605,6 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
memif->phys_buf_addr = substream->runtime->dma_addr;
|
||||
memif->buffer_size = substream->runtime->dma_bytes;
|
||||
memif->hw_ptr = 0;
|
||||
|
||||
/* start */
|
||||
regmap_write(afe->regmap,
|
||||
|
|
@ -672,17 +673,6 @@ static int mtk_afe_dais_hw_free(struct snd_pcm_substream *substream,
|
|||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
static int mtk_afe_dais_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
|
||||
|
||||
/* enable AFE */
|
||||
regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
|
|
@ -738,7 +728,6 @@ static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
/* and clear pending IRQ */
|
||||
regmap_write(afe->regmap, AFE_IRQ_CLR,
|
||||
1 << memif->data->irq_clr_shift);
|
||||
memif->hw_ptr = 0;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
|
@ -751,7 +740,6 @@ static const struct snd_soc_dai_ops mtk_afe_dai_ops = {
|
|||
.shutdown = mtk_afe_dais_shutdown,
|
||||
.hw_params = mtk_afe_dais_hw_params,
|
||||
.hw_free = mtk_afe_dais_hw_free,
|
||||
.prepare = mtk_afe_dais_prepare,
|
||||
.trigger = mtk_afe_dais_trigger,
|
||||
};
|
||||
|
||||
|
|
@ -1082,7 +1070,7 @@ static const struct regmap_config mtk_afe_regmap_config = {
|
|||
static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct mtk_afe *afe = dev_id;
|
||||
unsigned int reg_value, hw_ptr;
|
||||
unsigned int reg_value;
|
||||
int i, ret;
|
||||
|
||||
ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, ®_value);
|
||||
|
|
@ -1098,13 +1086,6 @@ static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
|
|||
if (!(reg_value & (1 << memif->data->irq_clr_shift)))
|
||||
continue;
|
||||
|
||||
ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur,
|
||||
&hw_ptr);
|
||||
if (ret || hw_ptr == 0) {
|
||||
dev_err(afe->dev, "%s hw_ptr err\n", __func__);
|
||||
hw_ptr = memif->phys_buf_addr;
|
||||
}
|
||||
memif->hw_ptr = hw_ptr - memif->phys_buf_addr;
|
||||
snd_pcm_period_elapsed(memif->substream);
|
||||
}
|
||||
|
||||
|
|
@ -1119,6 +1100,9 @@ static int mtk_afe_runtime_suspend(struct device *dev)
|
|||
{
|
||||
struct mtk_afe *afe = dev_get_drvdata(dev);
|
||||
|
||||
/* disable AFE */
|
||||
regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
|
||||
|
||||
/* disable AFE clk */
|
||||
regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
|
||||
AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE);
|
||||
|
|
@ -1165,6 +1149,9 @@ static int mtk_afe_runtime_resume(struct device *dev)
|
|||
|
||||
/* unmask all IRQs */
|
||||
regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN, 0xff, 0xff);
|
||||
|
||||
/* enable AFE */
|
||||
regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
|
||||
return 0;
|
||||
|
||||
err_bck0:
|
||||
|
|
|
|||
|
|
@ -368,6 +368,8 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev)
|
|||
card->owner = THIS_MODULE;
|
||||
card->dai_link =
|
||||
devm_kzalloc(dev, sizeof(*(card->dai_link)), GFP_KERNEL);
|
||||
if (!card->dai_link)
|
||||
return -ENOMEM;
|
||||
card->dai_link->name = card->name;
|
||||
card->dai_link->stream_name = card->name;
|
||||
card->dai_link->cpu_dai_name = dev_name(ad->dssdev);
|
||||
|
|
|
|||
|
|
@ -63,8 +63,7 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
|
|||
sysclk = params_rate(params) * 512;
|
||||
sspa_mclk = params_rate(params) * 64;
|
||||
}
|
||||
sspa_div = freq_out;
|
||||
do_div(sspa_div, sspa_mclk);
|
||||
sspa_div = freq_out / sspa_mclk;
|
||||
|
||||
snd_soc_dai_set_sysclk(cpu_dai, MMP_SSPA_CLK_AUDIO, freq_out, 0);
|
||||
snd_soc_dai_set_pll(cpu_dai, MMP_SYSCLK, 0, freq_out, sysclk);
|
||||
|
|
|
|||
|
|
@ -355,6 +355,7 @@ static struct regmap_config lpass_cpu_regmap_config = {
|
|||
.readable_reg = lpass_cpu_regmap_readable,
|
||||
.volatile_reg = lpass_cpu_regmap_volatile,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
.val_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
};
|
||||
|
||||
int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
|
||||
|
|
|
|||
|
|
@ -34,13 +34,7 @@ struct rk_i2s_dev {
|
|||
|
||||
struct regmap *regmap;
|
||||
|
||||
/*
|
||||
* Used to indicate the tx/rx status.
|
||||
* I2S controller hopes to start the tx and rx together,
|
||||
* also to stop them when they are both try to stop.
|
||||
*/
|
||||
bool tx_start;
|
||||
bool rx_start;
|
||||
bool is_master_mode;
|
||||
};
|
||||
|
||||
static int i2s_runtime_suspend(struct device *dev)
|
||||
|
|
@ -81,37 +75,29 @@ static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on)
|
|||
I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_ENABLE);
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_XFER,
|
||||
I2S_XFER_TXS_START | I2S_XFER_RXS_START,
|
||||
I2S_XFER_TXS_START | I2S_XFER_RXS_START);
|
||||
|
||||
i2s->tx_start = true;
|
||||
I2S_XFER_TXS_START,
|
||||
I2S_XFER_TXS_START);
|
||||
} else {
|
||||
i2s->tx_start = false;
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_DMACR,
|
||||
I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE);
|
||||
|
||||
if (!i2s->rx_start) {
|
||||
regmap_update_bits(i2s->regmap, I2S_XFER,
|
||||
I2S_XFER_TXS_START |
|
||||
I2S_XFER_RXS_START,
|
||||
I2S_XFER_TXS_STOP |
|
||||
I2S_XFER_RXS_STOP);
|
||||
regmap_update_bits(i2s->regmap, I2S_XFER,
|
||||
I2S_XFER_TXS_START,
|
||||
I2S_XFER_TXS_STOP);
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_CLR,
|
||||
I2S_CLR_TXC | I2S_CLR_RXC,
|
||||
I2S_CLR_TXC | I2S_CLR_RXC);
|
||||
regmap_update_bits(i2s->regmap, I2S_CLR,
|
||||
I2S_CLR_TXC,
|
||||
I2S_CLR_TXC);
|
||||
|
||||
regmap_read(i2s->regmap, I2S_CLR, &val);
|
||||
|
||||
/* Should wait for clear operation to finish */
|
||||
while (val & I2S_CLR_TXC) {
|
||||
regmap_read(i2s->regmap, I2S_CLR, &val);
|
||||
|
||||
/* Should wait for clear operation to finish */
|
||||
while (val) {
|
||||
regmap_read(i2s->regmap, I2S_CLR, &val);
|
||||
retry--;
|
||||
if (!retry) {
|
||||
dev_warn(i2s->dev, "fail to clear\n");
|
||||
break;
|
||||
}
|
||||
retry--;
|
||||
if (!retry) {
|
||||
dev_warn(i2s->dev, "fail to clear\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -127,37 +113,29 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on)
|
|||
I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_ENABLE);
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_XFER,
|
||||
I2S_XFER_TXS_START | I2S_XFER_RXS_START,
|
||||
I2S_XFER_TXS_START | I2S_XFER_RXS_START);
|
||||
|
||||
i2s->rx_start = true;
|
||||
I2S_XFER_RXS_START,
|
||||
I2S_XFER_RXS_START);
|
||||
} else {
|
||||
i2s->rx_start = false;
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_DMACR,
|
||||
I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_DISABLE);
|
||||
|
||||
if (!i2s->tx_start) {
|
||||
regmap_update_bits(i2s->regmap, I2S_XFER,
|
||||
I2S_XFER_TXS_START |
|
||||
I2S_XFER_RXS_START,
|
||||
I2S_XFER_TXS_STOP |
|
||||
I2S_XFER_RXS_STOP);
|
||||
regmap_update_bits(i2s->regmap, I2S_XFER,
|
||||
I2S_XFER_RXS_START,
|
||||
I2S_XFER_RXS_STOP);
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_CLR,
|
||||
I2S_CLR_TXC | I2S_CLR_RXC,
|
||||
I2S_CLR_TXC | I2S_CLR_RXC);
|
||||
regmap_update_bits(i2s->regmap, I2S_CLR,
|
||||
I2S_CLR_RXC,
|
||||
I2S_CLR_RXC);
|
||||
|
||||
regmap_read(i2s->regmap, I2S_CLR, &val);
|
||||
|
||||
/* Should wait for clear operation to finish */
|
||||
while (val & I2S_CLR_RXC) {
|
||||
regmap_read(i2s->regmap, I2S_CLR, &val);
|
||||
|
||||
/* Should wait for clear operation to finish */
|
||||
while (val) {
|
||||
regmap_read(i2s->regmap, I2S_CLR, &val);
|
||||
retry--;
|
||||
if (!retry) {
|
||||
dev_warn(i2s->dev, "fail to clear\n");
|
||||
break;
|
||||
}
|
||||
retry--;
|
||||
if (!retry) {
|
||||
dev_warn(i2s->dev, "fail to clear\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -174,9 +152,11 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
|
|||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
/* Set source clock in Master mode */
|
||||
val = I2S_CKR_MSS_MASTER;
|
||||
i2s->is_master_mode = true;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
val = I2S_CKR_MSS_SLAVE;
|
||||
i2s->is_master_mode = false;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
|
@ -228,6 +208,26 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
|
|||
struct rk_i2s_dev *i2s = to_info(dai);
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
unsigned int val = 0;
|
||||
unsigned int mclk_rate, bclk_rate, div_bclk, div_lrck;
|
||||
|
||||
if (i2s->is_master_mode) {
|
||||
mclk_rate = clk_get_rate(i2s->mclk);
|
||||
bclk_rate = 2 * 32 * params_rate(params);
|
||||
if (bclk_rate && mclk_rate % bclk_rate)
|
||||
return -EINVAL;
|
||||
|
||||
div_bclk = mclk_rate / bclk_rate;
|
||||
div_lrck = bclk_rate / params_rate(params);
|
||||
regmap_update_bits(i2s->regmap, I2S_CKR,
|
||||
I2S_CKR_MDIV_MASK,
|
||||
I2S_CKR_MDIV(div_bclk));
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_CKR,
|
||||
I2S_CKR_TSD_MASK |
|
||||
I2S_CKR_RSD_MASK,
|
||||
I2S_CKR_TSD(div_lrck) |
|
||||
I2S_CKR_RSD(div_lrck));
|
||||
}
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S8:
|
||||
|
|
@ -451,6 +451,7 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct rk_i2s_dev *i2s;
|
||||
struct snd_soc_dai_driver *soc_dai;
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
int ret;
|
||||
|
|
@ -511,17 +512,26 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
|
|||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
/* refine capture channels */
|
||||
soc_dai = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*soc_dai), GFP_KERNEL);
|
||||
if (!soc_dai)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(soc_dai, &rockchip_i2s_dai, sizeof(*soc_dai));
|
||||
if (!of_property_read_u32(node, "rockchip,playback-channels", &val)) {
|
||||
if (val >= 2 && val <= 8)
|
||||
soc_dai->playback.channels_max = val;
|
||||
}
|
||||
|
||||
if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) {
|
||||
if (val >= 2 && val <= 8)
|
||||
rockchip_i2s_dai.capture.channels_max = val;
|
||||
else
|
||||
rockchip_i2s_dai.capture.channels_max = 2;
|
||||
soc_dai->capture.channels_max = val;
|
||||
}
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||
&rockchip_i2s_component,
|
||||
&rockchip_i2s_dai, 1);
|
||||
soc_dai, 1);
|
||||
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not register DAI\n");
|
||||
goto err_suspend;
|
||||
|
|
|
|||
|
|
@ -80,11 +80,17 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
|
|||
switch (params_rate(params)) {
|
||||
case 8000:
|
||||
case 16000:
|
||||
case 24000:
|
||||
case 32000:
|
||||
case 48000:
|
||||
case 64000:
|
||||
case 96000:
|
||||
mclk = 12288000;
|
||||
break;
|
||||
case 11025:
|
||||
case 22050:
|
||||
case 44100:
|
||||
case 88200:
|
||||
mclk = 11289600;
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -79,11 +79,17 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
|
|||
switch (params_rate(params)) {
|
||||
case 8000:
|
||||
case 16000:
|
||||
case 24000:
|
||||
case 32000:
|
||||
case 48000:
|
||||
case 64000:
|
||||
case 96000:
|
||||
mclk = 12288000;
|
||||
break;
|
||||
case 11025:
|
||||
case 22050:
|
||||
case 44100:
|
||||
case 88200:
|
||||
mclk = 11289600;
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
config SND_SOC_SAMSUNG
|
||||
tristate "ASoC support for Samsung"
|
||||
depends on (PLAT_SAMSUNG || ARCH_EXYNOS)
|
||||
depends on S3C64XX_PL080 || !ARCH_S3C64XX
|
||||
depends on S3C24XX_DMAC || !ARCH_S3C24XX
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ static const struct snd_soc_component_driver s3c_ac97_component = {
|
|||
|
||||
static int s3c_ac97_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res;
|
||||
struct resource *mem_res, *irq_res;
|
||||
struct s3c_audio_pdata *ac97_pdata;
|
||||
int ret;
|
||||
|
||||
|
|
@ -335,24 +335,6 @@ static int s3c_ac97_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* Check for availability of necessary resource */
|
||||
dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!dmatx_res) {
|
||||
dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!dmarx_res) {
|
||||
dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
|
||||
if (!dmamic_res) {
|
||||
dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!irq_res) {
|
||||
dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
|
||||
|
|
@ -364,11 +346,11 @@ static int s3c_ac97_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(s3c_ac97.regs))
|
||||
return PTR_ERR(s3c_ac97.regs);
|
||||
|
||||
s3c_ac97_pcm_out.channel = dmatx_res->start;
|
||||
s3c_ac97_pcm_out.slave = ac97_pdata->dma_playback;
|
||||
s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
|
||||
s3c_ac97_pcm_in.channel = dmarx_res->start;
|
||||
s3c_ac97_pcm_in.slave = ac97_pdata->dma_capture;
|
||||
s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
|
||||
s3c_ac97_mic_in.channel = dmamic_res->start;
|
||||
s3c_ac97_mic_in.slave = ac97_pdata->dma_capture_mic;
|
||||
s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
|
||||
|
||||
init_completion(&s3c_ac97.done);
|
||||
|
|
@ -406,7 +388,8 @@ static int s3c_ac97_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto err5;
|
||||
|
||||
ret = samsung_asoc_dma_platform_register(&pdev->dev);
|
||||
ret = samsung_asoc_dma_platform_register(&pdev->dev,
|
||||
ac97_pdata->dma_filter);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
|
||||
goto err5;
|
||||
|
|
|
|||
|
|
@ -13,9 +13,10 @@
|
|||
#define _S3C_AUDIO_H
|
||||
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <linux/dmaengine.h>
|
||||
|
||||
struct s3c_dma_params {
|
||||
int channel; /* Channel ID */
|
||||
void *slave; /* Channel ID */
|
||||
dma_addr_t dma_addr;
|
||||
int dma_size; /* Size of the DMA transfer */
|
||||
char *ch_name;
|
||||
|
|
@ -25,6 +26,7 @@ struct s3c_dma_params {
|
|||
void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
|
||||
struct s3c_dma_params *playback,
|
||||
struct s3c_dma_params *capture);
|
||||
int samsung_asoc_dma_platform_register(struct device *dev);
|
||||
int samsung_asoc_dma_platform_register(struct device *dev,
|
||||
dma_filter_fn fn);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -28,17 +28,8 @@
|
|||
|
||||
#include "dma.h"
|
||||
|
||||
#ifdef CONFIG_ARCH_S3C64XX
|
||||
#define filter_fn pl08x_filter_id
|
||||
#elif defined(CONFIG_ARCH_S3C24XX)
|
||||
#define filter_fn s3c24xx_dma_filter
|
||||
#else
|
||||
#define filter_fn NULL
|
||||
#endif
|
||||
|
||||
static const struct snd_dmaengine_pcm_config samsung_dmaengine_pcm_config = {
|
||||
static struct snd_dmaengine_pcm_config samsung_dmaengine_pcm_config = {
|
||||
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
|
||||
.compat_filter_fn = filter_fn,
|
||||
};
|
||||
|
||||
void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
|
||||
|
|
@ -50,14 +41,14 @@ void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
|
|||
|
||||
if (playback) {
|
||||
playback_data = &playback->dma_data;
|
||||
playback_data->filter_data = (void *)playback->channel;
|
||||
playback_data->filter_data = playback->slave;
|
||||
playback_data->chan_name = playback->ch_name;
|
||||
playback_data->addr = playback->dma_addr;
|
||||
playback_data->addr_width = playback->dma_size;
|
||||
}
|
||||
if (capture) {
|
||||
capture_data = &capture->dma_data;
|
||||
capture_data->filter_data = (void *)capture->channel;
|
||||
capture_data->filter_data = capture->slave;
|
||||
capture_data->chan_name = capture->ch_name;
|
||||
capture_data->addr = capture->dma_addr;
|
||||
capture_data->addr_width = capture->dma_size;
|
||||
|
|
@ -67,8 +58,11 @@ void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(samsung_asoc_init_dma_data);
|
||||
|
||||
int samsung_asoc_dma_platform_register(struct device *dev)
|
||||
int samsung_asoc_dma_platform_register(struct device *dev,
|
||||
dma_filter_fn filter)
|
||||
{
|
||||
samsung_dmaengine_pcm_config.compat_filter_fn = filter;
|
||||
|
||||
return devm_snd_dmaengine_pcm_register(dev,
|
||||
&samsung_dmaengine_pcm_config,
|
||||
SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME |
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ struct i2s_dai {
|
|||
struct s3c_dma_params dma_playback;
|
||||
struct s3c_dma_params dma_capture;
|
||||
struct s3c_dma_params idma_playback;
|
||||
dma_filter_fn filter;
|
||||
u32 quirks;
|
||||
u32 suspend_i2smod;
|
||||
u32 suspend_i2scon;
|
||||
|
|
@ -1244,7 +1245,8 @@ static int samsung_i2s_probe(struct platform_device *pdev)
|
|||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
return samsung_asoc_dma_platform_register(&pdev->dev);
|
||||
return samsung_asoc_dma_platform_register(&pdev->dev,
|
||||
sec_dai->filter);
|
||||
}
|
||||
|
||||
pri_dai = i2s_alloc_dai(pdev, false);
|
||||
|
|
@ -1257,27 +1259,15 @@ static int samsung_i2s_probe(struct platform_device *pdev)
|
|||
pri_dai->lock = &pri_dai->spinlock;
|
||||
|
||||
if (!np) {
|
||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev,
|
||||
"Unable to get I2S-TX dma resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
pri_dai->dma_playback.channel = res->start;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev,
|
||||
"Unable to get I2S-RX dma resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
pri_dai->dma_capture.channel = res->start;
|
||||
|
||||
if (i2s_pdata == NULL) {
|
||||
dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pri_dai->dma_playback.slave = i2s_pdata->dma_playback;
|
||||
pri_dai->dma_capture.slave = i2s_pdata->dma_capture;
|
||||
pri_dai->filter = i2s_pdata->dma_filter;
|
||||
|
||||
if (&i2s_pdata->type)
|
||||
i2s_cfg = &i2s_pdata->type.i2s;
|
||||
|
||||
|
|
@ -1339,9 +1329,8 @@ static int samsung_i2s_probe(struct platform_device *pdev)
|
|||
sec_dai->dma_playback.ch_name = "tx-sec";
|
||||
|
||||
if (!np) {
|
||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
|
||||
if (res)
|
||||
sec_dai->dma_playback.channel = res->start;
|
||||
sec_dai->dma_playback.slave = i2s_pdata->dma_play_sec;
|
||||
sec_dai->filter = i2s_pdata->dma_filter;
|
||||
}
|
||||
|
||||
sec_dai->dma_playback.dma_size = 4;
|
||||
|
|
@ -1364,7 +1353,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
|
|||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = samsung_asoc_dma_platform_register(&pdev->dev);
|
||||
ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -486,8 +486,9 @@ static const struct snd_soc_component_driver s3c_pcm_component = {
|
|||
static int s3c_pcm_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct s3c_pcm_info *pcm;
|
||||
struct resource *mem_res, *dmatx_res, *dmarx_res;
|
||||
struct resource *mem_res;
|
||||
struct s3c_audio_pdata *pcm_pdata;
|
||||
dma_filter_fn filter;
|
||||
int ret;
|
||||
|
||||
/* Check for valid device index */
|
||||
|
|
@ -499,18 +500,6 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
|
|||
pcm_pdata = pdev->dev.platform_data;
|
||||
|
||||
/* Check for availability of necessary resource */
|
||||
dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!dmatx_res) {
|
||||
dev_err(&pdev->dev, "Unable to get PCM-TX dma resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!dmarx_res) {
|
||||
dev_err(&pdev->dev, "Unable to get PCM-RX dma resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!mem_res) {
|
||||
dev_err(&pdev->dev, "Unable to get register resource\n");
|
||||
|
|
@ -568,8 +557,12 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
|
|||
s3c_pcm_stereo_out[pdev->id].dma_addr = mem_res->start
|
||||
+ S3C_PCM_TXFIFO;
|
||||
|
||||
s3c_pcm_stereo_in[pdev->id].channel = dmarx_res->start;
|
||||
s3c_pcm_stereo_out[pdev->id].channel = dmatx_res->start;
|
||||
filter = NULL;
|
||||
if (pcm_pdata) {
|
||||
s3c_pcm_stereo_in[pdev->id].slave = pcm_pdata->dma_capture;
|
||||
s3c_pcm_stereo_out[pdev->id].slave = pcm_pdata->dma_playback;
|
||||
filter = pcm_pdata->dma_filter;
|
||||
}
|
||||
|
||||
pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id];
|
||||
pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id];
|
||||
|
|
@ -583,7 +576,7 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
|
|||
goto err5;
|
||||
}
|
||||
|
||||
ret = samsung_asoc_dma_platform_register(&pdev->dev);
|
||||
ret = samsung_asoc_dma_platform_register(&pdev->dev, filter);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
|
||||
goto err5;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include <mach/dma.h>
|
||||
#include <mach/gpio-samsung.h>
|
||||
#include <plat/gpio-cfg.h>
|
||||
|
||||
|
|
@ -33,14 +32,14 @@
|
|||
#include "regs-i2s-v2.h"
|
||||
#include "s3c2412-i2s.h"
|
||||
|
||||
#include <linux/platform_data/asoc-s3c.h>
|
||||
|
||||
static struct s3c_dma_params s3c2412_i2s_pcm_stereo_out = {
|
||||
.channel = DMACH_I2S_OUT,
|
||||
.ch_name = "tx",
|
||||
.dma_size = 4,
|
||||
};
|
||||
|
||||
static struct s3c_dma_params s3c2412_i2s_pcm_stereo_in = {
|
||||
.channel = DMACH_I2S_IN,
|
||||
.ch_name = "rx",
|
||||
.dma_size = 4,
|
||||
};
|
||||
|
|
@ -152,6 +151,12 @@ static int s3c2412_iis_dev_probe(struct platform_device *pdev)
|
|||
{
|
||||
int ret = 0;
|
||||
struct resource *res;
|
||||
struct s3c_audio_pdata *pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "missing platform data");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
s3c2412_i2s.regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
|
|
@ -159,7 +164,9 @@ static int s3c2412_iis_dev_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(s3c2412_i2s.regs);
|
||||
|
||||
s3c2412_i2s_pcm_stereo_out.dma_addr = res->start + S3C2412_IISTXD;
|
||||
s3c2412_i2s_pcm_stereo_out.slave = pdata->dma_playback;
|
||||
s3c2412_i2s_pcm_stereo_in.dma_addr = res->start + S3C2412_IISRXD;
|
||||
s3c2412_i2s_pcm_stereo_in.slave = pdata->dma_capture;
|
||||
|
||||
ret = s3c_i2sv2_register_component(&pdev->dev, -1,
|
||||
&s3c2412_i2s_component,
|
||||
|
|
@ -169,7 +176,8 @@ static int s3c2412_iis_dev_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = samsung_asoc_dma_platform_register(&pdev->dev);
|
||||
ret = samsung_asoc_dma_platform_register(&pdev->dev,
|
||||
pdata->dma_filter);
|
||||
if (ret)
|
||||
pr_err("failed to register the DMA: %d\n", ret);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@
|
|||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include <mach/dma.h>
|
||||
#include <mach/gpio-samsung.h>
|
||||
#include <plat/gpio-cfg.h>
|
||||
#include "regs-iis.h"
|
||||
|
|
@ -31,14 +30,14 @@
|
|||
#include "dma.h"
|
||||
#include "s3c24xx-i2s.h"
|
||||
|
||||
#include <linux/platform_data/asoc-s3c.h>
|
||||
|
||||
static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_out = {
|
||||
.channel = DMACH_I2S_OUT,
|
||||
.ch_name = "tx",
|
||||
.dma_size = 2,
|
||||
};
|
||||
|
||||
static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_in = {
|
||||
.channel = DMACH_I2S_IN,
|
||||
.ch_name = "rx",
|
||||
.dma_size = 2,
|
||||
};
|
||||
|
|
@ -454,6 +453,12 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
|
|||
{
|
||||
int ret = 0;
|
||||
struct resource *res;
|
||||
struct s3c_audio_pdata *pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "missing platform data");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
|
|
@ -465,7 +470,9 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(s3c24xx_i2s.regs);
|
||||
|
||||
s3c24xx_i2s_pcm_stereo_out.dma_addr = res->start + S3C2410_IISFIFO;
|
||||
s3c24xx_i2s_pcm_stereo_out.slave = pdata->dma_playback;
|
||||
s3c24xx_i2s_pcm_stereo_in.dma_addr = res->start + S3C2410_IISFIFO;
|
||||
s3c24xx_i2s_pcm_stereo_in.slave = pdata->dma_capture;
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||
&s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1);
|
||||
|
|
@ -474,7 +481,8 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = samsung_asoc_dma_platform_register(&pdev->dev);
|
||||
ret = samsung_asoc_dma_platform_register(&pdev->dev,
|
||||
pdata->dma_filter);
|
||||
if (ret)
|
||||
pr_err("failed to register the dma: %d\n", ret);
|
||||
|
||||
|
|
|
|||
|
|
@ -359,20 +359,15 @@ static const struct snd_soc_component_driver samsung_spdif_component = {
|
|||
static int spdif_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct s3c_audio_pdata *spdif_pdata;
|
||||
struct resource *mem_res, *dma_res;
|
||||
struct resource *mem_res;
|
||||
struct samsung_spdif_info *spdif;
|
||||
dma_filter_fn filter;
|
||||
int ret;
|
||||
|
||||
spdif_pdata = pdev->dev.platform_data;
|
||||
|
||||
dev_dbg(&pdev->dev, "Entered %s\n", __func__);
|
||||
|
||||
dma_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!dma_res) {
|
||||
dev_err(&pdev->dev, "Unable to get dma resource.\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!mem_res) {
|
||||
dev_err(&pdev->dev, "Unable to get register resource.\n");
|
||||
|
|
@ -432,11 +427,15 @@ static int spdif_probe(struct platform_device *pdev)
|
|||
|
||||
spdif_stereo_out.dma_size = 2;
|
||||
spdif_stereo_out.dma_addr = mem_res->start + DATA_OUTBUF;
|
||||
spdif_stereo_out.channel = dma_res->start;
|
||||
filter = NULL;
|
||||
if (spdif_pdata) {
|
||||
spdif_stereo_out.slave = spdif_pdata->dma_playback;
|
||||
filter = spdif_pdata->dma_filter;
|
||||
}
|
||||
|
||||
spdif->dma_playback = &spdif_stereo_out;
|
||||
|
||||
ret = samsung_asoc_dma_platform_register(&pdev->dev);
|
||||
ret = samsung_asoc_dma_platform_register(&pdev->dev, filter);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register DMA: %d\n", ret);
|
||||
goto err4;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ config SND_SOC_SH4_SIU
|
|||
|
||||
config SND_SOC_RCAR
|
||||
tristate "R-Car series SRU/SCU/SSIU/SSI support"
|
||||
depends on DMA_OF
|
||||
depends on COMMON_CLK
|
||||
select SND_SIMPLE_CARD
|
||||
select REGMAP_MMIO
|
||||
|
|
|
|||
|
|
@ -1362,15 +1362,18 @@ static int fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
|
|||
|
||||
static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev)
|
||||
{
|
||||
dma_cap_mask_t mask;
|
||||
int is_play = fsi_stream_is_play(fsi, io);
|
||||
|
||||
#ifdef CONFIG_SUPERH
|
||||
dma_cap_mask_t mask;
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
io->chan = dma_request_slave_channel_compat(mask,
|
||||
shdma_chan_filter, (void *)io->dma_id,
|
||||
dev, is_play ? "tx" : "rx");
|
||||
io->chan = dma_request_channel(mask, shdma_chan_filter,
|
||||
(void *)io->dma_id);
|
||||
#else
|
||||
io->chan = dma_request_slave_channel(dev, is_play ? "tx" : "rx");
|
||||
#endif
|
||||
if (io->chan) {
|
||||
struct dma_slave_config cfg = {};
|
||||
int ret;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o src.o ctu.o mix.o dvc.o
|
||||
snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o
|
||||
obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
|
||||
|
||||
snd-soc-rsrc-card-objs := rsrc-card.o
|
||||
|
|
|
|||
|
|
@ -68,8 +68,8 @@ static u32 rsnd_adg_calculate_rbgx(unsigned long div)
|
|||
|
||||
static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
|
||||
{
|
||||
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
|
||||
int id = rsnd_mod_id(mod);
|
||||
struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
|
||||
int id = rsnd_mod_id(ssi_mod);
|
||||
int ws = id;
|
||||
|
||||
if (rsnd_ssi_is_pin_sharing(io)) {
|
||||
|
|
@ -90,13 +90,13 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
|
|||
return (0x6 + ws) << 8;
|
||||
}
|
||||
|
||||
int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
|
||||
int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
|
||||
struct rsnd_dai_stream *io)
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(cmd_mod);
|
||||
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
|
||||
struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
|
||||
int id = rsnd_mod_id(mod);
|
||||
int id = rsnd_mod_id(cmd_mod);
|
||||
int shift = (id % 2) ? 16 : 0;
|
||||
u32 mask, val;
|
||||
|
||||
|
|
@ -242,68 +242,6 @@ int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod,
|
|||
return rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
|
||||
}
|
||||
|
||||
int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
|
||||
struct rsnd_mod *mod,
|
||||
unsigned int src_rate,
|
||||
unsigned int dst_rate)
|
||||
{
|
||||
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
|
||||
struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
int idx, sel, div, shift;
|
||||
u32 mask, val;
|
||||
int id = rsnd_mod_id(mod);
|
||||
unsigned int sel_rate [] = {
|
||||
clk_get_rate(adg->clk[CLKA]), /* 000: CLKA */
|
||||
clk_get_rate(adg->clk[CLKB]), /* 001: CLKB */
|
||||
clk_get_rate(adg->clk[CLKC]), /* 010: CLKC */
|
||||
0, /* 011: MLBCLK (not used) */
|
||||
adg->rbga_rate_for_441khz, /* 100: RBGA */
|
||||
adg->rbgb_rate_for_48khz, /* 101: RBGB */
|
||||
};
|
||||
|
||||
/* find div (= 1/128, 1/256, 1/512, 1/1024, 1/2048 */
|
||||
for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
|
||||
for (div = 128, idx = 0;
|
||||
div <= 2048;
|
||||
div *= 2, idx++) {
|
||||
if (src_rate == sel_rate[sel] / div) {
|
||||
val = (idx << 4) | sel;
|
||||
goto find_rate;
|
||||
}
|
||||
}
|
||||
}
|
||||
dev_err(dev, "can't find convert src clk\n");
|
||||
return -EINVAL;
|
||||
|
||||
find_rate:
|
||||
shift = (id % 4) * 8;
|
||||
mask = 0xFF << shift;
|
||||
val = val << shift;
|
||||
|
||||
dev_dbg(dev, "adg convert src clk = %02x\n", val);
|
||||
|
||||
switch (id / 4) {
|
||||
case 0:
|
||||
rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL3, mask, val);
|
||||
break;
|
||||
case 1:
|
||||
rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL4, mask, val);
|
||||
break;
|
||||
case 2:
|
||||
rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL5, mask, val);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gen1 doesn't need dst_rate settings,
|
||||
* since it uses SSI WS pin.
|
||||
* see also rsnd_src_set_route_if_gen1()
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
|
||||
|
|
@ -337,20 +275,16 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
|
|||
}
|
||||
}
|
||||
|
||||
int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod)
|
||||
int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod)
|
||||
{
|
||||
/*
|
||||
* "mod" = "ssi" here.
|
||||
* we can get "ssi id" from mod
|
||||
*/
|
||||
rsnd_adg_set_ssi_clk(mod, 0);
|
||||
rsnd_adg_set_ssi_clk(ssi_mod, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
|
||||
int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
|
||||
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct clk *clk;
|
||||
|
|
@ -394,14 +328,10 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
|
|||
|
||||
found_clock:
|
||||
|
||||
/*
|
||||
* This "mod" = "ssi" here.
|
||||
* we can get "ssi id" from mod
|
||||
*/
|
||||
rsnd_adg_set_ssi_clk(mod, data);
|
||||
rsnd_adg_set_ssi_clk(ssi_mod, data);
|
||||
|
||||
dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod),
|
||||
rsnd_mod_name(ssi_mod), rsnd_mod_id(ssi_mod),
|
||||
data, rate);
|
||||
|
||||
return 0;
|
||||
|
|
@ -418,15 +348,20 @@ static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
|
|||
[CLKC] = "clk_c",
|
||||
[CLKI] = "clk_i",
|
||||
};
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < CLKMAX; i++) {
|
||||
clk = devm_clk_get(dev, clk_name[i]);
|
||||
adg->clk[i] = IS_ERR(clk) ? NULL : clk;
|
||||
}
|
||||
|
||||
for_each_rsnd_clk(clk, adg, i)
|
||||
for_each_rsnd_clk(clk, adg, i) {
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret < 0)
|
||||
dev_warn(dev, "can't use clk %d\n", i);
|
||||
|
||||
dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
|
||||
}
|
||||
}
|
||||
|
||||
static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
|
||||
|
|
@ -437,7 +372,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
|
|||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 ckr, rbgx, rbga, rbgb;
|
||||
u32 rate, req_rate, div;
|
||||
u32 rate, req_rate = 0, div;
|
||||
uint32_t count = 0;
|
||||
unsigned long req_48kHz_rate, req_441kHz_rate;
|
||||
int i;
|
||||
|
|
@ -572,9 +507,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
|
|||
ckr, rbga, rbgb);
|
||||
}
|
||||
|
||||
int rsnd_adg_probe(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv)
|
||||
int rsnd_adg_probe(struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_adg *adg;
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
|
|
@ -600,3 +533,14 @@ int rsnd_adg_probe(struct platform_device *pdev,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rsnd_adg_remove(struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
for_each_rsnd_clk(clk, adg, i) {
|
||||
clk_disable_unprepare(clk);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
171
sound/soc/sh/rcar/cmd.c
Normal file
171
sound/soc/sh/rcar/cmd.c
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Renesas R-Car CMD support
|
||||
*
|
||||
* Copyright (C) 2015 Renesas Solutions Corp.
|
||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include "rsnd.h"
|
||||
|
||||
struct rsnd_cmd {
|
||||
struct rsnd_mod mod;
|
||||
};
|
||||
|
||||
#define CMD_NAME "cmd"
|
||||
|
||||
#define rsnd_cmd_nr(priv) ((priv)->cmd_nr)
|
||||
#define for_each_rsnd_cmd(pos, priv, i) \
|
||||
for ((i) = 0; \
|
||||
((i) < rsnd_cmd_nr(priv)) && \
|
||||
((pos) = (struct rsnd_cmd *)(priv)->cmd + i); \
|
||||
i++)
|
||||
|
||||
static int rsnd_cmd_init(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
|
||||
struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
|
||||
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
u32 data;
|
||||
|
||||
if (!mix && !dvc)
|
||||
return 0;
|
||||
|
||||
if (mix) {
|
||||
struct rsnd_dai *rdai;
|
||||
int i;
|
||||
u32 path[] = {
|
||||
[0] = 0,
|
||||
[1] = 1 << 0,
|
||||
[2] = 0,
|
||||
[3] = 0,
|
||||
[4] = 0,
|
||||
[5] = 1 << 8
|
||||
};
|
||||
|
||||
/*
|
||||
* it is assuming that integrater is well understanding about
|
||||
* data path. Here doesn't check impossible connection,
|
||||
* like src2 + src5
|
||||
*/
|
||||
data = 0;
|
||||
for_each_rsnd_dai(rdai, priv, i) {
|
||||
io = &rdai->playback;
|
||||
if (mix == rsnd_io_to_mod_mix(io))
|
||||
data |= path[rsnd_mod_id(src)];
|
||||
|
||||
io = &rdai->capture;
|
||||
if (mix == rsnd_io_to_mod_mix(io))
|
||||
data |= path[rsnd_mod_id(src)];
|
||||
}
|
||||
|
||||
} else {
|
||||
u32 path[] = {
|
||||
[0] = 0x30000,
|
||||
[1] = 0x30001,
|
||||
[2] = 0x40000,
|
||||
[3] = 0x10000,
|
||||
[4] = 0x20000,
|
||||
[5] = 0x40100
|
||||
};
|
||||
|
||||
data = path[rsnd_mod_id(src)];
|
||||
}
|
||||
|
||||
dev_dbg(dev, "ctu/mix path = 0x%08x", data);
|
||||
|
||||
rsnd_mod_write(mod, CMD_ROUTE_SLCT, data);
|
||||
rsnd_mod_write(mod, CMD_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
|
||||
|
||||
rsnd_adg_set_cmd_timsel_gen2(mod, io);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_cmd_start(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
rsnd_mod_write(mod, CMD_CTRL, 0x10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_cmd_stop(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
rsnd_mod_write(mod, CMD_CTRL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rsnd_mod_ops rsnd_cmd_ops = {
|
||||
.name = CMD_NAME,
|
||||
.init = rsnd_cmd_init,
|
||||
.start = rsnd_cmd_start,
|
||||
.stop = rsnd_cmd_stop,
|
||||
};
|
||||
|
||||
int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id)
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_io_to_priv(io);
|
||||
struct rsnd_mod *mod = rsnd_cmd_mod_get(priv, id);
|
||||
|
||||
return rsnd_dai_connect(mod, io, mod->type);
|
||||
}
|
||||
|
||||
struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id)
|
||||
{
|
||||
if (WARN_ON(id < 0 || id >= rsnd_cmd_nr(priv)))
|
||||
id = 0;
|
||||
|
||||
return rsnd_mod_get((struct rsnd_cmd *)(priv->cmd) + id);
|
||||
}
|
||||
|
||||
int rsnd_cmd_probe(struct rsnd_priv *priv)
|
||||
{
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct rsnd_cmd *cmd;
|
||||
int i, nr, ret;
|
||||
|
||||
/* This driver doesn't support Gen1 at this point */
|
||||
if (rsnd_is_gen1(priv))
|
||||
return 0;
|
||||
|
||||
/* same number as DVC */
|
||||
nr = priv->dvc_nr;
|
||||
if (!nr)
|
||||
return 0;
|
||||
|
||||
cmd = devm_kzalloc(dev, sizeof(*cmd) * nr, GFP_KERNEL);
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->cmd_nr = nr;
|
||||
priv->cmd = cmd;
|
||||
|
||||
for_each_rsnd_cmd(cmd, priv, i) {
|
||||
ret = rsnd_mod_init(priv, rsnd_mod_get(cmd),
|
||||
&rsnd_cmd_ops, NULL, RSND_MOD_CMD, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rsnd_cmd_remove(struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_cmd *cmd;
|
||||
int i;
|
||||
|
||||
for_each_rsnd_cmd(cmd, priv, i) {
|
||||
rsnd_mod_quit(rsnd_mod_get(cmd));
|
||||
}
|
||||
}
|
||||
|
|
@ -99,34 +99,17 @@
|
|||
#define RSND_RATES SNDRV_PCM_RATE_8000_96000
|
||||
#define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
|
||||
|
||||
static const struct rsnd_of_data rsnd_of_data_gen1 = {
|
||||
.flags = RSND_GEN1,
|
||||
};
|
||||
|
||||
static const struct rsnd_of_data rsnd_of_data_gen2 = {
|
||||
.flags = RSND_GEN2,
|
||||
};
|
||||
|
||||
static const struct of_device_id rsnd_of_match[] = {
|
||||
{ .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 },
|
||||
{ .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 },
|
||||
{ .compatible = "renesas,rcar_sound-gen3", .data = &rsnd_of_data_gen2 }, /* gen2 compatible */
|
||||
{ .compatible = "renesas,rcar_sound-gen1", .data = (void *)RSND_GEN1 },
|
||||
{ .compatible = "renesas,rcar_sound-gen2", .data = (void *)RSND_GEN2 },
|
||||
{ .compatible = "renesas,rcar_sound-gen3", .data = (void *)RSND_GEN2 }, /* gen2 compatible */
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rsnd_of_match);
|
||||
|
||||
/*
|
||||
* rsnd_platform functions
|
||||
* rsnd_mod functions
|
||||
*/
|
||||
#define rsnd_platform_call(priv, dai, func, param...) \
|
||||
(!(priv->info->func) ? 0 : \
|
||||
priv->info->func(param))
|
||||
|
||||
#define rsnd_is_enable_path(io, name) \
|
||||
((io)->info ? (io)->info->name : NULL)
|
||||
#define rsnd_info_id(priv, io, name) \
|
||||
((io)->info->name - priv->info->name##_info)
|
||||
|
||||
void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
|
||||
{
|
||||
if (mod->type != type) {
|
||||
|
|
@ -138,9 +121,6 @@ void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* rsnd_mod functions
|
||||
*/
|
||||
char *rsnd_mod_name(struct rsnd_mod *mod)
|
||||
{
|
||||
if (!mod || !mod->ops)
|
||||
|
|
@ -192,19 +172,16 @@ void rsnd_mod_interrupt(struct rsnd_mod *mod,
|
|||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct rsnd_dai_stream *io;
|
||||
struct rsnd_dai *rdai;
|
||||
int i, j;
|
||||
int i;
|
||||
|
||||
for_each_rsnd_dai(rdai, priv, j) {
|
||||
for_each_rsnd_dai(rdai, priv, i) {
|
||||
io = &rdai->playback;
|
||||
if (mod == io->mod[mod->type])
|
||||
callback(mod, io);
|
||||
|
||||
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);
|
||||
}
|
||||
io = &rdai->capture;
|
||||
if (mod == io->mod[mod->type])
|
||||
callback(mod, io);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -214,6 +191,43 @@ int rsnd_io_is_working(struct rsnd_dai_stream *io)
|
|||
return !!io->substream;
|
||||
}
|
||||
|
||||
void rsnd_set_slot(struct rsnd_dai *rdai,
|
||||
int slots, int num)
|
||||
{
|
||||
rdai->slots = slots;
|
||||
rdai->slots_num = num;
|
||||
}
|
||||
|
||||
int rsnd_get_slot(struct rsnd_dai_stream *io)
|
||||
{
|
||||
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
||||
|
||||
return rdai->slots;
|
||||
}
|
||||
|
||||
int rsnd_get_slot_num(struct rsnd_dai_stream *io)
|
||||
{
|
||||
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
||||
|
||||
return rdai->slots_num;
|
||||
}
|
||||
|
||||
int rsnd_get_slot_width(struct rsnd_dai_stream *io)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
||||
int chan = runtime->channels;
|
||||
|
||||
/* Multi channel Mode */
|
||||
if (rsnd_ssi_multi_slaves(io))
|
||||
chan /= rsnd_get_slot_num(io);
|
||||
|
||||
/* TDM Extend Mode needs 8ch */
|
||||
if (chan == 6)
|
||||
chan = 8;
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
/*
|
||||
* ADINR function
|
||||
*/
|
||||
|
|
@ -222,21 +236,17 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
|
|||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
u32 adinr = runtime->channels;
|
||||
|
||||
switch (runtime->sample_bits) {
|
||||
case 16:
|
||||
adinr |= (8 << 16);
|
||||
break;
|
||||
return 8 << 16;
|
||||
case 32:
|
||||
adinr |= (0 << 16);
|
||||
break;
|
||||
default:
|
||||
dev_warn(dev, "not supported sample bits\n");
|
||||
return 0;
|
||||
return 0 << 16;
|
||||
}
|
||||
|
||||
return adinr;
|
||||
dev_warn(dev, "not supported sample bits\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
|
||||
|
|
@ -267,13 +277,22 @@ u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
|
|||
*/
|
||||
u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
|
||||
{
|
||||
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
|
||||
struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
|
||||
struct rsnd_mod *target = src ? src : ssi;
|
||||
struct rsnd_mod *target;
|
||||
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
||||
u32 val = 0x76543210;
|
||||
u32 mask = ~0;
|
||||
|
||||
if (rsnd_io_is_play(io)) {
|
||||
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
|
||||
|
||||
target = src ? src : ssi;
|
||||
} else {
|
||||
struct rsnd_mod *cmd = rsnd_io_to_mod_cmd(io);
|
||||
|
||||
target = cmd ? cmd : ssi;
|
||||
}
|
||||
|
||||
mask <<= runtime->channels * 4;
|
||||
val = val & mask;
|
||||
|
||||
|
|
@ -300,20 +319,22 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
|
|||
/*
|
||||
* rsnd_dai functions
|
||||
*/
|
||||
#define rsnd_mod_call(mod, io, func, param...) \
|
||||
#define rsnd_mod_call(idx, io, func, param...) \
|
||||
({ \
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \
|
||||
struct rsnd_mod *mod = (io)->mod[idx]; \
|
||||
struct device *dev = rsnd_priv_to_dev(priv); \
|
||||
u32 *status = (io)->mod_status + idx; \
|
||||
u32 mask = 0xF << __rsnd_mod_shift_##func; \
|
||||
u8 val = (mod->status >> __rsnd_mod_shift_##func) & 0xF; \
|
||||
u8 val = (*status >> __rsnd_mod_shift_##func) & 0xF; \
|
||||
u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \
|
||||
int ret = 0; \
|
||||
int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \
|
||||
mod->status = (mod->status & ~mask) + \
|
||||
*status = (*status & ~mask) + \
|
||||
(add << __rsnd_mod_shift_##func); \
|
||||
dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod), \
|
||||
mod->status, call ? #func : ""); \
|
||||
*status, call ? #func : ""); \
|
||||
if (call) \
|
||||
ret = (mod)->ops->func(mod, io, param); \
|
||||
ret; \
|
||||
|
|
@ -327,13 +348,14 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
|
|||
mod = (io)->mod[i]; \
|
||||
if (!mod) \
|
||||
continue; \
|
||||
ret |= rsnd_mod_call(mod, io, fn, param); \
|
||||
ret |= rsnd_mod_call(i, io, fn, param); \
|
||||
} \
|
||||
ret; \
|
||||
})
|
||||
|
||||
static int rsnd_dai_connect(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io)
|
||||
int rsnd_dai_connect(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
enum rsnd_mod_type type)
|
||||
{
|
||||
struct rsnd_priv *priv;
|
||||
struct device *dev;
|
||||
|
|
@ -341,10 +363,13 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
|
|||
if (!mod)
|
||||
return -EIO;
|
||||
|
||||
if (io->mod[type])
|
||||
return -EINVAL;
|
||||
|
||||
priv = rsnd_mod_to_priv(mod);
|
||||
dev = rsnd_priv_to_dev(priv);
|
||||
|
||||
io->mod[mod->type] = mod;
|
||||
io->mod[type] = mod;
|
||||
|
||||
dev_dbg(dev, "%s[%d] is connected to io (%s)\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod),
|
||||
|
|
@ -354,9 +379,10 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
|
|||
}
|
||||
|
||||
static void rsnd_dai_disconnect(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io)
|
||||
struct rsnd_dai_stream *io,
|
||||
enum rsnd_mod_type type)
|
||||
{
|
||||
io->mod[mod->type] = NULL;
|
||||
io->mod[type] = NULL;
|
||||
}
|
||||
|
||||
struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id)
|
||||
|
|
@ -469,7 +495,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
|
||||
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
|
||||
struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
|
||||
int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io));
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -479,10 +504,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
case SNDRV_PCM_TRIGGER_START:
|
||||
rsnd_dai_stream_init(io, substream);
|
||||
|
||||
ret = rsnd_platform_call(priv, dai, start, ssi_id);
|
||||
if (ret < 0)
|
||||
goto dai_trigger_end;
|
||||
|
||||
ret = rsnd_dai_call(init, io, priv);
|
||||
if (ret < 0)
|
||||
goto dai_trigger_end;
|
||||
|
|
@ -496,8 +517,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
|
||||
ret |= rsnd_dai_call(quit, io, priv);
|
||||
|
||||
ret |= rsnd_platform_call(priv, dai, stop, ssi_id);
|
||||
|
||||
rsnd_dai_stream_quit(io);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -567,332 +586,157 @@ static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
|
||||
u32 tx_mask, u32 rx_mask,
|
||||
int slots, int slot_width)
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
|
||||
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
|
||||
switch (slots) {
|
||||
case 6:
|
||||
/* TDM Extend Mode */
|
||||
rsnd_set_slot(rdai, slots, 1);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "unsupported TDM slots (%d)\n", slots);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
|
||||
.trigger = rsnd_soc_dai_trigger,
|
||||
.set_fmt = rsnd_soc_dai_set_fmt,
|
||||
.set_tdm_slot = rsnd_soc_set_dai_tdm_slot,
|
||||
};
|
||||
|
||||
#define rsnd_path_add(priv, io, type) \
|
||||
({ \
|
||||
struct rsnd_mod *mod; \
|
||||
int ret = 0; \
|
||||
int id = -1; \
|
||||
\
|
||||
if (rsnd_is_enable_path(io, type)) { \
|
||||
id = rsnd_info_id(priv, io, type); \
|
||||
if (id >= 0) { \
|
||||
mod = rsnd_##type##_mod_get(priv, id); \
|
||||
ret = rsnd_dai_connect(mod, io); \
|
||||
} \
|
||||
} \
|
||||
ret; \
|
||||
})
|
||||
|
||||
#define rsnd_path_remove(priv, io, type) \
|
||||
{ \
|
||||
struct rsnd_mod *mod; \
|
||||
int id = -1; \
|
||||
\
|
||||
if (rsnd_is_enable_path(io, type)) { \
|
||||
id = rsnd_info_id(priv, io, type); \
|
||||
if (id >= 0) { \
|
||||
mod = rsnd_##type##_mod_get(priv, id); \
|
||||
rsnd_dai_disconnect(mod, io); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
void rsnd_path_parse(struct rsnd_priv *priv,
|
||||
struct rsnd_dai_stream *io)
|
||||
void rsnd_parse_connect_common(struct rsnd_dai *rdai,
|
||||
struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
|
||||
struct device_node *node,
|
||||
struct device_node *playback,
|
||||
struct device_node *capture)
|
||||
{
|
||||
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
|
||||
struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
|
||||
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
|
||||
struct rsnd_mod *cmd;
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
u32 data;
|
||||
struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
|
||||
struct device_node *np;
|
||||
struct rsnd_mod *mod;
|
||||
int i;
|
||||
|
||||
/* Gen1 is not supported */
|
||||
if (rsnd_is_gen1(priv))
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
if (!mix && !dvc)
|
||||
return;
|
||||
|
||||
if (mix) {
|
||||
struct rsnd_dai *rdai;
|
||||
int i;
|
||||
u32 path[] = {
|
||||
[0] = 0,
|
||||
[1] = 1 << 0,
|
||||
[2] = 0,
|
||||
[3] = 0,
|
||||
[4] = 0,
|
||||
[5] = 1 << 8
|
||||
};
|
||||
|
||||
/*
|
||||
* it is assuming that integrater is well understanding about
|
||||
* data path. Here doesn't check impossible connection,
|
||||
* like src2 + src5
|
||||
*/
|
||||
data = 0;
|
||||
for_each_rsnd_dai(rdai, priv, i) {
|
||||
io = &rdai->playback;
|
||||
if (mix == rsnd_io_to_mod_mix(io))
|
||||
data |= path[rsnd_mod_id(src)];
|
||||
|
||||
io = &rdai->capture;
|
||||
if (mix == rsnd_io_to_mod_mix(io))
|
||||
data |= path[rsnd_mod_id(src)];
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't use ctu = rsnd_io_ctu() here.
|
||||
* Since, ID of dvc/mix are 0 or 1 (= same as CMD number)
|
||||
* but ctu IDs are 0 - 7 (= CTU00 - CTU13)
|
||||
*/
|
||||
cmd = mix;
|
||||
} else {
|
||||
u32 path[] = {
|
||||
[0] = 0x30000,
|
||||
[1] = 0x30001,
|
||||
[2] = 0x40000,
|
||||
[3] = 0x10000,
|
||||
[4] = 0x20000,
|
||||
[5] = 0x40100
|
||||
};
|
||||
|
||||
data = path[rsnd_mod_id(src)];
|
||||
|
||||
cmd = dvc;
|
||||
i = 0;
|
||||
for_each_child_of_node(node, np) {
|
||||
mod = mod_get(priv, i);
|
||||
if (np == playback)
|
||||
rsnd_dai_connect(mod, &rdai->playback, mod->type);
|
||||
if (np == capture)
|
||||
rsnd_dai_connect(mod, &rdai->capture, mod->type);
|
||||
i++;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "ctu/mix path = 0x%08x", data);
|
||||
|
||||
rsnd_mod_write(cmd, CMD_ROUTE_SLCT, data);
|
||||
|
||||
rsnd_mod_write(cmd, CMD_CTRL, 0x10);
|
||||
of_node_put(node);
|
||||
}
|
||||
|
||||
static int rsnd_path_init(struct rsnd_priv *priv,
|
||||
struct rsnd_dai *rdai,
|
||||
struct rsnd_dai_stream *io)
|
||||
static int rsnd_dai_probe(struct rsnd_priv *priv)
|
||||
{
|
||||
struct device_node *dai_node;
|
||||
struct device_node *dai_np;
|
||||
struct device_node *playback, *capture;
|
||||
struct rsnd_dai_stream *io_playback;
|
||||
struct rsnd_dai_stream *io_capture;
|
||||
struct snd_soc_dai_driver *rdrv, *drv;
|
||||
struct rsnd_dai *rdai;
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
int nr, dai_i, io_i;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Gen1 is created by SRU/SSI, and this SRU is base module of
|
||||
* Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU)
|
||||
*
|
||||
* Easy image is..
|
||||
* Gen1 SRU = Gen2 SCU + SSIU + etc
|
||||
*
|
||||
* Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is
|
||||
* using fixed path.
|
||||
*/
|
||||
|
||||
/* SSI */
|
||||
ret = rsnd_path_add(priv, io, ssi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* SRC */
|
||||
ret = rsnd_path_add(priv, io, src);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* CTU */
|
||||
ret = rsnd_path_add(priv, io, ctu);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* MIX */
|
||||
ret = rsnd_path_add(priv, io, mix);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* DVC */
|
||||
ret = rsnd_path_add(priv, io, dvc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rsnd_of_parse_dai(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct device_node *dai_node, *dai_np;
|
||||
struct device_node *ssi_node, *ssi_np;
|
||||
struct device_node *src_node, *src_np;
|
||||
struct device_node *ctu_node, *ctu_np;
|
||||
struct device_node *mix_node, *mix_np;
|
||||
struct device_node *dvc_node, *dvc_np;
|
||||
struct device_node *playback, *capture;
|
||||
struct rsnd_dai_platform_info *dai_info;
|
||||
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
|
||||
struct device *dev = &pdev->dev;
|
||||
int nr, i;
|
||||
int dai_i, ssi_i, src_i, ctu_i, mix_i, dvc_i;
|
||||
|
||||
if (!of_data)
|
||||
return;
|
||||
|
||||
dai_node = of_get_child_by_name(dev->of_node, "rcar_sound,dai");
|
||||
if (!dai_node)
|
||||
return;
|
||||
|
||||
dai_node = rsnd_dai_of_node(priv);
|
||||
nr = of_get_child_count(dai_node);
|
||||
if (!nr)
|
||||
return;
|
||||
|
||||
dai_info = devm_kzalloc(dev,
|
||||
sizeof(struct rsnd_dai_platform_info) * nr,
|
||||
GFP_KERNEL);
|
||||
if (!dai_info) {
|
||||
dev_err(dev, "dai info allocation error\n");
|
||||
return;
|
||||
if (!nr) {
|
||||
ret = -EINVAL;
|
||||
goto rsnd_dai_probe_done;
|
||||
}
|
||||
|
||||
info->dai_info_nr = nr;
|
||||
info->dai_info = dai_info;
|
||||
rdrv = devm_kzalloc(dev, sizeof(*rdrv) * nr, GFP_KERNEL);
|
||||
rdai = devm_kzalloc(dev, sizeof(*rdai) * nr, GFP_KERNEL);
|
||||
if (!rdrv || !rdai) {
|
||||
ret = -ENOMEM;
|
||||
goto rsnd_dai_probe_done;
|
||||
}
|
||||
|
||||
ssi_node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi");
|
||||
src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src");
|
||||
ctu_node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu");
|
||||
mix_node = of_get_child_by_name(dev->of_node, "rcar_sound,mix");
|
||||
dvc_node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc");
|
||||
|
||||
#define mod_parse(name) \
|
||||
if (name##_node) { \
|
||||
struct rsnd_##name##_platform_info *name##_info; \
|
||||
\
|
||||
name##_i = 0; \
|
||||
for_each_child_of_node(name##_node, name##_np) { \
|
||||
name##_info = info->name##_info + name##_i; \
|
||||
\
|
||||
if (name##_np == playback) \
|
||||
dai_info->playback.name = name##_info; \
|
||||
if (name##_np == capture) \
|
||||
dai_info->capture.name = name##_info; \
|
||||
\
|
||||
name##_i++; \
|
||||
} \
|
||||
}
|
||||
priv->rdai_nr = nr;
|
||||
priv->daidrv = rdrv;
|
||||
priv->rdai = rdai;
|
||||
|
||||
/*
|
||||
* parse all dai
|
||||
*/
|
||||
dai_i = 0;
|
||||
for_each_child_of_node(dai_node, dai_np) {
|
||||
dai_info = info->dai_info + dai_i;
|
||||
rdai = rsnd_rdai_get(priv, dai_i);
|
||||
drv = rdrv + dai_i;
|
||||
io_playback = &rdai->playback;
|
||||
io_capture = &rdai->capture;
|
||||
|
||||
for (i = 0;; i++) {
|
||||
snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i);
|
||||
|
||||
playback = of_parse_phandle(dai_np, "playback", i);
|
||||
capture = of_parse_phandle(dai_np, "capture", i);
|
||||
rdai->priv = priv;
|
||||
drv->name = rdai->name;
|
||||
drv->ops = &rsnd_soc_dai_ops;
|
||||
|
||||
snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE,
|
||||
"DAI%d Playback", dai_i);
|
||||
drv->playback.rates = RSND_RATES;
|
||||
drv->playback.formats = RSND_FMTS;
|
||||
drv->playback.channels_min = 2;
|
||||
drv->playback.channels_max = 6;
|
||||
drv->playback.stream_name = rdai->playback.name;
|
||||
|
||||
snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE,
|
||||
"DAI%d Capture", dai_i);
|
||||
drv->capture.rates = RSND_RATES;
|
||||
drv->capture.formats = RSND_FMTS;
|
||||
drv->capture.channels_min = 2;
|
||||
drv->capture.channels_max = 6;
|
||||
drv->capture.stream_name = rdai->capture.name;
|
||||
|
||||
rdai->playback.rdai = rdai;
|
||||
rdai->capture.rdai = rdai;
|
||||
rsnd_set_slot(rdai, 2, 1); /* default */
|
||||
|
||||
for (io_i = 0;; io_i++) {
|
||||
playback = of_parse_phandle(dai_np, "playback", io_i);
|
||||
capture = of_parse_phandle(dai_np, "capture", io_i);
|
||||
|
||||
if (!playback && !capture)
|
||||
break;
|
||||
|
||||
mod_parse(ssi);
|
||||
mod_parse(src);
|
||||
mod_parse(ctu);
|
||||
mod_parse(mix);
|
||||
mod_parse(dvc);
|
||||
rsnd_parse_connect_ssi(rdai, playback, capture);
|
||||
rsnd_parse_connect_src(rdai, playback, capture);
|
||||
rsnd_parse_connect_ctu(rdai, playback, capture);
|
||||
rsnd_parse_connect_mix(rdai, playback, capture);
|
||||
rsnd_parse_connect_dvc(rdai, playback, capture);
|
||||
|
||||
of_node_put(playback);
|
||||
of_node_put(capture);
|
||||
}
|
||||
|
||||
dai_i++;
|
||||
}
|
||||
}
|
||||
|
||||
static int rsnd_dai_probe(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct snd_soc_dai_driver *drv;
|
||||
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
|
||||
struct rsnd_dai *rdai;
|
||||
struct rsnd_ssi_platform_info *pmod, *cmod;
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
int dai_nr;
|
||||
int i;
|
||||
|
||||
rsnd_of_parse_dai(pdev, of_data, priv);
|
||||
|
||||
dai_nr = info->dai_info_nr;
|
||||
if (!dai_nr) {
|
||||
dev_err(dev, "no dai\n");
|
||||
return -EIO;
|
||||
dev_dbg(dev, "%s (%s/%s)\n", rdai->name,
|
||||
rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ",
|
||||
rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- ");
|
||||
}
|
||||
|
||||
drv = devm_kzalloc(dev, sizeof(*drv) * dai_nr, GFP_KERNEL);
|
||||
rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL);
|
||||
if (!drv || !rdai) {
|
||||
dev_err(dev, "dai allocate failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
priv->rdai_nr = dai_nr;
|
||||
priv->daidrv = drv;
|
||||
priv->rdai = rdai;
|
||||
rsnd_dai_probe_done:
|
||||
of_node_put(dai_node);
|
||||
|
||||
for (i = 0; i < dai_nr; i++) {
|
||||
|
||||
pmod = info->dai_info[i].playback.ssi;
|
||||
cmod = info->dai_info[i].capture.ssi;
|
||||
|
||||
/*
|
||||
* init rsnd_dai
|
||||
*/
|
||||
snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i);
|
||||
rdai[i].priv = priv;
|
||||
|
||||
/*
|
||||
* init snd_soc_dai_driver
|
||||
*/
|
||||
drv[i].name = rdai[i].name;
|
||||
drv[i].ops = &rsnd_soc_dai_ops;
|
||||
if (pmod) {
|
||||
snprintf(rdai[i].playback.name, RSND_DAI_NAME_SIZE,
|
||||
"DAI%d Playback", i);
|
||||
|
||||
drv[i].playback.rates = RSND_RATES;
|
||||
drv[i].playback.formats = RSND_FMTS;
|
||||
drv[i].playback.channels_min = 2;
|
||||
drv[i].playback.channels_max = 2;
|
||||
drv[i].playback.stream_name = rdai[i].playback.name;
|
||||
|
||||
rdai[i].playback.info = &info->dai_info[i].playback;
|
||||
rdai[i].playback.rdai = rdai + i;
|
||||
rsnd_path_init(priv, &rdai[i], &rdai[i].playback);
|
||||
}
|
||||
if (cmod) {
|
||||
snprintf(rdai[i].capture.name, RSND_DAI_NAME_SIZE,
|
||||
"DAI%d Capture", i);
|
||||
|
||||
drv[i].capture.rates = RSND_RATES;
|
||||
drv[i].capture.formats = RSND_FMTS;
|
||||
drv[i].capture.channels_min = 2;
|
||||
drv[i].capture.channels_max = 2;
|
||||
drv[i].capture.stream_name = rdai[i].capture.name;
|
||||
|
||||
rdai[i].capture.info = &info->dai_info[i].capture;
|
||||
rdai[i].capture.rdai = rdai + i;
|
||||
rsnd_path_init(priv, &rdai[i], &rdai[i].capture);
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s (%s/%s)\n", rdai[i].name,
|
||||
pmod ? "play" : " -- ",
|
||||
cmod ? "capture" : " -- ");
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1033,7 +877,6 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
|
|||
void (*update)(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod))
|
||||
{
|
||||
struct snd_soc_card *soc_card = rtd->card;
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_kcontrol *kctrl;
|
||||
struct snd_kcontrol_new knew = {
|
||||
|
|
@ -1077,10 +920,14 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod,
|
|||
void (*update)(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod),
|
||||
struct rsnd_kctrl_cfg_m *_cfg,
|
||||
int ch_size,
|
||||
u32 max)
|
||||
{
|
||||
if (ch_size > RSND_DVC_CHANNELS)
|
||||
return -EINVAL;
|
||||
|
||||
_cfg->cfg.max = max;
|
||||
_cfg->cfg.size = RSND_DVC_CHANNELS;
|
||||
_cfg->cfg.size = ch_size;
|
||||
_cfg->cfg.val = _cfg->val;
|
||||
return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update);
|
||||
}
|
||||
|
|
@ -1161,6 +1008,9 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
|
|||
|
||||
ret = rsnd_dai_call(probe, io, priv);
|
||||
if (ret == -EAGAIN) {
|
||||
struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Fallback to PIO mode
|
||||
*/
|
||||
|
|
@ -1175,10 +1025,12 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
|
|||
rsnd_dai_call(remove, io, priv);
|
||||
|
||||
/*
|
||||
* remove SRC/DVC from DAI,
|
||||
* remove all mod from io
|
||||
* and, re connect ssi
|
||||
*/
|
||||
rsnd_path_remove(priv, io, src);
|
||||
rsnd_path_remove(priv, io, dvc);
|
||||
for (i = 0; i < RSND_MOD_MAX; i++)
|
||||
rsnd_dai_disconnect((io)->mod[i], io, i);
|
||||
rsnd_dai_connect(ssi_mod, io, RSND_MOD_SSI);
|
||||
|
||||
/*
|
||||
* fallback
|
||||
|
|
@ -1200,33 +1052,25 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
|
|||
*/
|
||||
static int rsnd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rcar_snd_info *info;
|
||||
struct rsnd_priv *priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rsnd_dai *rdai;
|
||||
const struct of_device_id *of_id = of_match_device(rsnd_of_match, dev);
|
||||
const struct rsnd_of_data *of_data;
|
||||
int (*probe_func[])(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv) = {
|
||||
int (*probe_func[])(struct rsnd_priv *priv) = {
|
||||
rsnd_gen_probe,
|
||||
rsnd_dma_probe,
|
||||
rsnd_ssi_probe,
|
||||
rsnd_ssiu_probe,
|
||||
rsnd_src_probe,
|
||||
rsnd_ctu_probe,
|
||||
rsnd_mix_probe,
|
||||
rsnd_dvc_probe,
|
||||
rsnd_cmd_probe,
|
||||
rsnd_adg_probe,
|
||||
rsnd_dai_probe,
|
||||
};
|
||||
int ret, i;
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(struct rcar_snd_info),
|
||||
GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
of_data = of_id->data;
|
||||
|
||||
/*
|
||||
* init priv data
|
||||
*/
|
||||
|
|
@ -1237,14 +1081,14 @@ static int rsnd_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
priv->pdev = pdev;
|
||||
priv->info = info;
|
||||
priv->flags = (unsigned long)of_id->data;
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
/*
|
||||
* init each module
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(probe_func); i++) {
|
||||
ret = probe_func[i](pdev, of_data, priv);
|
||||
ret = probe_func[i](priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1297,13 +1141,15 @@ static int rsnd_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev);
|
||||
struct rsnd_dai *rdai;
|
||||
void (*remove_func[])(struct platform_device *pdev,
|
||||
struct rsnd_priv *priv) = {
|
||||
void (*remove_func[])(struct rsnd_priv *priv) = {
|
||||
rsnd_ssi_remove,
|
||||
rsnd_ssiu_remove,
|
||||
rsnd_src_remove,
|
||||
rsnd_ctu_remove,
|
||||
rsnd_mix_remove,
|
||||
rsnd_dvc_remove,
|
||||
rsnd_cmd_remove,
|
||||
rsnd_adg_remove,
|
||||
};
|
||||
int ret = 0, i;
|
||||
|
||||
|
|
@ -1315,7 +1161,7 @@ static int rsnd_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(remove_func); i++)
|
||||
remove_func[i](pdev, priv);
|
||||
remove_func[i](priv);
|
||||
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
#define CTU_NAME "ctu"
|
||||
|
||||
struct rsnd_ctu {
|
||||
struct rsnd_ctu_platform_info *info; /* rcar_snd.h */
|
||||
struct rsnd_mod mod;
|
||||
};
|
||||
|
||||
|
|
@ -24,6 +23,7 @@ struct rsnd_ctu {
|
|||
((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \
|
||||
i++)
|
||||
|
||||
#define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id)
|
||||
#define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1)
|
||||
#define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0)
|
||||
static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable)
|
||||
|
|
@ -31,6 +31,13 @@ static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable)
|
|||
rsnd_mod_write(mod, CTU_CTUIR, enable);
|
||||
}
|
||||
|
||||
static int rsnd_ctu_probe_(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
return rsnd_cmd_attach(io, rsnd_mod_id(mod) / 4);
|
||||
}
|
||||
|
||||
static int rsnd_ctu_init(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
|
|
@ -57,6 +64,7 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod,
|
|||
|
||||
static struct rsnd_mod_ops rsnd_ctu_ops = {
|
||||
.name = CTU_NAME,
|
||||
.probe = rsnd_ctu_probe_,
|
||||
.init = rsnd_ctu_init,
|
||||
.quit = rsnd_ctu_quit,
|
||||
};
|
||||
|
|
@ -66,51 +74,13 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
|
|||
if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv)))
|
||||
id = 0;
|
||||
|
||||
return rsnd_mod_get((struct rsnd_ctu *)(priv->ctu) + id);
|
||||
return rsnd_mod_get(rsnd_ctu_get(priv, id));
|
||||
}
|
||||
|
||||
static void rsnd_of_parse_ctu(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv)
|
||||
int rsnd_ctu_probe(struct rsnd_priv *priv)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct rsnd_ctu_platform_info *ctu_info;
|
||||
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
|
||||
struct device *dev = &pdev->dev;
|
||||
int nr;
|
||||
|
||||
if (!of_data)
|
||||
return;
|
||||
|
||||
node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu");
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
nr = of_get_child_count(node);
|
||||
if (!nr)
|
||||
goto rsnd_of_parse_ctu_end;
|
||||
|
||||
ctu_info = devm_kzalloc(dev,
|
||||
sizeof(struct rsnd_ctu_platform_info) * nr,
|
||||
GFP_KERNEL);
|
||||
if (!ctu_info) {
|
||||
dev_err(dev, "ctu info allocation error\n");
|
||||
goto rsnd_of_parse_ctu_end;
|
||||
}
|
||||
|
||||
info->ctu_info = ctu_info;
|
||||
info->ctu_info_nr = nr;
|
||||
|
||||
rsnd_of_parse_ctu_end:
|
||||
of_node_put(node);
|
||||
|
||||
}
|
||||
|
||||
int rsnd_ctu_probe(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
|
||||
struct device_node *np;
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct rsnd_ctu *ctu;
|
||||
struct clk *clk;
|
||||
|
|
@ -121,20 +91,30 @@ int rsnd_ctu_probe(struct platform_device *pdev,
|
|||
if (rsnd_is_gen1(priv))
|
||||
return 0;
|
||||
|
||||
rsnd_of_parse_ctu(pdev, of_data, priv);
|
||||
node = rsnd_ctu_of_node(priv);
|
||||
if (!node)
|
||||
return 0; /* not used is not error */
|
||||
|
||||
nr = info->ctu_info_nr;
|
||||
if (!nr)
|
||||
return 0;
|
||||
nr = of_get_child_count(node);
|
||||
if (!nr) {
|
||||
ret = -EINVAL;
|
||||
goto rsnd_ctu_probe_done;
|
||||
}
|
||||
|
||||
ctu = devm_kzalloc(dev, sizeof(*ctu) * nr, GFP_KERNEL);
|
||||
if (!ctu)
|
||||
return -ENOMEM;
|
||||
if (!ctu) {
|
||||
ret = -ENOMEM;
|
||||
goto rsnd_ctu_probe_done;
|
||||
}
|
||||
|
||||
priv->ctu_nr = nr;
|
||||
priv->ctu = ctu;
|
||||
|
||||
for_each_rsnd_ctu(ctu, priv, i) {
|
||||
i = 0;
|
||||
ret = 0;
|
||||
for_each_child_of_node(node, np) {
|
||||
ctu = rsnd_ctu_get(priv, i);
|
||||
|
||||
/*
|
||||
* CTU00, CTU01, CTU02, CTU03 => CTU0
|
||||
* CTU10, CTU11, CTU12, CTU13 => CTU1
|
||||
|
|
@ -143,22 +123,27 @@ int rsnd_ctu_probe(struct platform_device *pdev,
|
|||
CTU_NAME, i / 4);
|
||||
|
||||
clk = devm_clk_get(dev, name);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
ctu->info = &info->ctu_info[i];
|
||||
if (IS_ERR(clk)) {
|
||||
ret = PTR_ERR(clk);
|
||||
goto rsnd_ctu_probe_done;
|
||||
}
|
||||
|
||||
ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
|
||||
clk, RSND_MOD_CTU, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto rsnd_ctu_probe_done;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
rsnd_ctu_probe_done:
|
||||
of_node_put(node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rsnd_ctu_remove(struct platform_device *pdev,
|
||||
struct rsnd_priv *priv)
|
||||
void rsnd_ctu_remove(struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_ctu *ctu;
|
||||
int i;
|
||||
|
|
|
|||
|
|
@ -22,21 +22,36 @@
|
|||
/* PDMACHCR */
|
||||
#define PDMACHCR_DE (1 << 0)
|
||||
|
||||
|
||||
struct rsnd_dmaen {
|
||||
struct dma_chan *chan;
|
||||
};
|
||||
|
||||
struct rsnd_dmapp {
|
||||
int dmapp_id;
|
||||
u32 chcr;
|
||||
};
|
||||
|
||||
struct rsnd_dma {
|
||||
struct rsnd_mod mod;
|
||||
dma_addr_t src_addr;
|
||||
dma_addr_t dst_addr;
|
||||
union {
|
||||
struct rsnd_dmaen en;
|
||||
struct rsnd_dmapp pp;
|
||||
} dma;
|
||||
};
|
||||
|
||||
struct rsnd_dma_ctrl {
|
||||
void __iomem *base;
|
||||
int dmaen_num;
|
||||
int dmapp_num;
|
||||
};
|
||||
|
||||
struct rsnd_dma_ops {
|
||||
char *name;
|
||||
void (*start)(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
|
||||
void (*stop)(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
|
||||
int (*init)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id,
|
||||
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to);
|
||||
void (*quit)(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
|
||||
};
|
||||
|
||||
#define rsnd_priv_to_dmac(p) ((struct rsnd_dma_ctrl *)(p)->dma)
|
||||
#define rsnd_mod_to_dma(_mod) container_of((_mod), struct rsnd_dma, mod)
|
||||
#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en)
|
||||
#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp)
|
||||
|
||||
/*
|
||||
* Audio DMAC
|
||||
|
|
@ -77,18 +92,24 @@ static void rsnd_dmaen_complete(void *data)
|
|||
rsnd_mod_interrupt(mod, __rsnd_dmaen_complete);
|
||||
}
|
||||
|
||||
static void rsnd_dmaen_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
|
||||
static int rsnd_dmaen_stop(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
|
||||
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
|
||||
|
||||
dmaengine_terminate_all(dmaen->chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rsnd_dmaen_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
|
||||
static int rsnd_dmaen_start(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
|
||||
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
|
||||
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct snd_pcm_substream *substream = io->substream;
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
|
|
@ -103,18 +124,20 @@ static void rsnd_dmaen_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
|
|||
|
||||
if (!desc) {
|
||||
dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
|
||||
return;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
desc->callback = rsnd_dmaen_complete;
|
||||
desc->callback_param = mod;
|
||||
desc->callback_param = rsnd_mod_get(dma);
|
||||
|
||||
if (dmaengine_submit(desc) < 0) {
|
||||
dev_err(dev, "dmaengine_submit() fail\n");
|
||||
return;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dma_async_issue_pending(dmaen->chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
|
||||
|
|
@ -152,12 +175,29 @@ static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io,
|
|||
return rsnd_mod_dma_req(io, mod_to);
|
||||
}
|
||||
|
||||
static int rsnd_dmaen_init(struct rsnd_dai_stream *io,
|
||||
static int rsnd_dmaen_remove(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
|
||||
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
|
||||
|
||||
if (dmaen->chan)
|
||||
dma_release_channel(dmaen->chan);
|
||||
|
||||
dmaen->chan = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_dmaen_attach(struct rsnd_dai_stream *io,
|
||||
struct rsnd_dma *dma, int id,
|
||||
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
|
||||
{
|
||||
struct rsnd_mod *mod = rsnd_mod_get(dma);
|
||||
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
|
||||
struct rsnd_priv *priv = rsnd_io_to_priv(io);
|
||||
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct dma_slave_config cfg = {};
|
||||
int is_play = rsnd_io_is_play(io);
|
||||
|
|
@ -191,18 +231,20 @@ static int rsnd_dmaen_init(struct rsnd_dai_stream *io,
|
|||
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
|
||||
dev_dbg(dev, "%s %pad -> %pad\n",
|
||||
dma->ops->name,
|
||||
dev_dbg(dev, "%s[%d] %pad -> %pad\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod),
|
||||
&cfg.src_addr, &cfg.dst_addr);
|
||||
|
||||
ret = dmaengine_slave_config(dmaen->chan, &cfg);
|
||||
if (ret < 0)
|
||||
goto rsnd_dma_init_err;
|
||||
goto rsnd_dma_attach_err;
|
||||
|
||||
dmac->dmaen_num++;
|
||||
|
||||
return 0;
|
||||
|
||||
rsnd_dma_init_err:
|
||||
rsnd_dma_quit(io, dma);
|
||||
rsnd_dma_attach_err:
|
||||
rsnd_dmaen_remove(mod, io, priv);
|
||||
rsnd_dma_channel_err:
|
||||
|
||||
/*
|
||||
|
|
@ -214,22 +256,11 @@ rsnd_dma_channel_err:
|
|||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static void rsnd_dmaen_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
|
||||
{
|
||||
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
|
||||
|
||||
if (dmaen->chan)
|
||||
dma_release_channel(dmaen->chan);
|
||||
|
||||
dmaen->chan = NULL;
|
||||
}
|
||||
|
||||
static struct rsnd_dma_ops rsnd_dmaen_ops = {
|
||||
static struct rsnd_mod_ops rsnd_dmaen_ops = {
|
||||
.name = "audmac",
|
||||
.start = rsnd_dmaen_start,
|
||||
.stop = rsnd_dmaen_stop,
|
||||
.init = rsnd_dmaen_init,
|
||||
.quit = rsnd_dmaen_quit,
|
||||
.remove = rsnd_dmaen_remove,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -307,7 +338,7 @@ static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io,
|
|||
(0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id))
|
||||
static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg)
|
||||
{
|
||||
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
|
||||
struct rsnd_mod *mod = rsnd_mod_get(dma);
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
|
|
@ -319,38 +350,48 @@ static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg)
|
|||
|
||||
static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg)
|
||||
{
|
||||
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
|
||||
struct rsnd_mod *mod = rsnd_mod_get(dma);
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
|
||||
|
||||
return ioread32(rsnd_dmapp_addr(dmac, dma, reg));
|
||||
}
|
||||
|
||||
static void rsnd_dmapp_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
|
||||
static int rsnd_dmapp_stop(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
|
||||
int i;
|
||||
|
||||
rsnd_dmapp_write(dma, 0, PDMACHCR);
|
||||
|
||||
for (i = 0; i < 1024; i++) {
|
||||
if (0 == rsnd_dmapp_read(dma, PDMACHCR))
|
||||
return;
|
||||
return 0;
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void rsnd_dmapp_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
|
||||
static int rsnd_dmapp_start(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
|
||||
struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
|
||||
|
||||
rsnd_dmapp_write(dma, dma->src_addr, PDMASAR);
|
||||
rsnd_dmapp_write(dma, dma->dst_addr, PDMADAR);
|
||||
rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_dmapp_init(struct rsnd_dai_stream *io,
|
||||
struct rsnd_dma *dma, int id,
|
||||
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
|
||||
static int rsnd_dmapp_attach(struct rsnd_dai_stream *io,
|
||||
struct rsnd_dma *dma, int id,
|
||||
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
|
||||
{
|
||||
struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
|
||||
struct rsnd_priv *priv = rsnd_io_to_priv(io);
|
||||
|
|
@ -362,19 +403,16 @@ static int rsnd_dmapp_init(struct rsnd_dai_stream *io,
|
|||
|
||||
dmac->dmapp_num++;
|
||||
|
||||
rsnd_dmapp_stop(io, dma);
|
||||
|
||||
dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n",
|
||||
dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rsnd_dma_ops rsnd_dmapp_ops = {
|
||||
static struct rsnd_mod_ops rsnd_dmapp_ops = {
|
||||
.name = "audmac-pp",
|
||||
.start = rsnd_dmapp_start,
|
||||
.stop = rsnd_dmapp_stop,
|
||||
.init = rsnd_dmapp_init,
|
||||
.quit = rsnd_dmapp_stop,
|
||||
};
|
||||
|
||||
|
|
@ -497,13 +535,12 @@ static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io,
|
|||
}
|
||||
|
||||
#define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */
|
||||
static void rsnd_dma_of_path(struct rsnd_dma *dma,
|
||||
static void rsnd_dma_of_path(struct rsnd_mod *this,
|
||||
struct rsnd_dai_stream *io,
|
||||
int is_play,
|
||||
struct rsnd_mod **mod_from,
|
||||
struct rsnd_mod **mod_to)
|
||||
{
|
||||
struct rsnd_mod *this = rsnd_dma_to_mod(dma);
|
||||
struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
|
||||
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
|
||||
struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io);
|
||||
|
|
@ -513,7 +550,7 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
|
|||
struct rsnd_mod *mod_start, *mod_end;
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(this);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
int nr, i;
|
||||
int nr, i, idx;
|
||||
|
||||
if (!ssi)
|
||||
return;
|
||||
|
|
@ -542,23 +579,24 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
|
|||
mod_start = (is_play) ? NULL : ssi;
|
||||
mod_end = (is_play) ? ssi : NULL;
|
||||
|
||||
mod[0] = mod_start;
|
||||
idx = 0;
|
||||
mod[idx++] = mod_start;
|
||||
for (i = 1; i < nr; i++) {
|
||||
if (src) {
|
||||
mod[i] = src;
|
||||
mod[idx++] = src;
|
||||
src = NULL;
|
||||
} else if (ctu) {
|
||||
mod[i] = ctu;
|
||||
mod[idx++] = ctu;
|
||||
ctu = NULL;
|
||||
} else if (mix) {
|
||||
mod[i] = mix;
|
||||
mod[idx++] = mix;
|
||||
mix = NULL;
|
||||
} else if (dvc) {
|
||||
mod[i] = dvc;
|
||||
mod[idx++] = dvc;
|
||||
dvc = NULL;
|
||||
}
|
||||
}
|
||||
mod[i] = mod_end;
|
||||
mod[idx] = mod_end;
|
||||
|
||||
/*
|
||||
* | SSI | SRC |
|
||||
|
|
@ -567,8 +605,8 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
|
|||
* !is_play | * | o |
|
||||
*/
|
||||
if ((this == ssi) == (is_play)) {
|
||||
*mod_from = mod[nr - 1];
|
||||
*mod_to = mod[nr];
|
||||
*mod_from = mod[idx - 1];
|
||||
*mod_to = mod[idx];
|
||||
} else {
|
||||
*mod_from = mod[0];
|
||||
*mod_to = mod[1];
|
||||
|
|
@ -576,7 +614,7 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
|
|||
|
||||
dev_dbg(dev, "module connection (this is %s[%d])\n",
|
||||
rsnd_mod_name(this), rsnd_mod_id(this));
|
||||
for (i = 0; i <= nr; i++) {
|
||||
for (i = 0; i <= idx; i++) {
|
||||
dev_dbg(dev, " %s[%d]%s\n",
|
||||
rsnd_mod_name(mod[i]), rsnd_mod_id(mod[i]),
|
||||
(mod[i] == *mod_from) ? " from" :
|
||||
|
|
@ -584,36 +622,22 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
|
|||
}
|
||||
}
|
||||
|
||||
void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
|
||||
{
|
||||
dma->ops->stop(io, dma);
|
||||
}
|
||||
|
||||
void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
|
||||
{
|
||||
dma->ops->start(io, dma);
|
||||
}
|
||||
|
||||
void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
|
||||
{
|
||||
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
|
||||
|
||||
if (!dmac)
|
||||
return;
|
||||
|
||||
dma->ops->quit(io, dma);
|
||||
}
|
||||
|
||||
int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id)
|
||||
struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod, int id)
|
||||
{
|
||||
struct rsnd_mod *dma_mod;
|
||||
struct rsnd_mod *mod_from = NULL;
|
||||
struct rsnd_mod *mod_to = NULL;
|
||||
struct rsnd_priv *priv = rsnd_io_to_priv(io);
|
||||
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
|
||||
struct rsnd_dma *dma;
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct rsnd_mod_ops *ops;
|
||||
enum rsnd_mod_type type;
|
||||
int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id,
|
||||
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to);
|
||||
int is_play = rsnd_io_is_play(io);
|
||||
int ret, dma_id;
|
||||
|
||||
/*
|
||||
* DMA failed. try to PIO mode
|
||||
|
|
@ -622,35 +646,64 @@ int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id)
|
|||
* rsnd_rdai_continuance_probe()
|
||||
*/
|
||||
if (!dmac)
|
||||
return -EAGAIN;
|
||||
return ERR_PTR(-EAGAIN);
|
||||
|
||||
rsnd_dma_of_path(dma, io, is_play, &mod_from, &mod_to);
|
||||
dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
|
||||
if (!dma)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to);
|
||||
|
||||
dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
|
||||
dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
|
||||
|
||||
/* for Gen2 */
|
||||
if (mod_from && mod_to)
|
||||
dma->ops = &rsnd_dmapp_ops;
|
||||
else
|
||||
dma->ops = &rsnd_dmaen_ops;
|
||||
if (mod_from && mod_to) {
|
||||
ops = &rsnd_dmapp_ops;
|
||||
attach = rsnd_dmapp_attach;
|
||||
dma_id = dmac->dmapp_num;
|
||||
type = RSND_MOD_AUDMAPP;
|
||||
} else {
|
||||
ops = &rsnd_dmaen_ops;
|
||||
attach = rsnd_dmaen_attach;
|
||||
dma_id = dmac->dmaen_num;
|
||||
type = RSND_MOD_AUDMA;
|
||||
}
|
||||
|
||||
/* for Gen1, overwrite */
|
||||
if (rsnd_is_gen1(priv))
|
||||
dma->ops = &rsnd_dmaen_ops;
|
||||
if (rsnd_is_gen1(priv)) {
|
||||
ops = &rsnd_dmaen_ops;
|
||||
attach = rsnd_dmaen_attach;
|
||||
dma_id = dmac->dmaen_num;
|
||||
type = RSND_MOD_AUDMA;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s %s[%d] -> %s[%d]\n",
|
||||
dma->ops->name,
|
||||
dma_mod = rsnd_mod_get(dma);
|
||||
|
||||
ret = rsnd_mod_init(priv, dma_mod,
|
||||
ops, NULL, type, dma_id);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n",
|
||||
rsnd_mod_name(dma_mod), rsnd_mod_id(dma_mod),
|
||||
rsnd_mod_name(mod_from), rsnd_mod_id(mod_from),
|
||||
rsnd_mod_name(mod_to), rsnd_mod_id(mod_to));
|
||||
|
||||
return dma->ops->init(io, dma, id, mod_from, mod_to);
|
||||
ret = attach(io, dma, id, mod_from, mod_to);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
ret = rsnd_dai_connect(dma_mod, io, type);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return rsnd_mod_get(dma);
|
||||
}
|
||||
|
||||
int rsnd_dma_probe(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv)
|
||||
int rsnd_dma_probe(struct rsnd_priv *priv)
|
||||
{
|
||||
struct platform_device *pdev = rsnd_priv_to_pdev(priv);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct rsnd_dma_ctrl *dmac;
|
||||
struct resource *res;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
#define DVC_NAME "dvc"
|
||||
|
||||
struct rsnd_dvc {
|
||||
struct rsnd_dvc_platform_info *info; /* rcar_snd.h */
|
||||
struct rsnd_mod mod;
|
||||
struct rsnd_kctrl_cfg_m volume;
|
||||
struct rsnd_kctrl_cfg_m mute;
|
||||
|
|
@ -24,6 +23,7 @@ struct rsnd_dvc {
|
|||
struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */
|
||||
};
|
||||
|
||||
#define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id)
|
||||
#define rsnd_dvc_nr(priv) ((priv)->dvc_nr)
|
||||
#define rsnd_dvc_of_node(priv) \
|
||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc")
|
||||
|
|
@ -64,79 +64,142 @@ static const char * const dvc_ramp_rate[] = {
|
|||
"0.125 dB/8192 steps", /* 10111 */
|
||||
};
|
||||
|
||||
static void rsnd_dvc_soft_reset(struct rsnd_mod *mod)
|
||||
static void rsnd_dvc_activation(struct rsnd_mod *mod)
|
||||
{
|
||||
rsnd_mod_write(mod, DVC_SWRSR, 0);
|
||||
rsnd_mod_write(mod, DVC_SWRSR, 1);
|
||||
}
|
||||
|
||||
#define rsnd_dvc_initialize_lock(mod) __rsnd_dvc_initialize_lock(mod, 1)
|
||||
#define rsnd_dvc_initialize_unlock(mod) __rsnd_dvc_initialize_lock(mod, 0)
|
||||
static void __rsnd_dvc_initialize_lock(struct rsnd_mod *mod, u32 enable)
|
||||
static void rsnd_dvc_halt(struct rsnd_mod *mod)
|
||||
{
|
||||
rsnd_mod_write(mod, DVC_DVUIR, enable);
|
||||
rsnd_mod_write(mod, DVC_DVUIR, 1);
|
||||
rsnd_mod_write(mod, DVC_SWRSR, 0);
|
||||
}
|
||||
|
||||
static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod)
|
||||
#define rsnd_dvc_get_vrpdr(dvc) (dvc->rup.val << 8 | dvc->rdown.val)
|
||||
#define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (dvc->volume.val[0] >> 13))
|
||||
|
||||
static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod)
|
||||
{
|
||||
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
|
||||
u32 val[RSND_DVC_CHANNELS];
|
||||
u32 dvucr = 0;
|
||||
u32 mute = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dvc->mute.cfg.size; i++)
|
||||
mute |= (!!dvc->mute.cfg.val[i]) << i;
|
||||
/* Enable Ramp */
|
||||
if (dvc->ren.val)
|
||||
for (i = 0; i < RSND_DVC_CHANNELS; i++)
|
||||
val[i] = dvc->volume.cfg.max;
|
||||
else
|
||||
for (i = 0; i < RSND_DVC_CHANNELS; i++)
|
||||
val[i] = dvc->volume.val[i];
|
||||
|
||||
/* Disable DVC Register access */
|
||||
rsnd_mod_write(mod, DVC_DVUER, 0);
|
||||
/* Enable Digital Volume */
|
||||
rsnd_mod_write(mod, DVC_VOL0R, val[0]);
|
||||
rsnd_mod_write(mod, DVC_VOL1R, val[1]);
|
||||
rsnd_mod_write(mod, DVC_VOL2R, val[2]);
|
||||
rsnd_mod_write(mod, DVC_VOL3R, val[3]);
|
||||
rsnd_mod_write(mod, DVC_VOL4R, val[4]);
|
||||
rsnd_mod_write(mod, DVC_VOL5R, val[5]);
|
||||
rsnd_mod_write(mod, DVC_VOL6R, val[6]);
|
||||
rsnd_mod_write(mod, DVC_VOL7R, val[7]);
|
||||
}
|
||||
|
||||
static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod)
|
||||
{
|
||||
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
|
||||
u32 adinr = 0;
|
||||
u32 dvucr = 0;
|
||||
u32 vrctr = 0;
|
||||
u32 vrpdr = 0;
|
||||
u32 vrdbr = 0;
|
||||
|
||||
adinr = rsnd_get_adinr_bit(mod, io) |
|
||||
rsnd_get_adinr_chan(mod, io);
|
||||
|
||||
/* Enable Digital Volume, Zero Cross Mute Mode */
|
||||
dvucr |= 0x101;
|
||||
|
||||
/* Enable Ramp */
|
||||
if (dvc->ren.val) {
|
||||
dvucr |= 0x10;
|
||||
|
||||
/* Digital Volume Max */
|
||||
for (i = 0; i < RSND_DVC_CHANNELS; i++)
|
||||
val[i] = dvc->volume.cfg.max;
|
||||
|
||||
rsnd_mod_write(mod, DVC_VRCTR, 0xff);
|
||||
rsnd_mod_write(mod, DVC_VRPDR, dvc->rup.val << 8 |
|
||||
dvc->rdown.val);
|
||||
/*
|
||||
* FIXME !!
|
||||
* use scale-downed Digital Volume
|
||||
* as Volume Ramp
|
||||
* 7F FFFF -> 3FF
|
||||
*/
|
||||
rsnd_mod_write(mod, DVC_VRDBR,
|
||||
0x3ff - (dvc->volume.val[0] >> 13));
|
||||
|
||||
} else {
|
||||
for (i = 0; i < RSND_DVC_CHANNELS; i++)
|
||||
val[i] = dvc->volume.val[i];
|
||||
vrctr = 0xff;
|
||||
vrpdr = rsnd_dvc_get_vrpdr(dvc);
|
||||
vrdbr = rsnd_dvc_get_vrdbr(dvc);
|
||||
}
|
||||
|
||||
/* Enable Digital Volume */
|
||||
dvucr |= 0x100;
|
||||
rsnd_mod_write(mod, DVC_VOL0R, val[0]);
|
||||
rsnd_mod_write(mod, DVC_VOL1R, val[1]);
|
||||
|
||||
/* Enable Mute */
|
||||
if (mute) {
|
||||
dvucr |= 0x1;
|
||||
rsnd_mod_write(mod, DVC_ZCMCR, mute);
|
||||
}
|
||||
/* Initialize operation */
|
||||
rsnd_mod_write(mod, DVC_DVUIR, 1);
|
||||
|
||||
/* General Information */
|
||||
rsnd_mod_write(mod, DVC_ADINR, adinr);
|
||||
rsnd_mod_write(mod, DVC_DVUCR, dvucr);
|
||||
|
||||
/* Volume Ramp Parameter */
|
||||
rsnd_mod_write(mod, DVC_VRCTR, vrctr);
|
||||
rsnd_mod_write(mod, DVC_VRPDR, vrpdr);
|
||||
rsnd_mod_write(mod, DVC_VRDBR, vrdbr);
|
||||
|
||||
/* Digital Volume Function Parameter */
|
||||
rsnd_dvc_volume_parameter(io, mod);
|
||||
|
||||
/* cancel operation */
|
||||
rsnd_mod_write(mod, DVC_DVUIR, 0);
|
||||
}
|
||||
|
||||
static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod)
|
||||
{
|
||||
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
|
||||
u32 zcmcr = 0;
|
||||
u32 vrpdr = 0;
|
||||
u32 vrdbr = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dvc->mute.cfg.size; i++)
|
||||
zcmcr |= (!!dvc->mute.cfg.val[i]) << i;
|
||||
|
||||
if (dvc->ren.val) {
|
||||
vrpdr = rsnd_dvc_get_vrpdr(dvc);
|
||||
vrdbr = rsnd_dvc_get_vrdbr(dvc);
|
||||
}
|
||||
|
||||
/* Disable DVC Register access */
|
||||
rsnd_mod_write(mod, DVC_DVUER, 0);
|
||||
|
||||
/* Zero Cross Mute Function */
|
||||
rsnd_mod_write(mod, DVC_ZCMCR, zcmcr);
|
||||
|
||||
/* Volume Ramp Function */
|
||||
rsnd_mod_write(mod, DVC_VRPDR, vrpdr);
|
||||
rsnd_mod_write(mod, DVC_VRDBR, vrdbr);
|
||||
/* add DVC_VRWTR here */
|
||||
|
||||
/* Digital Volume Function Parameter */
|
||||
rsnd_dvc_volume_parameter(io, mod);
|
||||
|
||||
/* Enable DVC Register access */
|
||||
rsnd_mod_write(mod, DVC_DVUER, 1);
|
||||
}
|
||||
|
||||
static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
static int rsnd_dvc_probe_(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
return rsnd_cmd_attach(io, rsnd_mod_id(mod));
|
||||
}
|
||||
|
||||
static int rsnd_dvc_remove_(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
|
||||
|
||||
|
|
@ -155,19 +218,12 @@ static int rsnd_dvc_init(struct rsnd_mod *mod,
|
|||
{
|
||||
rsnd_mod_power_on(mod);
|
||||
|
||||
rsnd_dvc_soft_reset(mod);
|
||||
rsnd_dvc_activation(mod);
|
||||
|
||||
rsnd_dvc_initialize_lock(mod);
|
||||
rsnd_dvc_volume_init(io, mod);
|
||||
|
||||
rsnd_path_parse(priv, io);
|
||||
|
||||
rsnd_mod_write(mod, DVC_ADINR, rsnd_get_adinr_bit(mod, io));
|
||||
|
||||
/* ch0/ch1 Volume */
|
||||
rsnd_dvc_volume_update(io, mod);
|
||||
|
||||
rsnd_adg_set_cmd_timsel_gen2(mod, io);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -175,37 +231,20 @@ static int rsnd_dvc_quit(struct rsnd_mod *mod,
|
|||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
rsnd_dvc_halt(mod);
|
||||
|
||||
rsnd_mod_power_off(mod);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_dvc_start(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
rsnd_dvc_initialize_unlock(mod);
|
||||
|
||||
rsnd_mod_write(mod, CMD_CTRL, 0x10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_dvc_stop(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
rsnd_mod_write(mod, CMD_CTRL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
|
||||
int is_play = rsnd_io_is_play(io);
|
||||
int slots = rsnd_get_slot(io);
|
||||
int ret;
|
||||
|
||||
/* Volume */
|
||||
|
|
@ -213,7 +252,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
|
|||
is_play ?
|
||||
"DVC Out Playback Volume" : "DVC In Capture Volume",
|
||||
rsnd_dvc_volume_update,
|
||||
&dvc->volume, 0x00800000 - 1);
|
||||
&dvc->volume, slots,
|
||||
0x00800000 - 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -222,7 +262,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
|
|||
is_play ?
|
||||
"DVC Out Mute Switch" : "DVC In Mute Switch",
|
||||
rsnd_dvc_volume_update,
|
||||
&dvc->mute, 1);
|
||||
&dvc->mute, slots,
|
||||
1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -269,11 +310,10 @@ static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io,
|
|||
static struct rsnd_mod_ops rsnd_dvc_ops = {
|
||||
.name = DVC_NAME,
|
||||
.dma_req = rsnd_dvc_dma_req,
|
||||
.remove = rsnd_dvc_remove_gen2,
|
||||
.probe = rsnd_dvc_probe_,
|
||||
.remove = rsnd_dvc_remove_,
|
||||
.init = rsnd_dvc_init,
|
||||
.quit = rsnd_dvc_quit,
|
||||
.start = rsnd_dvc_start,
|
||||
.stop = rsnd_dvc_stop,
|
||||
.pcm_new = rsnd_dvc_pcm_new,
|
||||
};
|
||||
|
||||
|
|
@ -282,50 +322,13 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
|
|||
if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv)))
|
||||
id = 0;
|
||||
|
||||
return rsnd_mod_get((struct rsnd_dvc *)(priv->dvc) + id);
|
||||
return rsnd_mod_get(rsnd_dvc_get(priv, id));
|
||||
}
|
||||
|
||||
static void rsnd_of_parse_dvc(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv)
|
||||
int rsnd_dvc_probe(struct rsnd_priv *priv)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct rsnd_dvc_platform_info *dvc_info;
|
||||
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
|
||||
struct device *dev = &pdev->dev;
|
||||
int nr;
|
||||
|
||||
if (!of_data)
|
||||
return;
|
||||
|
||||
node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc");
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
nr = of_get_child_count(node);
|
||||
if (!nr)
|
||||
goto rsnd_of_parse_dvc_end;
|
||||
|
||||
dvc_info = devm_kzalloc(dev,
|
||||
sizeof(struct rsnd_dvc_platform_info) * nr,
|
||||
GFP_KERNEL);
|
||||
if (!dvc_info) {
|
||||
dev_err(dev, "dvc info allocation error\n");
|
||||
goto rsnd_of_parse_dvc_end;
|
||||
}
|
||||
|
||||
info->dvc_info = dvc_info;
|
||||
info->dvc_info_nr = nr;
|
||||
|
||||
rsnd_of_parse_dvc_end:
|
||||
of_node_put(node);
|
||||
}
|
||||
|
||||
int rsnd_dvc_probe(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
|
||||
struct device_node *np;
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct rsnd_dvc *dvc;
|
||||
struct clk *clk;
|
||||
|
|
@ -336,40 +339,54 @@ int rsnd_dvc_probe(struct platform_device *pdev,
|
|||
if (rsnd_is_gen1(priv))
|
||||
return 0;
|
||||
|
||||
rsnd_of_parse_dvc(pdev, of_data, priv);
|
||||
node = rsnd_dvc_of_node(priv);
|
||||
if (!node)
|
||||
return 0; /* not used is not error */
|
||||
|
||||
nr = info->dvc_info_nr;
|
||||
if (!nr)
|
||||
return 0;
|
||||
nr = of_get_child_count(node);
|
||||
if (!nr) {
|
||||
ret = -EINVAL;
|
||||
goto rsnd_dvc_probe_done;
|
||||
}
|
||||
|
||||
dvc = devm_kzalloc(dev, sizeof(*dvc) * nr, GFP_KERNEL);
|
||||
if (!dvc)
|
||||
return -ENOMEM;
|
||||
if (!dvc) {
|
||||
ret = -ENOMEM;
|
||||
goto rsnd_dvc_probe_done;
|
||||
}
|
||||
|
||||
priv->dvc_nr = nr;
|
||||
priv->dvc = dvc;
|
||||
|
||||
for_each_rsnd_dvc(dvc, priv, i) {
|
||||
i = 0;
|
||||
ret = 0;
|
||||
for_each_child_of_node(node, np) {
|
||||
dvc = rsnd_dvc_get(priv, i);
|
||||
|
||||
snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d",
|
||||
DVC_NAME, i);
|
||||
|
||||
clk = devm_clk_get(dev, name);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
dvc->info = &info->dvc_info[i];
|
||||
if (IS_ERR(clk)) {
|
||||
ret = PTR_ERR(clk);
|
||||
goto rsnd_dvc_probe_done;
|
||||
}
|
||||
|
||||
ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
|
||||
clk, RSND_MOD_DVC, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto rsnd_dvc_probe_done;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
rsnd_dvc_probe_done:
|
||||
of_node_put(node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rsnd_dvc_remove(struct platform_device *pdev,
|
||||
struct rsnd_priv *priv)
|
||||
void rsnd_dvc_remove(struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_dvc *dvc;
|
||||
int i;
|
||||
|
|
|
|||
|
|
@ -31,29 +31,33 @@ struct rsnd_gen {
|
|||
|
||||
/* RSND_REG_MAX base */
|
||||
struct regmap_field *regs[RSND_REG_MAX];
|
||||
const char *reg_name[RSND_REG_MAX];
|
||||
};
|
||||
|
||||
#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen)
|
||||
#define rsnd_reg_name(gen, id) ((gen)->reg_name[id])
|
||||
|
||||
struct rsnd_regmap_field_conf {
|
||||
int idx;
|
||||
unsigned int reg_offset;
|
||||
unsigned int id_offset;
|
||||
const char *reg_name;
|
||||
};
|
||||
|
||||
#define RSND_REG_SET(id, offset, _id_offset) \
|
||||
#define RSND_REG_SET(id, offset, _id_offset, n) \
|
||||
{ \
|
||||
.idx = id, \
|
||||
.reg_offset = offset, \
|
||||
.id_offset = _id_offset, \
|
||||
.reg_name = n, \
|
||||
}
|
||||
/* single address mapping */
|
||||
#define RSND_GEN_S_REG(id, offset) \
|
||||
RSND_REG_SET(RSND_REG_##id, offset, 0)
|
||||
RSND_REG_SET(RSND_REG_##id, offset, 0, #id)
|
||||
|
||||
/* multi address mapping */
|
||||
#define RSND_GEN_M_REG(id, offset, _id_offset) \
|
||||
RSND_REG_SET(RSND_REG_##id, offset, _id_offset)
|
||||
RSND_REG_SET(RSND_REG_##id, offset, _id_offset, #id)
|
||||
|
||||
/*
|
||||
* basic function
|
||||
|
|
@ -83,8 +87,9 @@ u32 rsnd_read(struct rsnd_priv *priv,
|
|||
|
||||
regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
|
||||
|
||||
dev_dbg(dev, "r %s[%d] - %4d : %08x\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val);
|
||||
dev_dbg(dev, "r %s[%d] - %-18s (%4d) : %08x\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod),
|
||||
rsnd_reg_name(gen, reg), reg, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
|
@ -99,10 +104,11 @@ void rsnd_write(struct rsnd_priv *priv,
|
|||
if (!rsnd_is_accessible_reg(priv, gen, reg))
|
||||
return;
|
||||
|
||||
dev_dbg(dev, "w %s[%d] - %4d : %08x\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data);
|
||||
|
||||
regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
|
||||
|
||||
dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod),
|
||||
rsnd_reg_name(gen, reg), reg, data);
|
||||
}
|
||||
|
||||
void rsnd_force_write(struct rsnd_priv *priv,
|
||||
|
|
@ -115,10 +121,11 @@ void rsnd_force_write(struct rsnd_priv *priv,
|
|||
if (!rsnd_is_accessible_reg(priv, gen, reg))
|
||||
return;
|
||||
|
||||
dev_dbg(dev, "w %s[%d] - %4d : %08x\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data);
|
||||
|
||||
regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data);
|
||||
|
||||
dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod),
|
||||
rsnd_reg_name(gen, reg), reg, data);
|
||||
}
|
||||
|
||||
void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
|
||||
|
|
@ -130,11 +137,13 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
|
|||
if (!rsnd_is_accessible_reg(priv, gen, reg))
|
||||
return;
|
||||
|
||||
dev_dbg(dev, "b %s[%d] - %4d : %08x/%08x\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data, mask);
|
||||
|
||||
regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod),
|
||||
mask, data);
|
||||
|
||||
dev_dbg(dev, "b %s[%d] - %-18s (%4d) : %08x/%08x\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod),
|
||||
rsnd_reg_name(gen, reg), reg, data, mask);
|
||||
|
||||
}
|
||||
|
||||
phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id)
|
||||
|
|
@ -150,7 +159,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
|
|||
int id_size,
|
||||
int reg_id,
|
||||
const char *name,
|
||||
struct rsnd_regmap_field_conf *conf,
|
||||
const struct rsnd_regmap_field_conf *conf,
|
||||
int conf_size)
|
||||
{
|
||||
struct platform_device *pdev = rsnd_priv_to_pdev(priv);
|
||||
|
|
@ -203,6 +212,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
|
|||
|
||||
/* RSND_REG_MAX base */
|
||||
gen->regs[conf[i].idx] = regs;
|
||||
gen->reg_name[conf[i].idx] = conf[i].reg_name;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -211,25 +221,31 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
|
|||
/*
|
||||
* Gen2
|
||||
*/
|
||||
static int rsnd_gen2_probe(struct platform_device *pdev,
|
||||
struct rsnd_priv *priv)
|
||||
static int rsnd_gen2_probe(struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_regmap_field_conf conf_ssiu[] = {
|
||||
const static struct rsnd_regmap_field_conf conf_ssiu[] = {
|
||||
RSND_GEN_S_REG(SSI_MODE0, 0x800),
|
||||
RSND_GEN_S_REG(SSI_MODE1, 0x804),
|
||||
RSND_GEN_S_REG(SSI_MODE2, 0x808),
|
||||
RSND_GEN_S_REG(SSI_CONTROL, 0x810),
|
||||
|
||||
/* FIXME: it needs SSI_MODE2/3 in the future */
|
||||
RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80),
|
||||
RSND_GEN_M_REG(SSI_BUSIF_ADINR, 0x4, 0x80),
|
||||
RSND_GEN_M_REG(SSI_BUSIF_DALIGN,0x8, 0x80),
|
||||
RSND_GEN_M_REG(SSI_MODE, 0xc, 0x80),
|
||||
RSND_GEN_M_REG(SSI_CTRL, 0x10, 0x80),
|
||||
RSND_GEN_M_REG(SSI_INT_ENABLE, 0x18, 0x80),
|
||||
};
|
||||
struct rsnd_regmap_field_conf conf_scu[] = {
|
||||
RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x0, 0x20),
|
||||
|
||||
const static struct rsnd_regmap_field_conf conf_scu[] = {
|
||||
RSND_GEN_M_REG(SRC_I_BUSIF_MODE,0x0, 0x20),
|
||||
RSND_GEN_M_REG(SRC_O_BUSIF_MODE,0x4, 0x20),
|
||||
RSND_GEN_M_REG(SRC_BUSIF_DALIGN,0x8, 0x20),
|
||||
RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20),
|
||||
RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20),
|
||||
RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20),
|
||||
RSND_GEN_M_REG(CMD_BUSIF_DALIGN,0x188, 0x20),
|
||||
RSND_GEN_M_REG(CMD_ROUTE_SLCT, 0x18c, 0x20),
|
||||
RSND_GEN_M_REG(CMD_CTRL, 0x190, 0x20),
|
||||
RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8),
|
||||
|
|
@ -266,9 +282,15 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
|
|||
RSND_GEN_M_REG(DVC_VRDBR, 0xe20, 0x100),
|
||||
RSND_GEN_M_REG(DVC_VOL0R, 0xe28, 0x100),
|
||||
RSND_GEN_M_REG(DVC_VOL1R, 0xe2c, 0x100),
|
||||
RSND_GEN_M_REG(DVC_VOL2R, 0xe30, 0x100),
|
||||
RSND_GEN_M_REG(DVC_VOL3R, 0xe34, 0x100),
|
||||
RSND_GEN_M_REG(DVC_VOL4R, 0xe38, 0x100),
|
||||
RSND_GEN_M_REG(DVC_VOL5R, 0xe3c, 0x100),
|
||||
RSND_GEN_M_REG(DVC_VOL6R, 0xe40, 0x100),
|
||||
RSND_GEN_M_REG(DVC_VOL7R, 0xe44, 0x100),
|
||||
RSND_GEN_M_REG(DVC_DVUER, 0xe48, 0x100),
|
||||
};
|
||||
struct rsnd_regmap_field_conf conf_adg[] = {
|
||||
const static struct rsnd_regmap_field_conf conf_adg[] = {
|
||||
RSND_GEN_S_REG(BRRA, 0x00),
|
||||
RSND_GEN_S_REG(BRRB, 0x04),
|
||||
RSND_GEN_S_REG(SSICKR, 0x08),
|
||||
|
|
@ -288,7 +310,7 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
|
|||
RSND_GEN_S_REG(SRCOUT_TIMSEL4, 0x58),
|
||||
RSND_GEN_S_REG(CMDOUT_TIMSEL, 0x5c),
|
||||
};
|
||||
struct rsnd_regmap_field_conf conf_ssi[] = {
|
||||
const static struct rsnd_regmap_field_conf conf_ssi[] = {
|
||||
RSND_GEN_M_REG(SSICR, 0x00, 0x40),
|
||||
RSND_GEN_M_REG(SSISR, 0x04, 0x40),
|
||||
RSND_GEN_M_REG(SSITDR, 0x08, 0x40),
|
||||
|
|
@ -317,65 +339,30 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
|
|||
* Gen1
|
||||
*/
|
||||
|
||||
static int rsnd_gen1_probe(struct platform_device *pdev,
|
||||
struct rsnd_priv *priv)
|
||||
static int rsnd_gen1_probe(struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_regmap_field_conf conf_sru[] = {
|
||||
RSND_GEN_S_REG(SRC_ROUTE_SEL, 0x00),
|
||||
RSND_GEN_S_REG(SRC_TMG_SEL0, 0x08),
|
||||
RSND_GEN_S_REG(SRC_TMG_SEL1, 0x0c),
|
||||
RSND_GEN_S_REG(SRC_TMG_SEL2, 0x10),
|
||||
RSND_GEN_S_REG(SRC_ROUTE_CTRL, 0xc0),
|
||||
RSND_GEN_S_REG(SSI_MODE0, 0xD0),
|
||||
RSND_GEN_S_REG(SSI_MODE1, 0xD4),
|
||||
RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x20, 0x4),
|
||||
RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0x50, 0x8),
|
||||
RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40),
|
||||
RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40),
|
||||
RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40),
|
||||
RSND_GEN_M_REG(SRC_IFSCR, 0x21c, 0x40),
|
||||
RSND_GEN_M_REG(SRC_IFSVR, 0x220, 0x40),
|
||||
RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40),
|
||||
RSND_GEN_M_REG(SRC_MNFSR, 0x228, 0x40),
|
||||
/*
|
||||
* ADD US
|
||||
*
|
||||
* SRC_STATUS
|
||||
* SRC_INT_EN
|
||||
* SCU_SYS_STATUS0
|
||||
* SCU_SYS_STATUS1
|
||||
* SCU_SYS_INT_EN0
|
||||
* SCU_SYS_INT_EN1
|
||||
*/
|
||||
};
|
||||
struct rsnd_regmap_field_conf conf_adg[] = {
|
||||
const static struct rsnd_regmap_field_conf conf_adg[] = {
|
||||
RSND_GEN_S_REG(BRRA, 0x00),
|
||||
RSND_GEN_S_REG(BRRB, 0x04),
|
||||
RSND_GEN_S_REG(SSICKR, 0x08),
|
||||
RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c),
|
||||
RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10),
|
||||
RSND_GEN_S_REG(AUDIO_CLK_SEL3, 0x18),
|
||||
RSND_GEN_S_REG(AUDIO_CLK_SEL4, 0x1c),
|
||||
RSND_GEN_S_REG(AUDIO_CLK_SEL5, 0x20),
|
||||
};
|
||||
struct rsnd_regmap_field_conf conf_ssi[] = {
|
||||
const static struct rsnd_regmap_field_conf conf_ssi[] = {
|
||||
RSND_GEN_M_REG(SSICR, 0x00, 0x40),
|
||||
RSND_GEN_M_REG(SSISR, 0x04, 0x40),
|
||||
RSND_GEN_M_REG(SSITDR, 0x08, 0x40),
|
||||
RSND_GEN_M_REG(SSIRDR, 0x0c, 0x40),
|
||||
RSND_GEN_M_REG(SSIWSR, 0x20, 0x40),
|
||||
};
|
||||
int ret_sru;
|
||||
int ret_adg;
|
||||
int ret_ssi;
|
||||
|
||||
ret_sru = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SRU, "sru", conf_sru);
|
||||
ret_adg = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, "adg", conf_adg);
|
||||
ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, "ssi", conf_ssi);
|
||||
if (ret_sru < 0 ||
|
||||
ret_adg < 0 ||
|
||||
if (ret_adg < 0 ||
|
||||
ret_ssi < 0)
|
||||
return ret_sru | ret_adg | ret_ssi;
|
||||
return ret_adg | ret_ssi;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -383,28 +370,12 @@ static int rsnd_gen1_probe(struct platform_device *pdev,
|
|||
/*
|
||||
* Gen
|
||||
*/
|
||||
static void rsnd_of_parse_gen(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rcar_snd_info *info = priv->info;
|
||||
|
||||
if (!of_data)
|
||||
return;
|
||||
|
||||
info->flags = of_data->flags;
|
||||
}
|
||||
|
||||
int rsnd_gen_probe(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv)
|
||||
int rsnd_gen_probe(struct rsnd_priv *priv)
|
||||
{
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct rsnd_gen *gen;
|
||||
int ret;
|
||||
|
||||
rsnd_of_parse_gen(pdev, of_data, priv);
|
||||
|
||||
gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL);
|
||||
if (!gen) {
|
||||
dev_err(dev, "GEN allocate failed\n");
|
||||
|
|
@ -415,9 +386,9 @@ int rsnd_gen_probe(struct platform_device *pdev,
|
|||
|
||||
ret = -ENODEV;
|
||||
if (rsnd_is_gen1(priv))
|
||||
ret = rsnd_gen1_probe(pdev, priv);
|
||||
ret = rsnd_gen1_probe(priv);
|
||||
else if (rsnd_is_gen2(priv))
|
||||
ret = rsnd_gen2_probe(pdev, priv);
|
||||
ret = rsnd_gen2_probe(priv);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(dev, "unknown generation R-Car sound device\n");
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@
|
|||
#define MIX_NAME "mix"
|
||||
|
||||
struct rsnd_mix {
|
||||
struct rsnd_mix_platform_info *info; /* rcar_snd.h */
|
||||
struct rsnd_mod mod;
|
||||
};
|
||||
|
||||
#define rsnd_mix_get(priv, id) ((struct rsnd_mix *)(priv->mix) + id)
|
||||
#define rsnd_mix_nr(priv) ((priv)->mix_nr)
|
||||
#define for_each_rsnd_mix(pos, priv, i) \
|
||||
for ((i) = 0; \
|
||||
|
|
@ -24,58 +24,77 @@ struct rsnd_mix {
|
|||
((pos) = (struct rsnd_mix *)(priv)->mix + i); \
|
||||
i++)
|
||||
|
||||
|
||||
static void rsnd_mix_soft_reset(struct rsnd_mod *mod)
|
||||
static void rsnd_mix_activation(struct rsnd_mod *mod)
|
||||
{
|
||||
rsnd_mod_write(mod, MIX_SWRSR, 0);
|
||||
rsnd_mod_write(mod, MIX_SWRSR, 1);
|
||||
}
|
||||
|
||||
#define rsnd_mix_initialize_lock(mod) __rsnd_mix_initialize_lock(mod, 1)
|
||||
#define rsnd_mix_initialize_unlock(mod) __rsnd_mix_initialize_lock(mod, 0)
|
||||
static void __rsnd_mix_initialize_lock(struct rsnd_mod *mod, u32 enable)
|
||||
static void rsnd_mix_halt(struct rsnd_mod *mod)
|
||||
{
|
||||
rsnd_mod_write(mod, MIX_MIXIR, enable);
|
||||
rsnd_mod_write(mod, MIX_MIXIR, 1);
|
||||
rsnd_mod_write(mod, MIX_SWRSR, 0);
|
||||
}
|
||||
|
||||
static void rsnd_mix_volume_parameter(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod)
|
||||
{
|
||||
rsnd_mod_write(mod, MIX_MDBAR, 0);
|
||||
rsnd_mod_write(mod, MIX_MDBBR, 0);
|
||||
rsnd_mod_write(mod, MIX_MDBCR, 0);
|
||||
rsnd_mod_write(mod, MIX_MDBDR, 0);
|
||||
}
|
||||
|
||||
static void rsnd_mix_volume_init(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod)
|
||||
{
|
||||
rsnd_mod_write(mod, MIX_MIXIR, 1);
|
||||
|
||||
/* General Information */
|
||||
rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
|
||||
|
||||
/* volume step */
|
||||
rsnd_mod_write(mod, MIX_MIXMR, 0);
|
||||
rsnd_mod_write(mod, MIX_MVPDR, 0);
|
||||
|
||||
/* common volume parameter */
|
||||
rsnd_mix_volume_parameter(io, mod);
|
||||
|
||||
rsnd_mod_write(mod, MIX_MIXIR, 0);
|
||||
}
|
||||
|
||||
static void rsnd_mix_volume_update(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod)
|
||||
{
|
||||
|
||||
/* Disable MIX dB setting */
|
||||
rsnd_mod_write(mod, MIX_MDBER, 0);
|
||||
|
||||
rsnd_mod_write(mod, MIX_MDBAR, 0);
|
||||
rsnd_mod_write(mod, MIX_MDBBR, 0);
|
||||
rsnd_mod_write(mod, MIX_MDBCR, 0);
|
||||
rsnd_mod_write(mod, MIX_MDBDR, 0);
|
||||
/* common volume parameter */
|
||||
rsnd_mix_volume_parameter(io, mod);
|
||||
|
||||
/* Enable MIX dB setting */
|
||||
rsnd_mod_write(mod, MIX_MDBER, 1);
|
||||
}
|
||||
|
||||
static int rsnd_mix_probe_(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
return rsnd_cmd_attach(io, rsnd_mod_id(mod));
|
||||
}
|
||||
|
||||
static int rsnd_mix_init(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
rsnd_mod_power_on(mod);
|
||||
|
||||
rsnd_mix_soft_reset(mod);
|
||||
rsnd_mix_activation(mod);
|
||||
|
||||
rsnd_mix_initialize_lock(mod);
|
||||
|
||||
rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
|
||||
|
||||
rsnd_path_parse(priv, io);
|
||||
|
||||
/* volume step */
|
||||
rsnd_mod_write(mod, MIX_MIXMR, 0);
|
||||
rsnd_mod_write(mod, MIX_MVPDR, 0);
|
||||
rsnd_mix_volume_init(io, mod);
|
||||
|
||||
rsnd_mix_volume_update(io, mod);
|
||||
|
||||
rsnd_mix_initialize_unlock(mod);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -83,6 +102,8 @@ static int rsnd_mix_quit(struct rsnd_mod *mod,
|
|||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
rsnd_mix_halt(mod);
|
||||
|
||||
rsnd_mod_power_off(mod);
|
||||
|
||||
return 0;
|
||||
|
|
@ -90,6 +111,7 @@ static int rsnd_mix_quit(struct rsnd_mod *mod,
|
|||
|
||||
static struct rsnd_mod_ops rsnd_mix_ops = {
|
||||
.name = MIX_NAME,
|
||||
.probe = rsnd_mix_probe_,
|
||||
.init = rsnd_mix_init,
|
||||
.quit = rsnd_mix_quit,
|
||||
};
|
||||
|
|
@ -99,51 +121,13 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id)
|
|||
if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv)))
|
||||
id = 0;
|
||||
|
||||
return rsnd_mod_get((struct rsnd_mix *)(priv->mix) + id);
|
||||
return rsnd_mod_get(rsnd_mix_get(priv, id));
|
||||
}
|
||||
|
||||
static void rsnd_of_parse_mix(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv)
|
||||
int rsnd_mix_probe(struct rsnd_priv *priv)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct rsnd_mix_platform_info *mix_info;
|
||||
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
|
||||
struct device *dev = &pdev->dev;
|
||||
int nr;
|
||||
|
||||
if (!of_data)
|
||||
return;
|
||||
|
||||
node = of_get_child_by_name(dev->of_node, "rcar_sound,mix");
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
nr = of_get_child_count(node);
|
||||
if (!nr)
|
||||
goto rsnd_of_parse_mix_end;
|
||||
|
||||
mix_info = devm_kzalloc(dev,
|
||||
sizeof(struct rsnd_mix_platform_info) * nr,
|
||||
GFP_KERNEL);
|
||||
if (!mix_info) {
|
||||
dev_err(dev, "mix info allocation error\n");
|
||||
goto rsnd_of_parse_mix_end;
|
||||
}
|
||||
|
||||
info->mix_info = mix_info;
|
||||
info->mix_info_nr = nr;
|
||||
|
||||
rsnd_of_parse_mix_end:
|
||||
of_node_put(node);
|
||||
|
||||
}
|
||||
|
||||
int rsnd_mix_probe(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
|
||||
struct device_node *np;
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct rsnd_mix *mix;
|
||||
struct clk *clk;
|
||||
|
|
@ -154,40 +138,54 @@ int rsnd_mix_probe(struct platform_device *pdev,
|
|||
if (rsnd_is_gen1(priv))
|
||||
return 0;
|
||||
|
||||
rsnd_of_parse_mix(pdev, of_data, priv);
|
||||
node = rsnd_mix_of_node(priv);
|
||||
if (!node)
|
||||
return 0; /* not used is not error */
|
||||
|
||||
nr = info->mix_info_nr;
|
||||
if (!nr)
|
||||
return 0;
|
||||
nr = of_get_child_count(node);
|
||||
if (!nr) {
|
||||
ret = -EINVAL;
|
||||
goto rsnd_mix_probe_done;
|
||||
}
|
||||
|
||||
mix = devm_kzalloc(dev, sizeof(*mix) * nr, GFP_KERNEL);
|
||||
if (!mix)
|
||||
return -ENOMEM;
|
||||
if (!mix) {
|
||||
ret = -ENOMEM;
|
||||
goto rsnd_mix_probe_done;
|
||||
}
|
||||
|
||||
priv->mix_nr = nr;
|
||||
priv->mix = mix;
|
||||
|
||||
for_each_rsnd_mix(mix, priv, i) {
|
||||
i = 0;
|
||||
ret = 0;
|
||||
for_each_child_of_node(node, np) {
|
||||
mix = rsnd_mix_get(priv, i);
|
||||
|
||||
snprintf(name, MIX_NAME_SIZE, "%s.%d",
|
||||
MIX_NAME, i);
|
||||
|
||||
clk = devm_clk_get(dev, name);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
mix->info = &info->mix_info[i];
|
||||
if (IS_ERR(clk)) {
|
||||
ret = PTR_ERR(clk);
|
||||
goto rsnd_mix_probe_done;
|
||||
}
|
||||
|
||||
ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
|
||||
clk, RSND_MOD_MIX, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto rsnd_mix_probe_done;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
rsnd_mix_probe_done:
|
||||
of_node_put(node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rsnd_mix_remove(struct platform_device *pdev,
|
||||
struct rsnd_priv *priv)
|
||||
void rsnd_mix_remove(struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_mix *mix;
|
||||
int i;
|
||||
|
|
|
|||
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
* Renesas R-Car SRU/SCU/SSIU/SSI support
|
||||
*
|
||||
* Copyright (C) 2013 Renesas Solutions Corp.
|
||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef RCAR_SND_H
|
||||
#define RCAR_SND_H
|
||||
|
||||
|
||||
#define RSND_GEN1_SRU 0
|
||||
#define RSND_GEN1_ADG 1
|
||||
#define RSND_GEN1_SSI 2
|
||||
|
||||
#define RSND_GEN2_SCU 0
|
||||
#define RSND_GEN2_ADG 1
|
||||
#define RSND_GEN2_SSIU 2
|
||||
#define RSND_GEN2_SSI 3
|
||||
|
||||
#define RSND_BASE_MAX 4
|
||||
|
||||
/*
|
||||
* flags
|
||||
*
|
||||
* 0xAB000000
|
||||
*
|
||||
* A : clock sharing settings
|
||||
* B : SSI direction
|
||||
*/
|
||||
#define RSND_SSI_CLK_PIN_SHARE (1 << 31)
|
||||
#define RSND_SSI_NO_BUSIF (1 << 30) /* SSI+DMA without BUSIF */
|
||||
|
||||
#define RSND_SSI(_dma_id, _irq, _flags) \
|
||||
{ .dma_id = _dma_id, .irq = _irq, .flags = _flags }
|
||||
#define RSND_SSI_UNUSED \
|
||||
{ .dma_id = -1, .irq = -1, .flags = 0 }
|
||||
|
||||
struct rsnd_ssi_platform_info {
|
||||
int dma_id;
|
||||
int irq;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
#define RSND_SRC(rate, _dma_id) \
|
||||
{ .convert_rate = rate, .dma_id = _dma_id, }
|
||||
#define RSND_SRC_UNUSED \
|
||||
{ .convert_rate = 0, .dma_id = -1, }
|
||||
|
||||
struct rsnd_src_platform_info {
|
||||
u32 convert_rate; /* sampling rate convert */
|
||||
int dma_id; /* for Gen2 SCU */
|
||||
int irq;
|
||||
};
|
||||
|
||||
/*
|
||||
* flags
|
||||
*/
|
||||
struct rsnd_ctu_platform_info {
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
struct rsnd_mix_platform_info {
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
struct rsnd_dvc_platform_info {
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
struct rsnd_dai_path_info {
|
||||
struct rsnd_ssi_platform_info *ssi;
|
||||
struct rsnd_src_platform_info *src;
|
||||
struct rsnd_ctu_platform_info *ctu;
|
||||
struct rsnd_mix_platform_info *mix;
|
||||
struct rsnd_dvc_platform_info *dvc;
|
||||
};
|
||||
|
||||
struct rsnd_dai_platform_info {
|
||||
struct rsnd_dai_path_info playback;
|
||||
struct rsnd_dai_path_info capture;
|
||||
};
|
||||
|
||||
/*
|
||||
* flags
|
||||
*
|
||||
* 0x0000000A
|
||||
*
|
||||
* A : generation
|
||||
*/
|
||||
#define RSND_GEN_MASK (0xF << 0)
|
||||
#define RSND_GEN1 (1 << 0) /* fixme */
|
||||
#define RSND_GEN2 (2 << 0) /* fixme */
|
||||
|
||||
struct rcar_snd_info {
|
||||
u32 flags;
|
||||
struct rsnd_ssi_platform_info *ssi_info;
|
||||
int ssi_info_nr;
|
||||
struct rsnd_src_platform_info *src_info;
|
||||
int src_info_nr;
|
||||
struct rsnd_ctu_platform_info *ctu_info;
|
||||
int ctu_info_nr;
|
||||
struct rsnd_mix_platform_info *mix_info;
|
||||
int mix_info_nr;
|
||||
struct rsnd_dvc_platform_info *dvc_info;
|
||||
int dvc_info_nr;
|
||||
struct rsnd_dai_platform_info *dai_info;
|
||||
int dai_info_nr;
|
||||
int (*start)(int id);
|
||||
int (*stop)(int id);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -24,7 +24,16 @@
|
|||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "rcar_snd.h"
|
||||
#define RSND_GEN1_SRU 0
|
||||
#define RSND_GEN1_ADG 1
|
||||
#define RSND_GEN1_SSI 2
|
||||
|
||||
#define RSND_GEN2_SCU 0
|
||||
#define RSND_GEN2_ADG 1
|
||||
#define RSND_GEN2_SSIU 2
|
||||
#define RSND_GEN2_SSI 3
|
||||
|
||||
#define RSND_BASE_MAX 4
|
||||
|
||||
/*
|
||||
* pseudo register
|
||||
|
|
@ -34,10 +43,19 @@
|
|||
* see gen1/gen2 for detail
|
||||
*/
|
||||
enum rsnd_reg {
|
||||
/* SRU/SCU/SSIU */
|
||||
/* SCU (SRC/SSIU/MIX/CTU/DVC) */
|
||||
RSND_REG_SSI_MODE, /* Gen2 only */
|
||||
RSND_REG_SSI_MODE0,
|
||||
RSND_REG_SSI_MODE1,
|
||||
RSND_REG_SRC_BUSIF_MODE,
|
||||
RSND_REG_SSI_MODE2,
|
||||
RSND_REG_SSI_CONTROL,
|
||||
RSND_REG_SSI_CTRL, /* Gen2 only */
|
||||
RSND_REG_SSI_BUSIF_MODE, /* Gen2 only */
|
||||
RSND_REG_SSI_BUSIF_ADINR, /* Gen2 only */
|
||||
RSND_REG_SSI_BUSIF_DALIGN, /* Gen2 only */
|
||||
RSND_REG_SSI_INT_ENABLE, /* Gen2 only */
|
||||
RSND_REG_SRC_I_BUSIF_MODE,
|
||||
RSND_REG_SRC_O_BUSIF_MODE,
|
||||
RSND_REG_SRC_ROUTE_MODE0,
|
||||
RSND_REG_SRC_SWRSR,
|
||||
RSND_REG_SRC_SRCIR,
|
||||
|
|
@ -45,9 +63,29 @@ enum rsnd_reg {
|
|||
RSND_REG_SRC_IFSCR,
|
||||
RSND_REG_SRC_IFSVR,
|
||||
RSND_REG_SRC_SRCCR,
|
||||
RSND_REG_SRC_CTRL, /* Gen2 only */
|
||||
RSND_REG_SRC_BSDSR, /* Gen2 only */
|
||||
RSND_REG_SRC_BSISR, /* Gen2 only */
|
||||
RSND_REG_SRC_INT_ENABLE0, /* Gen2 only */
|
||||
RSND_REG_SRC_BUSIF_DALIGN, /* Gen2 only */
|
||||
RSND_REG_SRCIN_TIMSEL0, /* Gen2 only */
|
||||
RSND_REG_SRCIN_TIMSEL1, /* Gen2 only */
|
||||
RSND_REG_SRCIN_TIMSEL2, /* Gen2 only */
|
||||
RSND_REG_SRCIN_TIMSEL3, /* Gen2 only */
|
||||
RSND_REG_SRCIN_TIMSEL4, /* Gen2 only */
|
||||
RSND_REG_SRCOUT_TIMSEL0, /* Gen2 only */
|
||||
RSND_REG_SRCOUT_TIMSEL1, /* Gen2 only */
|
||||
RSND_REG_SRCOUT_TIMSEL2, /* Gen2 only */
|
||||
RSND_REG_SRCOUT_TIMSEL3, /* Gen2 only */
|
||||
RSND_REG_SRCOUT_TIMSEL4, /* Gen2 only */
|
||||
RSND_REG_SCU_SYS_STATUS0,
|
||||
RSND_REG_SCU_SYS_STATUS1, /* Gen2 only */
|
||||
RSND_REG_SCU_SYS_INT_EN0,
|
||||
RSND_REG_SCU_SYS_INT_EN1, /* Gen2 only */
|
||||
RSND_REG_CMD_CTRL, /* Gen2 only */
|
||||
RSND_REG_CMD_BUSIF_DALIGN, /* Gen2 only */
|
||||
RSND_REG_CMD_ROUTE_SLCT,
|
||||
RSND_REG_CMDOUT_TIMSEL, /* Gen2 only */
|
||||
RSND_REG_CTU_CTUIR,
|
||||
RSND_REG_CTU_ADINR,
|
||||
RSND_REG_MIX_SWRSR,
|
||||
|
|
@ -67,14 +105,25 @@ enum rsnd_reg {
|
|||
RSND_REG_DVC_ZCMCR,
|
||||
RSND_REG_DVC_VOL0R,
|
||||
RSND_REG_DVC_VOL1R,
|
||||
RSND_REG_DVC_VOL2R,
|
||||
RSND_REG_DVC_VOL3R,
|
||||
RSND_REG_DVC_VOL4R,
|
||||
RSND_REG_DVC_VOL5R,
|
||||
RSND_REG_DVC_VOL6R,
|
||||
RSND_REG_DVC_VOL7R,
|
||||
RSND_REG_DVC_DVUER,
|
||||
RSND_REG_DVC_VRCTR, /* Gen2 only */
|
||||
RSND_REG_DVC_VRPDR, /* Gen2 only */
|
||||
RSND_REG_DVC_VRDBR, /* Gen2 only */
|
||||
|
||||
/* ADG */
|
||||
RSND_REG_BRRA,
|
||||
RSND_REG_BRRB,
|
||||
RSND_REG_SSICKR,
|
||||
RSND_REG_DIV_EN, /* Gen2 only */
|
||||
RSND_REG_AUDIO_CLK_SEL0,
|
||||
RSND_REG_AUDIO_CLK_SEL1,
|
||||
RSND_REG_AUDIO_CLK_SEL2, /* Gen2 only */
|
||||
|
||||
/* SSI */
|
||||
RSND_REG_SSICR,
|
||||
|
|
@ -83,83 +132,9 @@ enum rsnd_reg {
|
|||
RSND_REG_SSIRDR,
|
||||
RSND_REG_SSIWSR,
|
||||
|
||||
/* SHARE see below */
|
||||
RSND_REG_SHARE01,
|
||||
RSND_REG_SHARE02,
|
||||
RSND_REG_SHARE03,
|
||||
RSND_REG_SHARE04,
|
||||
RSND_REG_SHARE05,
|
||||
RSND_REG_SHARE06,
|
||||
RSND_REG_SHARE07,
|
||||
RSND_REG_SHARE08,
|
||||
RSND_REG_SHARE09,
|
||||
RSND_REG_SHARE10,
|
||||
RSND_REG_SHARE11,
|
||||
RSND_REG_SHARE12,
|
||||
RSND_REG_SHARE13,
|
||||
RSND_REG_SHARE14,
|
||||
RSND_REG_SHARE15,
|
||||
RSND_REG_SHARE16,
|
||||
RSND_REG_SHARE17,
|
||||
RSND_REG_SHARE18,
|
||||
RSND_REG_SHARE19,
|
||||
RSND_REG_SHARE20,
|
||||
RSND_REG_SHARE21,
|
||||
RSND_REG_SHARE22,
|
||||
RSND_REG_SHARE23,
|
||||
RSND_REG_SHARE24,
|
||||
RSND_REG_SHARE25,
|
||||
RSND_REG_SHARE26,
|
||||
RSND_REG_SHARE27,
|
||||
RSND_REG_SHARE28,
|
||||
RSND_REG_SHARE29,
|
||||
|
||||
RSND_REG_MAX,
|
||||
};
|
||||
|
||||
/* Gen1 only */
|
||||
#define RSND_REG_SRC_ROUTE_SEL RSND_REG_SHARE01
|
||||
#define RSND_REG_SRC_TMG_SEL0 RSND_REG_SHARE02
|
||||
#define RSND_REG_SRC_TMG_SEL1 RSND_REG_SHARE03
|
||||
#define RSND_REG_SRC_TMG_SEL2 RSND_REG_SHARE04
|
||||
#define RSND_REG_SRC_ROUTE_CTRL RSND_REG_SHARE05
|
||||
#define RSND_REG_SRC_MNFSR RSND_REG_SHARE06
|
||||
#define RSND_REG_AUDIO_CLK_SEL3 RSND_REG_SHARE07
|
||||
#define RSND_REG_AUDIO_CLK_SEL4 RSND_REG_SHARE08
|
||||
#define RSND_REG_AUDIO_CLK_SEL5 RSND_REG_SHARE09
|
||||
|
||||
/* Gen2 only */
|
||||
#define RSND_REG_SRC_CTRL RSND_REG_SHARE01
|
||||
#define RSND_REG_SSI_CTRL RSND_REG_SHARE02
|
||||
#define RSND_REG_SSI_BUSIF_MODE RSND_REG_SHARE03
|
||||
#define RSND_REG_SSI_BUSIF_ADINR RSND_REG_SHARE04
|
||||
#define RSND_REG_SSI_INT_ENABLE RSND_REG_SHARE05
|
||||
#define RSND_REG_SRC_BSDSR RSND_REG_SHARE06
|
||||
#define RSND_REG_SRC_BSISR RSND_REG_SHARE07
|
||||
#define RSND_REG_DIV_EN RSND_REG_SHARE08
|
||||
#define RSND_REG_SRCIN_TIMSEL0 RSND_REG_SHARE09
|
||||
#define RSND_REG_SRCIN_TIMSEL1 RSND_REG_SHARE10
|
||||
#define RSND_REG_SRCIN_TIMSEL2 RSND_REG_SHARE11
|
||||
#define RSND_REG_SRCIN_TIMSEL3 RSND_REG_SHARE12
|
||||
#define RSND_REG_SRCIN_TIMSEL4 RSND_REG_SHARE13
|
||||
#define RSND_REG_SRCOUT_TIMSEL0 RSND_REG_SHARE14
|
||||
#define RSND_REG_SRCOUT_TIMSEL1 RSND_REG_SHARE15
|
||||
#define RSND_REG_SRCOUT_TIMSEL2 RSND_REG_SHARE16
|
||||
#define RSND_REG_SRCOUT_TIMSEL3 RSND_REG_SHARE17
|
||||
#define RSND_REG_SRCOUT_TIMSEL4 RSND_REG_SHARE18
|
||||
#define RSND_REG_AUDIO_CLK_SEL2 RSND_REG_SHARE19
|
||||
#define RSND_REG_CMD_CTRL RSND_REG_SHARE20
|
||||
#define RSND_REG_CMDOUT_TIMSEL RSND_REG_SHARE21
|
||||
#define RSND_REG_SSI_BUSIF_DALIGN RSND_REG_SHARE22
|
||||
#define RSND_REG_DVC_VRCTR RSND_REG_SHARE23
|
||||
#define RSND_REG_DVC_VRPDR RSND_REG_SHARE24
|
||||
#define RSND_REG_DVC_VRDBR RSND_REG_SHARE25
|
||||
#define RSND_REG_SCU_SYS_STATUS1 RSND_REG_SHARE26
|
||||
#define RSND_REG_SCU_SYS_INT_EN1 RSND_REG_SHARE27
|
||||
#define RSND_REG_SRC_INT_ENABLE0 RSND_REG_SHARE28
|
||||
#define RSND_REG_SRC_BUSIF_DALIGN RSND_REG_SHARE29
|
||||
|
||||
struct rsnd_of_data;
|
||||
struct rsnd_priv;
|
||||
struct rsnd_mod;
|
||||
struct rsnd_dai;
|
||||
|
|
@ -187,43 +162,13 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
|
|||
u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
|
||||
u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
|
||||
u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
|
||||
void rsnd_path_parse(struct rsnd_priv *priv,
|
||||
struct rsnd_dai_stream *io);
|
||||
|
||||
/*
|
||||
* R-Car DMA
|
||||
*/
|
||||
struct rsnd_dma;
|
||||
|
||||
struct rsnd_dmaen {
|
||||
struct dma_chan *chan;
|
||||
};
|
||||
|
||||
struct rsnd_dmapp {
|
||||
int dmapp_id;
|
||||
u32 chcr;
|
||||
};
|
||||
|
||||
struct rsnd_dma {
|
||||
struct rsnd_dma_ops *ops;
|
||||
dma_addr_t src_addr;
|
||||
dma_addr_t dst_addr;
|
||||
union {
|
||||
struct rsnd_dmaen en;
|
||||
struct rsnd_dmapp pp;
|
||||
} dma;
|
||||
};
|
||||
#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en)
|
||||
#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp)
|
||||
#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
|
||||
|
||||
void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
|
||||
void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
|
||||
int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id);
|
||||
void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
|
||||
int rsnd_dma_probe(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv);
|
||||
struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod, int id);
|
||||
int rsnd_dma_probe(struct rsnd_priv *priv);
|
||||
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
|
||||
struct rsnd_mod *mod, char *name);
|
||||
|
||||
|
|
@ -231,11 +176,19 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
|
|||
* R-Car sound mod
|
||||
*/
|
||||
enum rsnd_mod_type {
|
||||
RSND_MOD_DVC = 0,
|
||||
RSND_MOD_AUDMAPP,
|
||||
RSND_MOD_AUDMA,
|
||||
RSND_MOD_DVC,
|
||||
RSND_MOD_MIX,
|
||||
RSND_MOD_CTU,
|
||||
RSND_MOD_CMD,
|
||||
RSND_MOD_SRC,
|
||||
RSND_MOD_SSIM3, /* SSI multi 3 */
|
||||
RSND_MOD_SSIM2, /* SSI multi 2 */
|
||||
RSND_MOD_SSIM1, /* SSI multi 1 */
|
||||
RSND_MOD_SSIP, /* SSI parent */
|
||||
RSND_MOD_SSI,
|
||||
RSND_MOD_SSIU,
|
||||
RSND_MOD_MAX,
|
||||
};
|
||||
|
||||
|
|
@ -278,10 +231,8 @@ struct rsnd_mod {
|
|||
int id;
|
||||
enum rsnd_mod_type type;
|
||||
struct rsnd_mod_ops *ops;
|
||||
struct rsnd_dma dma;
|
||||
struct rsnd_priv *priv;
|
||||
struct clk *clk;
|
||||
u32 status;
|
||||
};
|
||||
/*
|
||||
* status
|
||||
|
|
@ -328,7 +279,6 @@ struct rsnd_mod {
|
|||
#define __rsnd_mod_call_hw_params 0
|
||||
|
||||
#define rsnd_mod_to_priv(mod) ((mod)->priv)
|
||||
#define rsnd_mod_to_dma(mod) (&(mod)->dma)
|
||||
#define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1)
|
||||
#define rsnd_mod_power_on(mod) clk_enable((mod)->clk)
|
||||
#define rsnd_mod_power_off(mod) clk_disable((mod)->clk)
|
||||
|
|
@ -347,6 +297,17 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
|
|||
void rsnd_mod_interrupt(struct rsnd_mod *mod,
|
||||
void (*callback)(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io));
|
||||
void rsnd_parse_connect_common(struct rsnd_dai *rdai,
|
||||
struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
|
||||
struct device_node *node,
|
||||
struct device_node *playback,
|
||||
struct device_node *capture);
|
||||
|
||||
void rsnd_set_slot(struct rsnd_dai *rdai,
|
||||
int slots, int slots_total);
|
||||
int rsnd_get_slot(struct rsnd_dai_stream *io);
|
||||
int rsnd_get_slot_width(struct rsnd_dai_stream *io);
|
||||
int rsnd_get_slot_num(struct rsnd_dai_stream *io);
|
||||
|
||||
/*
|
||||
* R-Car sound DAI
|
||||
|
|
@ -358,6 +319,7 @@ struct rsnd_dai_stream {
|
|||
struct rsnd_mod *mod[RSND_MOD_MAX];
|
||||
struct rsnd_dai_path_info *info; /* rcar_snd.h */
|
||||
struct rsnd_dai *rdai;
|
||||
u32 mod_status[RSND_MOD_MAX];
|
||||
int byte_pos;
|
||||
int period_pos;
|
||||
int byte_per_period;
|
||||
|
|
@ -365,10 +327,12 @@ struct rsnd_dai_stream {
|
|||
};
|
||||
#define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL)
|
||||
#define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI)
|
||||
#define rsnd_io_to_mod_ssip(io) rsnd_io_to_mod((io), RSND_MOD_SSIP)
|
||||
#define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC)
|
||||
#define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU)
|
||||
#define rsnd_io_to_mod_mix(io) rsnd_io_to_mod((io), RSND_MOD_MIX)
|
||||
#define rsnd_io_to_mod_dvc(io) rsnd_io_to_mod((io), RSND_MOD_DVC)
|
||||
#define rsnd_io_to_mod_cmd(io) rsnd_io_to_mod((io), RSND_MOD_CMD)
|
||||
#define rsnd_io_to_rdai(io) ((io)->rdai)
|
||||
#define rsnd_io_to_priv(io) (rsnd_rdai_to_priv(rsnd_io_to_rdai(io)))
|
||||
#define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io)
|
||||
|
|
@ -382,6 +346,9 @@ struct rsnd_dai {
|
|||
struct rsnd_dai_stream capture;
|
||||
struct rsnd_priv *priv;
|
||||
|
||||
int slots;
|
||||
int slots_num;
|
||||
|
||||
unsigned int clk_master:1;
|
||||
unsigned int bit_clk_inv:1;
|
||||
unsigned int frm_clk_inv:1;
|
||||
|
|
@ -403,33 +370,28 @@ struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id);
|
|||
bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
|
||||
void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io);
|
||||
int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
|
||||
int rsnd_dai_connect(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
enum rsnd_mod_type type);
|
||||
#define rsnd_dai_of_node(priv) \
|
||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dai")
|
||||
|
||||
/*
|
||||
* R-Car Gen1/Gen2
|
||||
*/
|
||||
int rsnd_gen_probe(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv);
|
||||
int rsnd_gen_probe(struct rsnd_priv *priv);
|
||||
void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
|
||||
struct rsnd_mod *mod,
|
||||
enum rsnd_reg reg);
|
||||
phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id);
|
||||
|
||||
#define rsnd_is_gen1(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1)
|
||||
#define rsnd_is_gen2(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2)
|
||||
|
||||
/*
|
||||
* R-Car ADG
|
||||
*/
|
||||
int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
|
||||
int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
|
||||
int rsnd_adg_probe(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv);
|
||||
int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
|
||||
struct rsnd_mod *mod,
|
||||
unsigned int src_rate,
|
||||
unsigned int dst_rate);
|
||||
int rsnd_adg_probe(struct rsnd_priv *priv);
|
||||
void rsnd_adg_remove(struct rsnd_priv *priv);
|
||||
int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
unsigned int src_rate,
|
||||
|
|
@ -442,15 +404,14 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
|
|||
/*
|
||||
* R-Car sound priv
|
||||
*/
|
||||
struct rsnd_of_data {
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
struct rsnd_priv {
|
||||
|
||||
struct platform_device *pdev;
|
||||
struct rcar_snd_info *info;
|
||||
spinlock_t lock;
|
||||
unsigned long flags;
|
||||
#define RSND_GEN_MASK (0xF << 0)
|
||||
#define RSND_GEN1 (1 << 0)
|
||||
#define RSND_GEN2 (2 << 0)
|
||||
|
||||
/*
|
||||
* below value will be filled on rsnd_gen_probe()
|
||||
|
|
@ -473,6 +434,12 @@ struct rsnd_priv {
|
|||
void *ssi;
|
||||
int ssi_nr;
|
||||
|
||||
/*
|
||||
* below value will be filled on rsnd_ssiu_probe()
|
||||
*/
|
||||
void *ssiu;
|
||||
int ssiu_nr;
|
||||
|
||||
/*
|
||||
* below value will be filled on rsnd_src_probe()
|
||||
*/
|
||||
|
|
@ -497,6 +464,12 @@ struct rsnd_priv {
|
|||
void *dvc;
|
||||
int dvc_nr;
|
||||
|
||||
/*
|
||||
* below value will be filled on rsnd_cmd_probe()
|
||||
*/
|
||||
void *cmd;
|
||||
int cmd_nr;
|
||||
|
||||
/*
|
||||
* below value will be filled on rsnd_dai_probe()
|
||||
*/
|
||||
|
|
@ -507,7 +480,9 @@ struct rsnd_priv {
|
|||
|
||||
#define rsnd_priv_to_pdev(priv) ((priv)->pdev)
|
||||
#define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev))
|
||||
#define rsnd_priv_to_info(priv) ((priv)->info)
|
||||
|
||||
#define rsnd_is_gen1(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN1)
|
||||
#define rsnd_is_gen2(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN2)
|
||||
|
||||
/*
|
||||
* rsnd_kctrl
|
||||
|
|
@ -523,7 +498,7 @@ struct rsnd_kctrl_cfg {
|
|||
struct snd_kcontrol *kctrl;
|
||||
};
|
||||
|
||||
#define RSND_DVC_CHANNELS 2
|
||||
#define RSND_DVC_CHANNELS 8
|
||||
struct rsnd_kctrl_cfg_m {
|
||||
struct rsnd_kctrl_cfg cfg;
|
||||
u32 val[RSND_DVC_CHANNELS];
|
||||
|
|
@ -544,6 +519,7 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod,
|
|||
void (*update)(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod),
|
||||
struct rsnd_kctrl_cfg_m *_cfg,
|
||||
int ch_size,
|
||||
u32 max);
|
||||
int rsnd_kctrl_new_s(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
|
|
@ -566,70 +542,93 @@ int rsnd_kctrl_new_e(struct rsnd_mod *mod,
|
|||
/*
|
||||
* R-Car SSI
|
||||
*/
|
||||
int rsnd_ssi_probe(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv);
|
||||
void rsnd_ssi_remove(struct platform_device *pdev,
|
||||
struct rsnd_priv *priv);
|
||||
int rsnd_ssi_probe(struct rsnd_priv *priv);
|
||||
void rsnd_ssi_remove(struct rsnd_priv *priv);
|
||||
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
|
||||
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
|
||||
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
|
||||
u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io);
|
||||
|
||||
#define rsnd_ssi_is_pin_sharing(io) \
|
||||
__rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
|
||||
int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
|
||||
|
||||
#define rsnd_ssi_of_node(priv) \
|
||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
|
||||
void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
|
||||
struct device_node *playback,
|
||||
struct device_node *capture);
|
||||
|
||||
/*
|
||||
* R-Car SSIU
|
||||
*/
|
||||
int rsnd_ssiu_attach(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *mod);
|
||||
int rsnd_ssiu_probe(struct rsnd_priv *priv);
|
||||
void rsnd_ssiu_remove(struct rsnd_priv *priv);
|
||||
|
||||
/*
|
||||
* R-Car SRC
|
||||
*/
|
||||
int rsnd_src_probe(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv);
|
||||
void rsnd_src_remove(struct platform_device *pdev,
|
||||
struct rsnd_priv *priv);
|
||||
int rsnd_src_probe(struct rsnd_priv *priv);
|
||||
void rsnd_src_remove(struct rsnd_priv *priv);
|
||||
struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id);
|
||||
unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct snd_pcm_runtime *runtime);
|
||||
int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
int use_busif);
|
||||
int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
|
||||
struct rsnd_dai_stream *io);
|
||||
int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod);
|
||||
int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod);
|
||||
#define rsnd_src_of_node(priv) \
|
||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src")
|
||||
#define rsnd_parse_connect_src(rdai, playback, capture) \
|
||||
rsnd_parse_connect_common(rdai, rsnd_src_mod_get, \
|
||||
rsnd_src_of_node(rsnd_rdai_to_priv(rdai)), \
|
||||
playback, capture)
|
||||
|
||||
/*
|
||||
* R-Car CTU
|
||||
*/
|
||||
int rsnd_ctu_probe(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv);
|
||||
|
||||
void rsnd_ctu_remove(struct platform_device *pdev,
|
||||
struct rsnd_priv *priv);
|
||||
int rsnd_ctu_probe(struct rsnd_priv *priv);
|
||||
void rsnd_ctu_remove(struct rsnd_priv *priv);
|
||||
struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
|
||||
#define rsnd_ctu_of_node(priv) \
|
||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ctu")
|
||||
#define rsnd_parse_connect_ctu(rdai, playback, capture) \
|
||||
rsnd_parse_connect_common(rdai, rsnd_ctu_mod_get, \
|
||||
rsnd_ctu_of_node(rsnd_rdai_to_priv(rdai)), \
|
||||
playback, capture)
|
||||
|
||||
/*
|
||||
* R-Car MIX
|
||||
*/
|
||||
int rsnd_mix_probe(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv);
|
||||
|
||||
void rsnd_mix_remove(struct platform_device *pdev,
|
||||
struct rsnd_priv *priv);
|
||||
int rsnd_mix_probe(struct rsnd_priv *priv);
|
||||
void rsnd_mix_remove(struct rsnd_priv *priv);
|
||||
struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id);
|
||||
#define rsnd_mix_of_node(priv) \
|
||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,mix")
|
||||
#define rsnd_parse_connect_mix(rdai, playback, capture) \
|
||||
rsnd_parse_connect_common(rdai, rsnd_mix_mod_get, \
|
||||
rsnd_mix_of_node(rsnd_rdai_to_priv(rdai)), \
|
||||
playback, capture)
|
||||
|
||||
/*
|
||||
* R-Car DVC
|
||||
*/
|
||||
int rsnd_dvc_probe(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv);
|
||||
void rsnd_dvc_remove(struct platform_device *pdev,
|
||||
struct rsnd_priv *priv);
|
||||
int rsnd_dvc_probe(struct rsnd_priv *priv);
|
||||
void rsnd_dvc_remove(struct rsnd_priv *priv);
|
||||
struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
|
||||
#define rsnd_dvc_of_node(priv) \
|
||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc")
|
||||
#define rsnd_parse_connect_dvc(rdai, playback, capture) \
|
||||
rsnd_parse_connect_common(rdai, rsnd_dvc_mod_get, \
|
||||
rsnd_dvc_of_node(rsnd_rdai_to_priv(rdai)), \
|
||||
playback, capture)
|
||||
|
||||
/*
|
||||
* R-Car CMD
|
||||
*/
|
||||
int rsnd_cmd_probe(struct rsnd_priv *priv);
|
||||
void rsnd_cmd_remove(struct rsnd_priv *priv);
|
||||
int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id);
|
||||
struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id);
|
||||
|
||||
#ifdef DEBUG
|
||||
void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
|
||||
|
|
|
|||
|
|
@ -48,8 +48,11 @@ MODULE_DEVICE_TABLE(of, rsrc_card_of_match);
|
|||
|
||||
#define DAI_NAME_NUM 32
|
||||
struct rsrc_card_dai {
|
||||
unsigned int fmt;
|
||||
unsigned int sysclk;
|
||||
unsigned int tx_slot_mask;
|
||||
unsigned int rx_slot_mask;
|
||||
int slots;
|
||||
int slot_width;
|
||||
struct clk *clk;
|
||||
char dai_name[DAI_NAME_NUM];
|
||||
};
|
||||
|
|
@ -110,14 +113,6 @@ static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|||
rtd->cpu_dai :
|
||||
rtd->codec_dai;
|
||||
|
||||
if (dai_props->fmt) {
|
||||
ret = snd_soc_dai_set_fmt(dai, dai_props->fmt);
|
||||
if (ret && ret != -ENOTSUPP) {
|
||||
dev_err(dai->dev, "set_fmt error\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (dai_props->sysclk) {
|
||||
ret = snd_soc_dai_set_sysclk(dai, 0, dai_props->sysclk, 0);
|
||||
if (ret && ret != -ENOTSUPP) {
|
||||
|
|
@ -126,6 +121,18 @@ static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|||
}
|
||||
}
|
||||
|
||||
if (dai_props->slots) {
|
||||
ret = snd_soc_dai_set_tdm_slot(dai,
|
||||
dai_props->tx_slot_mask,
|
||||
dai_props->rx_slot_mask,
|
||||
dai_props->slots,
|
||||
dai_props->slot_width);
|
||||
if (ret && ret != -ENOTSUPP) {
|
||||
dev_err(dai->dev, "set_tdm_slot error\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
err:
|
||||
|
|
@ -148,14 +155,13 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
|||
}
|
||||
|
||||
static int rsrc_card_parse_daifmt(struct device_node *node,
|
||||
struct device_node *np,
|
||||
struct device_node *codec,
|
||||
struct rsrc_card_priv *priv,
|
||||
int idx, bool is_fe)
|
||||
struct snd_soc_dai_link *dai_link,
|
||||
unsigned int *retfmt)
|
||||
{
|
||||
struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
|
||||
struct device_node *bitclkmaster = NULL;
|
||||
struct device_node *framemaster = NULL;
|
||||
struct device_node *codec = is_fe ? NULL : np;
|
||||
unsigned int daifmt;
|
||||
|
||||
daifmt = snd_soc_of_parse_daifmt(node, NULL,
|
||||
|
|
@ -172,11 +178,11 @@ static int rsrc_card_parse_daifmt(struct device_node *node,
|
|||
daifmt |= (codec == framemaster) ?
|
||||
SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
|
||||
|
||||
dai_props->fmt = daifmt;
|
||||
|
||||
of_node_put(bitclkmaster);
|
||||
of_node_put(framemaster);
|
||||
|
||||
*retfmt = daifmt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -198,6 +204,15 @@ static int rsrc_card_parse_links(struct device_node *np,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Parse TDM slot */
|
||||
ret = snd_soc_of_parse_tdm_slot(np,
|
||||
&dai_props->tx_slot_mask,
|
||||
&dai_props->rx_slot_mask,
|
||||
&dai_props->slots,
|
||||
&dai_props->slot_width);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (is_fe) {
|
||||
/* BE is dummy */
|
||||
dai_link->codec_of_node = NULL;
|
||||
|
|
@ -208,7 +223,9 @@ static int rsrc_card_parse_links(struct device_node *np,
|
|||
dai_link->dynamic = 1;
|
||||
dai_link->dpcm_merged_format = 1;
|
||||
dai_link->cpu_of_node = args.np;
|
||||
snd_soc_of_get_dai_name(np, &dai_link->cpu_dai_name);
|
||||
ret = snd_soc_of_get_dai_name(np, &dai_link->cpu_dai_name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set dai_name */
|
||||
snprintf(dai_props->dai_name, DAI_NAME_NUM, "fe.%s",
|
||||
|
|
@ -240,7 +257,9 @@ static int rsrc_card_parse_links(struct device_node *np,
|
|||
dai_link->no_pcm = 1;
|
||||
dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup;
|
||||
dai_link->codec_of_node = args.np;
|
||||
snd_soc_of_get_dai_name(np, &dai_link->codec_dai_name);
|
||||
ret = snd_soc_of_get_dai_name(np, &dai_link->codec_dai_name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* additional name prefix */
|
||||
if (of_data) {
|
||||
|
|
@ -305,23 +324,16 @@ static int rsrc_card_parse_clk(struct device_node *np,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int rsrc_card_dai_link_of(struct device_node *node,
|
||||
struct device_node *np,
|
||||
struct rsrc_card_priv *priv,
|
||||
int idx)
|
||||
static int rsrc_card_dai_sub_link_of(struct device_node *node,
|
||||
struct device_node *np,
|
||||
struct rsrc_card_priv *priv,
|
||||
int idx, bool is_fe)
|
||||
{
|
||||
struct device *dev = rsrc_priv_to_dev(priv);
|
||||
struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
|
||||
struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
|
||||
bool is_fe = false;
|
||||
int ret;
|
||||
|
||||
if (0 == strcmp(np->name, "cpu"))
|
||||
is_fe = true;
|
||||
|
||||
ret = rsrc_card_parse_daifmt(node, np, priv, idx, is_fe);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rsrc_card_parse_links(np, priv, idx, is_fe);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
@ -332,12 +344,54 @@ static int rsrc_card_dai_link_of(struct device_node *node,
|
|||
|
||||
dev_dbg(dev, "\t%s / %04x / %d\n",
|
||||
dai_props->dai_name,
|
||||
dai_props->fmt,
|
||||
dai_link->dai_fmt,
|
||||
dai_props->sysclk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rsrc_card_dai_link_of(struct device_node *node,
|
||||
struct rsrc_card_priv *priv)
|
||||
{
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
struct device_node *np;
|
||||
unsigned int daifmt = 0;
|
||||
int ret, i;
|
||||
bool is_fe;
|
||||
|
||||
/* find 1st codec */
|
||||
i = 0;
|
||||
for_each_child_of_node(node, np) {
|
||||
dai_link = rsrc_priv_to_link(priv, i);
|
||||
|
||||
if (strcmp(np->name, "codec") == 0) {
|
||||
ret = rsrc_card_parse_daifmt(node, np, priv,
|
||||
dai_link, &daifmt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for_each_child_of_node(node, np) {
|
||||
dai_link = rsrc_priv_to_link(priv, i);
|
||||
dai_link->dai_fmt = daifmt;
|
||||
|
||||
is_fe = false;
|
||||
if (strcmp(np->name, "cpu") == 0)
|
||||
is_fe = true;
|
||||
|
||||
ret = rsrc_card_dai_sub_link_of(node, np, priv, i, is_fe);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsrc_card_parse_of(struct device_node *node,
|
||||
struct rsrc_card_priv *priv,
|
||||
struct device *dev)
|
||||
|
|
@ -345,9 +399,8 @@ static int rsrc_card_parse_of(struct device_node *node,
|
|||
const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
|
||||
struct rsrc_card_dai *props;
|
||||
struct snd_soc_dai_link *links;
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
int i, num;
|
||||
int num;
|
||||
|
||||
if (!node)
|
||||
return -EINVAL;
|
||||
|
|
@ -388,13 +441,9 @@ static int rsrc_card_parse_of(struct device_node *node,
|
|||
priv->snd_card.name ? priv->snd_card.name : "",
|
||||
priv->convert_rate);
|
||||
|
||||
i = 0;
|
||||
for_each_child_of_node(node, np) {
|
||||
ret = rsrc_card_dai_link_of(node, np, priv, i);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
i++;
|
||||
}
|
||||
ret = rsrc_card_dai_link_of(node, priv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!priv->snd_card.name)
|
||||
priv->snd_card.name = priv->snd_card.dai_link->name;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
225
sound/soc/sh/rcar/ssiu.c
Normal file
225
sound/soc/sh/rcar/ssiu.c
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* Renesas R-Car SSIU support
|
||||
*
|
||||
* Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include "rsnd.h"
|
||||
|
||||
#define SSIU_NAME "ssiu"
|
||||
|
||||
struct rsnd_ssiu {
|
||||
struct rsnd_mod mod;
|
||||
};
|
||||
|
||||
#define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr)
|
||||
#define for_each_rsnd_ssiu(pos, priv, i) \
|
||||
for (i = 0; \
|
||||
(i < rsnd_ssiu_nr(priv)) && \
|
||||
((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i)); \
|
||||
i++)
|
||||
|
||||
static int rsnd_ssiu_init(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
||||
u32 multi_ssi_slaves = rsnd_ssi_multi_slaves(io);
|
||||
int use_busif = rsnd_ssi_use_busif(io);
|
||||
int id = rsnd_mod_id(mod);
|
||||
u32 mask1, val1;
|
||||
u32 mask2, val2;
|
||||
|
||||
/*
|
||||
* SSI_MODE0
|
||||
*/
|
||||
rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id);
|
||||
|
||||
/*
|
||||
* SSI_MODE1
|
||||
*/
|
||||
mask1 = (1 << 4) | (1 << 20); /* mask sync bit */
|
||||
mask2 = (1 << 4); /* mask sync bit */
|
||||
val1 = val2 = 0;
|
||||
if (rsnd_ssi_is_pin_sharing(io)) {
|
||||
int shift = -1;
|
||||
|
||||
switch (id) {
|
||||
case 1:
|
||||
shift = 0;
|
||||
break;
|
||||
case 2:
|
||||
shift = 2;
|
||||
break;
|
||||
case 4:
|
||||
shift = 16;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mask1 |= 0x3 << shift;
|
||||
val1 = rsnd_rdai_is_clk_master(rdai) ?
|
||||
0x2 << shift : 0x1 << shift;
|
||||
|
||||
} else if (multi_ssi_slaves) {
|
||||
|
||||
mask2 |= 0x00000007;
|
||||
mask1 |= 0x0000000f;
|
||||
|
||||
switch (multi_ssi_slaves) {
|
||||
case 0x0206: /* SSI0/1/2/9 */
|
||||
val2 = (1 << 4) | /* SSI0129 sync */
|
||||
rsnd_rdai_is_clk_master(rdai) ? 0x2 : 0x1;
|
||||
/* fall through */
|
||||
case 0x0006: /* SSI0/1/2 */
|
||||
val1 = rsnd_rdai_is_clk_master(rdai) ?
|
||||
0xa : 0x5;
|
||||
|
||||
if (!val2) /* SSI012 sync */
|
||||
val1 |= (1 << 4);
|
||||
}
|
||||
}
|
||||
|
||||
rsnd_mod_bset(mod, SSI_MODE1, mask1, val1);
|
||||
rsnd_mod_bset(mod, SSI_MODE2, mask2, val2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rsnd_mod_ops rsnd_ssiu_ops_gen1 = {
|
||||
.name = SSIU_NAME,
|
||||
.init = rsnd_ssiu_init,
|
||||
};
|
||||
|
||||
static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rsnd_ssiu_init(mod, io, priv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (rsnd_get_slot_width(io) >= 6) {
|
||||
/*
|
||||
* TDM Extend Mode
|
||||
* see
|
||||
* rsnd_ssi_config_init()
|
||||
*/
|
||||
rsnd_mod_write(mod, SSI_MODE, 0x1);
|
||||
}
|
||||
|
||||
if (rsnd_ssi_use_busif(io)) {
|
||||
u32 val = rsnd_get_dalign(mod, io);
|
||||
|
||||
rsnd_mod_write(mod, SSI_BUSIF_ADINR,
|
||||
rsnd_get_adinr_bit(mod, io) |
|
||||
rsnd_get_adinr_chan(mod, io));
|
||||
rsnd_mod_write(mod, SSI_BUSIF_MODE, 1);
|
||||
rsnd_mod_write(mod, SSI_BUSIF_DALIGN, val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
if (!rsnd_ssi_use_busif(io))
|
||||
return 0;
|
||||
|
||||
rsnd_mod_write(mod, SSI_CTRL, 0x1);
|
||||
|
||||
if (rsnd_ssi_multi_slaves(io))
|
||||
rsnd_mod_write(mod, SSI_CONTROL, 0x1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
if (!rsnd_ssi_use_busif(io))
|
||||
return 0;
|
||||
|
||||
rsnd_mod_write(mod, SSI_CTRL, 0);
|
||||
|
||||
if (rsnd_ssi_multi_slaves(io))
|
||||
rsnd_mod_write(mod, SSI_CONTROL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = {
|
||||
.name = SSIU_NAME,
|
||||
.init = rsnd_ssiu_init_gen2,
|
||||
.start = rsnd_ssiu_start_gen2,
|
||||
.stop = rsnd_ssiu_stop_gen2,
|
||||
};
|
||||
|
||||
static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id)
|
||||
{
|
||||
if (WARN_ON(id < 0 || id >= rsnd_ssiu_nr(priv)))
|
||||
id = 0;
|
||||
|
||||
return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id);
|
||||
}
|
||||
|
||||
int rsnd_ssiu_attach(struct rsnd_dai_stream *io,
|
||||
struct rsnd_mod *ssi_mod)
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_io_to_priv(io);
|
||||
struct rsnd_mod *mod = rsnd_ssiu_mod_get(priv, rsnd_mod_id(ssi_mod));
|
||||
|
||||
rsnd_mod_confirm_ssi(ssi_mod);
|
||||
|
||||
return rsnd_dai_connect(mod, io, mod->type);
|
||||
}
|
||||
|
||||
int rsnd_ssiu_probe(struct rsnd_priv *priv)
|
||||
{
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct rsnd_ssiu *ssiu;
|
||||
static struct rsnd_mod_ops *ops;
|
||||
int i, nr, ret;
|
||||
|
||||
/* same number to SSI */
|
||||
nr = priv->ssi_nr;
|
||||
ssiu = devm_kzalloc(dev, sizeof(*ssiu) * nr, GFP_KERNEL);
|
||||
if (!ssiu)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->ssiu = ssiu;
|
||||
priv->ssiu_nr = nr;
|
||||
|
||||
if (rsnd_is_gen1(priv))
|
||||
ops = &rsnd_ssiu_ops_gen1;
|
||||
else
|
||||
ops = &rsnd_ssiu_ops_gen2;
|
||||
|
||||
for_each_rsnd_ssiu(ssiu, priv, i) {
|
||||
ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
|
||||
ops, NULL, RSND_MOD_SSIU, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rsnd_ssiu_remove(struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_ssiu *ssiu;
|
||||
int i;
|
||||
|
||||
for_each_rsnd_ssiu(ssiu, priv, i) {
|
||||
rsnd_mod_quit(rsnd_mod_get(ssiu));
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of.h>
|
||||
|
|
@ -38,6 +39,14 @@ struct snd_ac97_reset_cfg {
|
|||
int gpio_reset;
|
||||
};
|
||||
|
||||
struct snd_ac97_gpio_priv {
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
struct gpio_chip gpio_chip;
|
||||
#endif
|
||||
unsigned int gpios_set;
|
||||
struct snd_soc_codec *codec;
|
||||
};
|
||||
|
||||
static struct snd_ac97_bus soc_ac97_bus = {
|
||||
.ops = NULL, /* Gets initialized in snd_soc_set_ac97_ops() */
|
||||
};
|
||||
|
|
@ -47,6 +56,117 @@ static void soc_ac97_device_release(struct device *dev)
|
|||
kfree(to_ac97_t(dev));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
static inline struct snd_soc_codec *gpio_to_codec(struct gpio_chip *chip)
|
||||
{
|
||||
struct snd_ac97_gpio_priv *gpio_priv =
|
||||
container_of(chip, struct snd_ac97_gpio_priv, gpio_chip);
|
||||
|
||||
return gpio_priv->codec;
|
||||
}
|
||||
|
||||
static int snd_soc_ac97_gpio_request(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
if (offset >= AC97_NUM_GPIOS)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_soc_ac97_gpio_direction_in(struct gpio_chip *chip,
|
||||
unsigned offset)
|
||||
{
|
||||
struct snd_soc_codec *codec = gpio_to_codec(chip);
|
||||
|
||||
dev_dbg(codec->dev, "set gpio %d to output\n", offset);
|
||||
return snd_soc_update_bits(codec, AC97_GPIO_CFG,
|
||||
1 << offset, 1 << offset);
|
||||
}
|
||||
|
||||
static int snd_soc_ac97_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct snd_soc_codec *codec = gpio_to_codec(chip);
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_read(codec, AC97_GPIO_STATUS);
|
||||
dev_dbg(codec->dev, "get gpio %d : %d\n", offset,
|
||||
ret < 0 ? ret : ret & (1 << offset));
|
||||
|
||||
return ret < 0 ? ret : !!(ret & (1 << offset));
|
||||
}
|
||||
|
||||
static void snd_soc_ac97_gpio_set(struct gpio_chip *chip, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct snd_ac97_gpio_priv *gpio_priv =
|
||||
container_of(chip, struct snd_ac97_gpio_priv, gpio_chip);
|
||||
struct snd_soc_codec *codec = gpio_to_codec(chip);
|
||||
|
||||
gpio_priv->gpios_set &= ~(1 << offset);
|
||||
gpio_priv->gpios_set |= (!!value) << offset;
|
||||
snd_soc_write(codec, AC97_GPIO_STATUS, gpio_priv->gpios_set);
|
||||
dev_dbg(codec->dev, "set gpio %d to %d\n", offset, !!value);
|
||||
}
|
||||
|
||||
static int snd_soc_ac97_gpio_direction_out(struct gpio_chip *chip,
|
||||
unsigned offset, int value)
|
||||
{
|
||||
struct snd_soc_codec *codec = gpio_to_codec(chip);
|
||||
|
||||
dev_dbg(codec->dev, "set gpio %d to output\n", offset);
|
||||
snd_soc_ac97_gpio_set(chip, offset, value);
|
||||
return snd_soc_update_bits(codec, AC97_GPIO_CFG, 1 << offset, 0);
|
||||
}
|
||||
|
||||
static struct gpio_chip snd_soc_ac97_gpio_chip = {
|
||||
.label = "snd_soc_ac97",
|
||||
.owner = THIS_MODULE,
|
||||
.request = snd_soc_ac97_gpio_request,
|
||||
.direction_input = snd_soc_ac97_gpio_direction_in,
|
||||
.get = snd_soc_ac97_gpio_get,
|
||||
.direction_output = snd_soc_ac97_gpio_direction_out,
|
||||
.set = snd_soc_ac97_gpio_set,
|
||||
.can_sleep = 1,
|
||||
};
|
||||
|
||||
static int snd_soc_ac97_init_gpio(struct snd_ac97 *ac97,
|
||||
struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_ac97_gpio_priv *gpio_priv;
|
||||
int ret;
|
||||
|
||||
gpio_priv = devm_kzalloc(codec->dev, sizeof(*gpio_priv), GFP_KERNEL);
|
||||
if (!gpio_priv)
|
||||
return -ENOMEM;
|
||||
ac97->gpio_priv = gpio_priv;
|
||||
gpio_priv->codec = codec;
|
||||
gpio_priv->gpio_chip = snd_soc_ac97_gpio_chip;
|
||||
gpio_priv->gpio_chip.ngpio = AC97_NUM_GPIOS;
|
||||
gpio_priv->gpio_chip.dev = codec->dev;
|
||||
gpio_priv->gpio_chip.base = -1;
|
||||
|
||||
ret = gpiochip_add(&gpio_priv->gpio_chip);
|
||||
if (ret != 0)
|
||||
dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void snd_soc_ac97_free_gpio(struct snd_ac97 *ac97)
|
||||
{
|
||||
gpiochip_remove(&ac97->gpio_priv->gpio_chip);
|
||||
}
|
||||
#else
|
||||
static int snd_soc_ac97_init_gpio(struct snd_ac97 *ac97,
|
||||
struct snd_soc_codec *codec)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_soc_ac97_free_gpio(struct snd_ac97 *ac97)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* snd_soc_alloc_ac97_codec() - Allocate new a AC'97 device
|
||||
* @codec: The CODEC for which to create the AC'97 device
|
||||
|
|
@ -119,6 +239,10 @@ struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
|
|||
if (ret)
|
||||
goto err_put_device;
|
||||
|
||||
ret = snd_soc_ac97_init_gpio(ac97, codec);
|
||||
if (ret)
|
||||
goto err_put_device;
|
||||
|
||||
return ac97;
|
||||
|
||||
err_put_device:
|
||||
|
|
@ -135,6 +259,7 @@ EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
|
|||
*/
|
||||
void snd_soc_free_ac97_codec(struct snd_ac97 *ac97)
|
||||
{
|
||||
snd_soc_ac97_free_gpio(ac97);
|
||||
device_del(&ac97->dev);
|
||||
ac97->bus = NULL;
|
||||
put_device(&ac97->dev);
|
||||
|
|
|
|||
|
|
@ -960,9 +960,22 @@ static struct snd_soc_dai *snd_soc_find_dai(
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int soc_bind_dai_link(struct snd_soc_card *card, int num)
|
||||
static bool soc_is_dai_link_bound(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *dai_link)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
list_for_each_entry(rtd, &card->rtd_list, list) {
|
||||
if (rtd->dai_link == dai_link)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int soc_bind_dai_link(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *dai_link)
|
||||
{
|
||||
struct snd_soc_dai_link *dai_link = &card->dai_link[num];
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai_link_component *codecs = dai_link->codecs;
|
||||
struct snd_soc_dai_link_component cpu_dai_component;
|
||||
|
|
@ -971,12 +984,18 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
|
|||
const char *platform_name;
|
||||
int i;
|
||||
|
||||
dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);
|
||||
dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name);
|
||||
|
||||
rtd = soc_new_pcm_runtime(card, dai_link);
|
||||
if (!rtd)
|
||||
return -ENOMEM;
|
||||
|
||||
if (soc_is_dai_link_bound(card, dai_link)) {
|
||||
dev_dbg(card->dev, "ASoC: dai link %s already bound\n",
|
||||
dai_link->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cpu_dai_component.name = dai_link->cpu_name;
|
||||
cpu_dai_component.of_node = dai_link->cpu_of_node;
|
||||
cpu_dai_component.dai_name = dai_link->cpu_dai_name;
|
||||
|
|
@ -1120,6 +1139,7 @@ static void soc_remove_dai_links(struct snd_soc_card *card)
|
|||
{
|
||||
int order;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai_link *link, *_link;
|
||||
|
||||
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
|
||||
order++) {
|
||||
|
|
@ -1132,8 +1152,185 @@ static void soc_remove_dai_links(struct snd_soc_card *card)
|
|||
list_for_each_entry(rtd, &card->rtd_list, list)
|
||||
soc_remove_link_components(card, rtd, order);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(link, _link, &card->dai_link_list, list) {
|
||||
if (link->dobj.type == SND_SOC_DOBJ_DAI_LINK)
|
||||
dev_warn(card->dev, "Topology forgot to remove link %s?\n",
|
||||
link->name);
|
||||
|
||||
list_del(&link->list);
|
||||
card->num_dai_links--;
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_soc_init_multicodec(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *dai_link)
|
||||
{
|
||||
/* Legacy codec/codec_dai link is a single entry in multicodec */
|
||||
if (dai_link->codec_name || dai_link->codec_of_node ||
|
||||
dai_link->codec_dai_name) {
|
||||
dai_link->num_codecs = 1;
|
||||
|
||||
dai_link->codecs = devm_kzalloc(card->dev,
|
||||
sizeof(struct snd_soc_dai_link_component),
|
||||
GFP_KERNEL);
|
||||
if (!dai_link->codecs)
|
||||
return -ENOMEM;
|
||||
|
||||
dai_link->codecs[0].name = dai_link->codec_name;
|
||||
dai_link->codecs[0].of_node = dai_link->codec_of_node;
|
||||
dai_link->codecs[0].dai_name = dai_link->codec_dai_name;
|
||||
}
|
||||
|
||||
if (!dai_link->codecs) {
|
||||
dev_err(card->dev, "ASoC: DAI link has no CODECs\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int soc_init_dai_link(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *link)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
ret = snd_soc_init_multicodec(card, link);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "ASoC: failed to init multicodec\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < link->num_codecs; i++) {
|
||||
/*
|
||||
* Codec must be specified by 1 of name or OF node,
|
||||
* not both or neither.
|
||||
*/
|
||||
if (!!link->codecs[i].name ==
|
||||
!!link->codecs[i].of_node) {
|
||||
dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
|
||||
link->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Codec DAI name must be specified */
|
||||
if (!link->codecs[i].dai_name) {
|
||||
dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
|
||||
link->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Platform may be specified by either name or OF node, but
|
||||
* can be left unspecified, and a dummy platform will be used.
|
||||
*/
|
||||
if (link->platform_name && link->platform_of_node) {
|
||||
dev_err(card->dev,
|
||||
"ASoC: Both platform name/of_node are set for %s\n",
|
||||
link->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* CPU device may be specified by either name or OF node, but
|
||||
* can be left unspecified, and will be matched based on DAI
|
||||
* name alone..
|
||||
*/
|
||||
if (link->cpu_name && link->cpu_of_node) {
|
||||
dev_err(card->dev,
|
||||
"ASoC: Neither/both cpu name/of_node are set for %s\n",
|
||||
link->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* At least one of CPU DAI name or CPU device name/node must be
|
||||
* specified
|
||||
*/
|
||||
if (!link->cpu_dai_name &&
|
||||
!(link->cpu_name || link->cpu_of_node)) {
|
||||
dev_err(card->dev,
|
||||
"ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
|
||||
link->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_add_dai_link - Add a DAI link dynamically
|
||||
* @card: The ASoC card to which the DAI link is added
|
||||
* @dai_link: The new DAI link to add
|
||||
*
|
||||
* This function adds a DAI link to the ASoC card's link list.
|
||||
*
|
||||
* Note: Topology can use this API to add DAI links when probing the
|
||||
* topology component. And machine drivers can still define static
|
||||
* DAI links in dai_link array.
|
||||
*/
|
||||
int snd_soc_add_dai_link(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *dai_link)
|
||||
{
|
||||
if (dai_link->dobj.type
|
||||
&& dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) {
|
||||
dev_err(card->dev, "Invalid dai link type %d\n",
|
||||
dai_link->dobj.type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lockdep_assert_held(&client_mutex);
|
||||
/* Notify the machine driver for extra initialization
|
||||
* on the link created by topology.
|
||||
*/
|
||||
if (dai_link->dobj.type && card->add_dai_link)
|
||||
card->add_dai_link(card, dai_link);
|
||||
|
||||
list_add_tail(&dai_link->list, &card->dai_link_list);
|
||||
card->num_dai_links++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_add_dai_link);
|
||||
|
||||
/**
|
||||
* snd_soc_remove_dai_link - Remove a DAI link from the list
|
||||
* @card: The ASoC card that owns the link
|
||||
* @dai_link: The DAI link to remove
|
||||
*
|
||||
* This function removes a DAI link from the ASoC card's link list.
|
||||
*
|
||||
* For DAI links previously added by topology, topology should
|
||||
* remove them by using the dobj embedded in the link.
|
||||
*/
|
||||
void snd_soc_remove_dai_link(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *dai_link)
|
||||
{
|
||||
struct snd_soc_dai_link *link, *_link;
|
||||
|
||||
if (dai_link->dobj.type
|
||||
&& dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) {
|
||||
dev_err(card->dev, "Invalid dai link type %d\n",
|
||||
dai_link->dobj.type);
|
||||
return;
|
||||
}
|
||||
|
||||
lockdep_assert_held(&client_mutex);
|
||||
/* Notify the machine driver for extra destruction
|
||||
* on the link created by topology.
|
||||
*/
|
||||
if (dai_link->dobj.type && card->remove_dai_link)
|
||||
card->remove_dai_link(card, dai_link);
|
||||
|
||||
list_for_each_entry_safe(link, _link, &card->dai_link_list, list) {
|
||||
if (link == dai_link) {
|
||||
list_del(&link->list);
|
||||
card->num_dai_links--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link);
|
||||
|
||||
static void soc_set_name_prefix(struct snd_soc_card *card,
|
||||
struct snd_soc_component *component)
|
||||
{
|
||||
|
|
@ -1339,35 +1536,35 @@ static int soc_link_dai_widgets(struct snd_soc_card *card,
|
|||
{
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
struct snd_soc_dapm_widget *play_w, *capture_w;
|
||||
struct snd_soc_dapm_widget *sink, *source;
|
||||
int ret;
|
||||
|
||||
if (rtd->num_codecs > 1)
|
||||
dev_warn(card->dev, "ASoC: Multiple codecs not supported yet\n");
|
||||
|
||||
/* link the DAI widgets */
|
||||
play_w = codec_dai->playback_widget;
|
||||
capture_w = cpu_dai->capture_widget;
|
||||
if (play_w && capture_w) {
|
||||
sink = codec_dai->playback_widget;
|
||||
source = cpu_dai->capture_widget;
|
||||
if (sink && source) {
|
||||
ret = snd_soc_dapm_new_pcm(card, dai_link->params,
|
||||
dai_link->num_params, capture_w,
|
||||
play_w);
|
||||
dai_link->num_params,
|
||||
source, sink);
|
||||
if (ret != 0) {
|
||||
dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
|
||||
play_w->name, capture_w->name, ret);
|
||||
sink->name, source->name, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
play_w = cpu_dai->playback_widget;
|
||||
capture_w = codec_dai->capture_widget;
|
||||
if (play_w && capture_w) {
|
||||
sink = cpu_dai->playback_widget;
|
||||
source = codec_dai->capture_widget;
|
||||
if (sink && source) {
|
||||
ret = snd_soc_dapm_new_pcm(card, dai_link->params,
|
||||
dai_link->num_params, capture_w,
|
||||
play_w);
|
||||
dai_link->num_params,
|
||||
source, sink);
|
||||
if (ret != 0) {
|
||||
dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
|
||||
play_w->name, capture_w->name, ret);
|
||||
sink->name, source->name, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -1609,6 +1806,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
|
|||
{
|
||||
struct snd_soc_codec *codec;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
int ret, i, order;
|
||||
|
||||
mutex_lock(&client_mutex);
|
||||
|
|
@ -1616,7 +1814,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
|
|||
|
||||
/* bind DAIs */
|
||||
for (i = 0; i < card->num_links; i++) {
|
||||
ret = soc_bind_dai_link(card, i);
|
||||
ret = soc_bind_dai_link(card, &card->dai_link[i]);
|
||||
if (ret != 0)
|
||||
goto base_error;
|
||||
}
|
||||
|
|
@ -1628,6 +1826,10 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
|
|||
goto base_error;
|
||||
}
|
||||
|
||||
/* add predefined DAI links to the list */
|
||||
for (i = 0; i < card->num_links; i++)
|
||||
snd_soc_add_dai_link(card, card->dai_link+i);
|
||||
|
||||
/* initialize the register cache for each available codec */
|
||||
list_for_each_entry(codec, &codec_list, list) {
|
||||
if (codec->cache_init)
|
||||
|
|
@ -1692,6 +1894,21 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
|
|||
}
|
||||
}
|
||||
|
||||
/* Find new DAI links added during probing components and bind them.
|
||||
* Components with topology may bring new DAIs and DAI links.
|
||||
*/
|
||||
list_for_each_entry(dai_link, &card->dai_link_list, list) {
|
||||
if (soc_is_dai_link_bound(card, dai_link))
|
||||
continue;
|
||||
|
||||
ret = soc_init_dai_link(card, dai_link);
|
||||
if (ret)
|
||||
goto probe_dai_err;
|
||||
ret = soc_bind_dai_link(card, dai_link);
|
||||
if (ret)
|
||||
goto probe_dai_err;
|
||||
}
|
||||
|
||||
/* probe all DAI links on this card */
|
||||
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
|
||||
order++) {
|
||||
|
|
@ -2356,33 +2573,6 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
|
||||
|
||||
static int snd_soc_init_multicodec(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *dai_link)
|
||||
{
|
||||
/* Legacy codec/codec_dai link is a single entry in multicodec */
|
||||
if (dai_link->codec_name || dai_link->codec_of_node ||
|
||||
dai_link->codec_dai_name) {
|
||||
dai_link->num_codecs = 1;
|
||||
|
||||
dai_link->codecs = devm_kzalloc(card->dev,
|
||||
sizeof(struct snd_soc_dai_link_component),
|
||||
GFP_KERNEL);
|
||||
if (!dai_link->codecs)
|
||||
return -ENOMEM;
|
||||
|
||||
dai_link->codecs[0].name = dai_link->codec_name;
|
||||
dai_link->codecs[0].of_node = dai_link->codec_of_node;
|
||||
dai_link->codecs[0].dai_name = dai_link->codec_dai_name;
|
||||
}
|
||||
|
||||
if (!dai_link->codecs) {
|
||||
dev_err(card->dev, "ASoC: DAI link has no CODECs\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_soc_register_card - Register a card with the ASoC core
|
||||
*
|
||||
|
|
@ -2391,7 +2581,7 @@ static int snd_soc_init_multicodec(struct snd_soc_card *card,
|
|||
*/
|
||||
int snd_soc_register_card(struct snd_soc_card *card)
|
||||
{
|
||||
int i, j, ret;
|
||||
int i, ret;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
if (!card->name || !card->dev)
|
||||
|
|
@ -2400,70 +2590,21 @@ int snd_soc_register_card(struct snd_soc_card *card)
|
|||
for (i = 0; i < card->num_links; i++) {
|
||||
struct snd_soc_dai_link *link = &card->dai_link[i];
|
||||
|
||||
ret = snd_soc_init_multicodec(card, link);
|
||||
ret = soc_init_dai_link(card, link);
|
||||
if (ret) {
|
||||
dev_err(card->dev, "ASoC: failed to init multicodec\n");
|
||||
dev_err(card->dev, "ASoC: failed to init link %s\n",
|
||||
link->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (j = 0; j < link->num_codecs; j++) {
|
||||
/*
|
||||
* Codec must be specified by 1 of name or OF node,
|
||||
* not both or neither.
|
||||
*/
|
||||
if (!!link->codecs[j].name ==
|
||||
!!link->codecs[j].of_node) {
|
||||
dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
|
||||
link->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Codec DAI name must be specified */
|
||||
if (!link->codecs[j].dai_name) {
|
||||
dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
|
||||
link->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Platform may be specified by either name or OF node, but
|
||||
* can be left unspecified, and a dummy platform will be used.
|
||||
*/
|
||||
if (link->platform_name && link->platform_of_node) {
|
||||
dev_err(card->dev,
|
||||
"ASoC: Both platform name/of_node are set for %s\n",
|
||||
link->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* CPU device may be specified by either name or OF node, but
|
||||
* can be left unspecified, and will be matched based on DAI
|
||||
* name alone..
|
||||
*/
|
||||
if (link->cpu_name && link->cpu_of_node) {
|
||||
dev_err(card->dev,
|
||||
"ASoC: Neither/both cpu name/of_node are set for %s\n",
|
||||
link->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* At least one of CPU DAI name or CPU device name/node must be
|
||||
* specified
|
||||
*/
|
||||
if (!link->cpu_dai_name &&
|
||||
!(link->cpu_name || link->cpu_of_node)) {
|
||||
dev_err(card->dev,
|
||||
"ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
|
||||
link->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
dev_set_drvdata(card->dev, card);
|
||||
|
||||
snd_soc_initialize_card_lists(card);
|
||||
|
||||
INIT_LIST_HEAD(&card->dai_link_list);
|
||||
card->num_dai_links = 0;
|
||||
|
||||
INIT_LIST_HEAD(&card->rtd_list);
|
||||
card->num_rtd = 0;
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue