mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
ASoC: Add support for Airoha AN7581
Christian Marangi <ansuelsmth@gmail.com> says: Add all the patch and documentation to support Airoha AN7581 sound driver. The card follows similar implementation with Mediatek with the codec present on i2c but with a simplified implementation register wise and additional frequency supported. Link: https://patch.msgid.link/20260908090448.3332-1-ansuelsmth@gmail.com
This commit is contained in:
commit
42bdb61dec
|
|
@ -0,0 +1,41 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/airoha,an7581-afe.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Airoha AFE PCM controller for AN7581
|
||||
|
||||
maintainers:
|
||||
- Christian Marangi <ansuelsmth@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: airoha,an7581-afe
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
afe@1fbe2200 {
|
||||
compatible = "airoha,an7581-afe";
|
||||
reg = <0x1fbe2200 0x9000>;
|
||||
|
||||
interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
||||
...
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/airoha,an7581-wm8960.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Airoha AN7581 sound card with WM8960 codec
|
||||
|
||||
maintainers:
|
||||
- Christian Marangi <ansuelsmth@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: sound-card-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: airoha,an7581-wm8960-sound
|
||||
|
||||
platform:
|
||||
type: object
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
sound-dai:
|
||||
items:
|
||||
- description: The phandle of AN7581 platform.
|
||||
|
||||
required:
|
||||
- sound-dai
|
||||
|
||||
codec:
|
||||
type: object
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
sound-dai:
|
||||
items:
|
||||
- description: The phandle of WM8960 i2c codec.
|
||||
|
||||
required:
|
||||
- sound-dai
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- audio-routing
|
||||
- platform
|
||||
- codec
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
sound {
|
||||
compatible = "airoha,an7581-wm8960-sound";
|
||||
model = "an7581-wm8960";
|
||||
audio-routing =
|
||||
"Headphone", "HP_L",
|
||||
"Headphone", "HP_R",
|
||||
"LINPUT1", "AMIC",
|
||||
"RINPUT1", "AMIC";
|
||||
|
||||
platform {
|
||||
sound-dai = <&afe>;
|
||||
};
|
||||
|
||||
codec {
|
||||
sound-dai = <&wm8960>;
|
||||
};
|
||||
};
|
||||
|
|
@ -767,6 +767,14 @@ F: Documentation/devicetree/bindings/phy/airoha,en7581-pcie-phy.yaml
|
|||
F: drivers/phy/phy-airoha-pcie-regs.h
|
||||
F: drivers/phy/phy-airoha-pcie.c
|
||||
|
||||
AIROHA SOUND DRIVER
|
||||
M: Christian Marangi <ansuelsmth@gmail.com>
|
||||
L: linux-sound@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/sound/airoha,an7581-afe.yaml
|
||||
F: Documentation/devicetree/bindings/sound/airoha,an7581-wm8960.yaml
|
||||
F: sound/soc/mediatek/an7581/*
|
||||
|
||||
AIROHA SPI SNFI DRIVER
|
||||
M: Lorenzo Bianconi <lorenzo@kernel.org>
|
||||
M: Ray Liu <ray.liu@airoha.com>
|
||||
|
|
|
|||
|
|
@ -697,7 +697,8 @@ struct snd_soc_dai_link_component {
|
|||
struct snd_soc_dai_link_ch_map {
|
||||
unsigned int cpu;
|
||||
unsigned int codec;
|
||||
unsigned int ch_mask;
|
||||
unsigned int cpu_ch_mask;
|
||||
unsigned int codec_ch_mask;
|
||||
};
|
||||
|
||||
struct snd_soc_dai_link {
|
||||
|
|
|
|||
|
|
@ -250,8 +250,6 @@ int asoc_sdw_cs_amp_init(struct snd_soc_card *card,
|
|||
struct snd_soc_dai_link *dai_links,
|
||||
struct asoc_sdw_codec_info *info,
|
||||
bool playback);
|
||||
int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_soc_dai *dai);
|
||||
int asoc_sdw_cs35l56_volume_limit(struct snd_soc_card *card, const char *name_prefix);
|
||||
|
||||
/* MAXIM codec support */
|
||||
|
|
|
|||
|
|
@ -181,6 +181,7 @@ config SND_SOC_AMD_LEGACY_SDW_MACH
|
|||
select SND_SOC_RT715_SDW
|
||||
select SND_SOC_RT715_SDCA_SDW
|
||||
select SND_SOC_RT722_SDCA_SDW
|
||||
select SND_SOC_TAS2783_SDW
|
||||
help
|
||||
This option enables Legacy(No DSP) sound card support for SoundWire
|
||||
enabled AMD platforms along with ACP PDM controller.
|
||||
|
|
|
|||
|
|
@ -205,9 +205,19 @@ static int create_sdw_dailink(struct snd_soc_card *card,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!soc_end->link_mask) {
|
||||
dev_err(dev, "invalid zero link_mask\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((ffs(soc_end->link_mask) - 1) >= amd_ctx->max_sdw_links) {
|
||||
dev_err(dev, "link_id %d exceeds max_sdw_links %d\n",
|
||||
ffs(soc_end->link_mask) - 1, amd_ctx->max_sdw_links);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (amd_ctx->acp_rev) {
|
||||
case ACP63_PCI_REV:
|
||||
ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask - 1),
|
||||
ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask) - 1,
|
||||
*be_id, &cpu_pin_id, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -215,7 +225,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
|
|||
case ACP70_PCI_REV:
|
||||
case ACP71_PCI_REV:
|
||||
case ACP72_PCI_REV:
|
||||
ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask - 1),
|
||||
ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask) - 1,
|
||||
*be_id, &cpu_pin_id, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -121,9 +121,18 @@ static int create_sdw_dailink(struct snd_soc_card *card,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!sof_end->link_mask) {
|
||||
dev_err(dev, "invalid zero link_mask\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((ffs(sof_end->link_mask) - 1) >= amd_ctx->max_sdw_links) {
|
||||
dev_err(dev, "link_id %d exceeds max_sdw_links %d\n",
|
||||
ffs(sof_end->link_mask) - 1, amd_ctx->max_sdw_links);
|
||||
return -EINVAL;
|
||||
}
|
||||
switch (amd_ctx->acp_rev) {
|
||||
case ACP63_PCI_REV:
|
||||
ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask - 1),
|
||||
ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask) - 1,
|
||||
*be_id, &cpu_pin_id, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -131,7 +140,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
|
|||
case ACP70_PCI_REV:
|
||||
case ACP71_PCI_REV:
|
||||
case ACP72_PCI_REV:
|
||||
ret = get_acp70_cpu_pin_id(ffs(sof_end->link_mask - 1),
|
||||
ret = get_acp70_cpu_pin_id(ffs(sof_end->link_mask) - 1,
|
||||
*be_id, &cpu_pin_id, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -277,6 +286,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
|
|||
int num_devs = 0;
|
||||
int num_ends = 0;
|
||||
int num_aux = 0;
|
||||
int num_confs;
|
||||
int num_links;
|
||||
int be_id = 0;
|
||||
int ret;
|
||||
|
|
@ -287,6 +297,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
|
|||
return ret;
|
||||
}
|
||||
|
||||
num_confs = num_ends;
|
||||
/* One per DAI link, worst case is a DAI link for every endpoint */
|
||||
struct asoc_sdw_dailink *sof_dais __free(kfree) =
|
||||
kzalloc_objs(*sof_dais, num_ends);
|
||||
|
|
@ -303,7 +314,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
|
|||
if (!sof_aux)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs);
|
||||
ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -315,7 +326,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
|
|||
|
||||
dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);
|
||||
|
||||
codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL);
|
||||
codec_conf = devm_kcalloc(dev, num_confs, sizeof(*codec_conf), GFP_KERNEL);
|
||||
if (!codec_conf)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -326,7 +337,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
|
|||
return -ENOMEM;
|
||||
|
||||
card->codec_conf = codec_conf;
|
||||
card->num_configs = num_devs;
|
||||
card->num_configs = num_confs;
|
||||
card->dai_link = dai_links;
|
||||
card->num_links = num_links;
|
||||
card->aux_dev = sof_aux;
|
||||
|
|
@ -379,7 +390,7 @@ static int mc_probe(struct platform_device *pdev)
|
|||
ctx->private = amd_ctx;
|
||||
card = &ctx->card;
|
||||
card->dev = &pdev->dev;
|
||||
card->name = "amd-soundwire";
|
||||
card->name = "amd-sdw";
|
||||
card->owner = THIS_MODULE;
|
||||
card->late_probe = asoc_sdw_card_late_probe;
|
||||
|
||||
|
|
|
|||
|
|
@ -748,7 +748,27 @@ static const struct snd_soc_acpi_link_adr acp70_rt721_only[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const struct snd_soc_acpi_link_adr acp70_tas2783a_l0_rt712_vb_l1[] = {
|
||||
{
|
||||
.mask = BIT(0),
|
||||
.num_adr = ARRAY_SIZE(tas2783_2_adr),
|
||||
.adr_d = tas2783_2_adr,
|
||||
},
|
||||
{
|
||||
.mask = BIT(1),
|
||||
.num_adr = ARRAY_SIZE(rt712_vb_1_group1_adr),
|
||||
.adr_d = rt712_vb_1_group1_adr,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = {
|
||||
{
|
||||
.link_mask = BIT(0) | BIT(1),
|
||||
.links = acp70_tas2783a_l0_rt712_vb_l1,
|
||||
.machine_check = snd_soc_acpi_amd_sdca_is_device_rt712_vb,
|
||||
.drv_name = "amd_sdw",
|
||||
},
|
||||
{
|
||||
.link_mask = BIT(0),
|
||||
.links = acp70_tas2783_2,
|
||||
|
|
|
|||
|
|
@ -248,6 +248,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
|
|||
DMI_MATCH(DMI_PRODUCT_NAME, "21J6"),
|
||||
}
|
||||
},
|
||||
{
|
||||
.driver_data = &acp6x_card,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "21JN"),
|
||||
}
|
||||
},
|
||||
{
|
||||
.driver_data = &acp6x_card,
|
||||
.matches = {
|
||||
|
|
|
|||
|
|
@ -527,13 +527,13 @@ config SND_SOC_ADAU1977
|
|||
tristate
|
||||
|
||||
config SND_SOC_ADAU1977_SPI
|
||||
tristate
|
||||
tristate "Analog Devices ADAU1977/ADAU1978/ADAU1979 CODEC - SPI"
|
||||
depends on SPI_MASTER
|
||||
select SND_SOC_ADAU1977
|
||||
select REGMAP_SPI
|
||||
|
||||
config SND_SOC_ADAU1977_I2C
|
||||
tristate
|
||||
tristate "Analog Devices ADAU1977/ADAU1978/ADAU1979 CODEC - I2C"
|
||||
depends on I2C
|
||||
select SND_SOC_ADAU1977
|
||||
select REGMAP_I2C
|
||||
|
|
@ -2005,9 +2005,7 @@ config SND_SOC_RT715_SDCA_SDW
|
|||
config SND_SOC_RT766_SDCA_SDW
|
||||
tristate "Realtek RT766 SDCA Codec - SDW"
|
||||
depends on SOUNDWIRE
|
||||
depends on SND_SOC_SDCA
|
||||
select SND_SOC_SDCA_HID
|
||||
select SND_SOC_SDCA_IRQ
|
||||
depends on SND_SOC_SDCA_CLASS
|
||||
select REGMAP_SOUNDWIRE
|
||||
select REGMAP_SOUNDWIRE_MBQ
|
||||
|
||||
|
|
|
|||
|
|
@ -34,9 +34,18 @@ static const struct i2c_device_id adau1977_i2c_ids[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids);
|
||||
|
||||
static const struct of_device_id adau1977_i2c_of_match[] = {
|
||||
{ .compatible = "adi,adau1977" },
|
||||
{ .compatible = "adi,adau1978" },
|
||||
{ .compatible = "adi,adau1979" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adau1977_i2c_of_match);
|
||||
|
||||
static struct i2c_driver adau1977_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "adau1977",
|
||||
.of_match_table = adau1977_i2c_of_match,
|
||||
},
|
||||
.probe = adau1977_i2c_probe,
|
||||
.id_table = adau1977_i2c_ids,
|
||||
|
|
|
|||
|
|
@ -53,18 +53,18 @@ static const struct spi_device_id adau1977_spi_ids[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(spi, adau1977_spi_ids);
|
||||
|
||||
static const struct of_device_id adau1977_spi_of_match[] __maybe_unused = {
|
||||
{ .compatible = "adi,adau1977" },
|
||||
{ .compatible = "adi,adau1978" },
|
||||
{ .compatible = "adi,adau1979" },
|
||||
{ },
|
||||
static const struct of_device_id adau1977_spi_of_match[] = {
|
||||
{ .compatible = "adi,adau1977" },
|
||||
{ .compatible = "adi,adau1978" },
|
||||
{ .compatible = "adi,adau1979" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adau1977_spi_of_match);
|
||||
|
||||
static struct spi_driver adau1977_spi_driver = {
|
||||
.driver = {
|
||||
.name = "adau1977",
|
||||
.of_match_table = of_match_ptr(adau1977_spi_of_match),
|
||||
.of_match_table = adau1977_spi_of_match,
|
||||
},
|
||||
.probe = adau1977_spi_probe,
|
||||
.id_table = adau1977_spi_ids,
|
||||
|
|
|
|||
|
|
@ -317,6 +317,8 @@ static void *cs_amp_alloc_get_efi_variable(efi_char16_t *name,
|
|||
unsigned long size = 0;
|
||||
|
||||
status = cs_amp_get_efi_variable(name, guid, NULL, &size, NULL);
|
||||
if (status == EFI_SUCCESS)
|
||||
return ERR_PTR(-ENOENT);
|
||||
if (status != EFI_BUFFER_TOO_SMALL)
|
||||
return ERR_PTR(cs_amp_convert_efi_status(status));
|
||||
|
||||
|
|
|
|||
|
|
@ -426,10 +426,14 @@ static int hdmi_codec_iec958_default_put(struct snd_kcontrol *kcontrol,
|
|||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (!memcmp(hcp->iec_status, ucontrol->value.iec958.status,
|
||||
sizeof(hcp->iec_status)))
|
||||
return 0;
|
||||
|
||||
memcpy(hcp->iec_status, ucontrol->value.iec958.status,
|
||||
sizeof(hcp->iec_status));
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int hdmi_codec_iec958_mask_get(struct snd_kcontrol *kcontrol,
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ static int max98363_sdw_dai_hw_params(struct snd_pcm_substream *substream,
|
|||
struct max98363_priv *max98363 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
struct sdw_stream_config stream_config;
|
||||
struct sdw_stream_config stream_config = {0};
|
||||
struct sdw_port_config port_config;
|
||||
enum sdw_data_direction direction;
|
||||
struct sdw_stream_runtime *stream;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/sdw.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
|
|
@ -573,11 +574,10 @@ static int rt1017_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
|
||||
struct sdw_stream_config stream_config;
|
||||
struct sdw_stream_config stream_config = {0};
|
||||
struct sdw_port_config port_config;
|
||||
enum sdw_data_direction direction;
|
||||
struct sdw_stream_runtime *sdw_stream;
|
||||
int retval, port, num_channels, ch_mask;
|
||||
int retval, port;
|
||||
unsigned int sampling_rate;
|
||||
|
||||
dev_dbg(dai->dev, "%s %s", __func__, dai->name);
|
||||
|
|
@ -591,28 +591,17 @@ static int rt1017_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
/* SoundWire specific configuration */
|
||||
/* port 1 for playback */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
direction = SDW_DATA_DIR_RX;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
port = 1;
|
||||
} else {
|
||||
direction = SDW_DATA_DIR_TX;
|
||||
else
|
||||
port = 2;
|
||||
}
|
||||
|
||||
num_channels = params_channels(params);
|
||||
ch_mask = (1 << num_channels) - 1;
|
||||
|
||||
stream_config.frame_rate = params_rate(params);
|
||||
stream_config.ch_count = num_channels;
|
||||
stream_config.bps = snd_pcm_format_width(params_format(params));
|
||||
stream_config.direction = direction;
|
||||
|
||||
port_config.ch_mask = ch_mask;
|
||||
snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
|
||||
port_config.num = port;
|
||||
|
||||
dev_dbg(dai->dev, "frame_rate %d, ch_count %d, bps %d, direction %d, ch_mask %d, port: %d\n",
|
||||
params_rate(params), num_channels, snd_pcm_format_width(params_format(params)),
|
||||
direction, ch_mask, port);
|
||||
stream_config.frame_rate, stream_config.ch_count, stream_config.bps,
|
||||
stream_config.direction, port_config.ch_mask, port);
|
||||
|
||||
retval = sdw_stream_add_slave(rt1017->sdw_slave, &stream_config,
|
||||
&port_config, 1, sdw_stream);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/sdw.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "rt712-sdca.h"
|
||||
#include "rt712-sdca-dmic.h"
|
||||
|
|
@ -632,10 +633,10 @@ static int rt712_sdca_dmic_hw_params(struct snd_pcm_substream *substream,
|
|||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
|
||||
struct sdw_stream_config stream_config;
|
||||
struct sdw_stream_config stream_config = {0};
|
||||
struct sdw_port_config port_config;
|
||||
struct sdw_stream_runtime *sdw_stream;
|
||||
int retval, num_channels;
|
||||
int retval;
|
||||
unsigned int sampling_rate;
|
||||
|
||||
dev_dbg(dai->dev, "%s %s", __func__, dai->name);
|
||||
|
|
@ -647,13 +648,8 @@ static int rt712_sdca_dmic_hw_params(struct snd_pcm_substream *substream,
|
|||
if (!rt712->slave)
|
||||
return -EINVAL;
|
||||
|
||||
stream_config.frame_rate = params_rate(params);
|
||||
stream_config.ch_count = params_channels(params);
|
||||
stream_config.bps = snd_pcm_format_width(params_format(params));
|
||||
stream_config.direction = SDW_DATA_DIR_TX;
|
||||
|
||||
num_channels = params_channels(params);
|
||||
port_config.ch_mask = GENMASK(num_channels - 1, 0);
|
||||
/* SoundWire specific configuration */
|
||||
snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
|
||||
port_config.num = 2;
|
||||
|
||||
retval = sdw_stream_add_slave(rt712->slave, &stream_config,
|
||||
|
|
|
|||
|
|
@ -18,12 +18,16 @@
|
|||
static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x004d:
|
||||
case 0x201a ... 0x201f:
|
||||
case 0x2029 ... 0x202a:
|
||||
case 0x202d ... 0x2034:
|
||||
case 0x2230 ... 0x2232:
|
||||
case 0x2f01 ... 0x2f0a:
|
||||
case 0x2f35 ... 0x2f36:
|
||||
case 0x2f3a:
|
||||
case 0x2f3d:
|
||||
case 0x2f41:
|
||||
case 0x2f50:
|
||||
case 0x2f54:
|
||||
case 0x2f58 ... 0x2f5d:
|
||||
|
|
@ -48,6 +52,7 @@ static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg)
|
|||
static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x004d:
|
||||
case 0x201b:
|
||||
case 0x201c:
|
||||
case 0x201d:
|
||||
|
|
@ -56,6 +61,9 @@ static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg)
|
|||
case 0x2230:
|
||||
case 0x2f01:
|
||||
case 0x2f35:
|
||||
case 0x2f3a:
|
||||
case 0x2f3d:
|
||||
case 0x2f41:
|
||||
case 0x320c:
|
||||
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0):
|
||||
case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
|
||||
|
|
|
|||
|
|
@ -73,14 +73,57 @@ static int rt712_sdca_index_update_bits(struct rt712_sdca_priv *rt712,
|
|||
return rt712_sdca_index_write(rt712, nid, reg, tmp);
|
||||
}
|
||||
|
||||
static void rt712_sdca_clk_patch(struct rt712_sdca_priv *rt712)
|
||||
{
|
||||
rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000);
|
||||
regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03);
|
||||
usleep_range(1000, 1100);
|
||||
regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x02);
|
||||
usleep_range(1000, 1100);
|
||||
regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x0080, 0x0000);
|
||||
regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x001f, 0x0017);
|
||||
regmap_update_bits(rt712->regmap, RT712_PLL2_CONF3, 0x0010, 0x0000);
|
||||
regmap_update_bits(rt712->regmap, RT712_PLL2_CONF1, 0x0081, 0x0001);
|
||||
regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03);
|
||||
usleep_range(1000, 1100);
|
||||
regmap_update_bits(rt712->regmap, RT712_PLL2_CONF1, 0x0081, 0x0081);
|
||||
regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x0080, 0x0080);
|
||||
regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x001f, 0x0000);
|
||||
regmap_update_bits(rt712->regmap, RT712_PLL2_CONF3, 0x0010, 0x0010);
|
||||
usleep_range(1000, 1100);
|
||||
rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0081);
|
||||
}
|
||||
|
||||
static void rt712_sdca_clk_patch2(struct rt712_sdca_priv *rt712)
|
||||
{
|
||||
rt712_sdca_index_update_bits(rt712, RT712_VENDOR_REG, 0x49, 0x0800,
|
||||
0x0000);
|
||||
rt712_sdca_index_update_bits(rt712, RT712_VENDOR_REG, 0x49, 0xf000,
|
||||
0x0000);
|
||||
rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000);
|
||||
rt712_sdca_index_update_bits(rt712, RT712_VENDOR_ANALOG_CTL, 0x0c, 0xc000,
|
||||
0xc000);
|
||||
rt712_sdca_index_update_bits(rt712, RT712_VENDOR_ANALOG_CTL, 0x00, 0xc000,
|
||||
0xc000);
|
||||
rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0081);
|
||||
regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x02);
|
||||
usleep_range(1000, 1100);
|
||||
regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03);
|
||||
usleep_range(1000, 1100);
|
||||
rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000);
|
||||
}
|
||||
|
||||
static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712)
|
||||
{
|
||||
unsigned int val, loop_rc = 0, loop_dc = 0;
|
||||
struct device *dev;
|
||||
struct regmap *regmap = rt712->regmap;
|
||||
unsigned int clk_base;
|
||||
int chk_cnt = 100;
|
||||
int ret = 0;
|
||||
|
||||
regmap_read(rt712->regmap, RT712_SDW_ROOT_CLK, &clk_base);
|
||||
|
||||
mutex_lock(&rt712->calibrate_mutex);
|
||||
dev = regmap_get_device(regmap);
|
||||
|
||||
|
|
@ -109,8 +152,35 @@ static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712)
|
|||
if (ret < 0)
|
||||
goto _cali_fail_;
|
||||
}
|
||||
if (loop_dc == chk_cnt)
|
||||
dev_err(dev, "%s, calibration time-out!\n", __func__);
|
||||
|
||||
if (loop_dc == chk_cnt) {
|
||||
if (clk_base == RT712_CLK_FREQ_24_576MHZ) {
|
||||
rt712_sdca_clk_patch(rt712);
|
||||
rt712_sdca_clk_patch2(rt712);
|
||||
}
|
||||
rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4100);
|
||||
rt712_sdca_index_write(rt712, RT712_VENDOR_CALI,
|
||||
RT712_DAC_DC_CALI_CTL1, 0x7883);
|
||||
rt712_sdca_index_write(rt712, RT712_VENDOR_CALI,
|
||||
RT712_DAC_DC_CALI_CTL1, 0xf893);
|
||||
rt712_sdca_index_read(rt712, RT712_VENDOR_CALI,
|
||||
RT712_DAC_DC_CALI_CTL1, &val);
|
||||
|
||||
for (loop_dc = 0; loop_dc < chk_cnt &&
|
||||
(val & RT712_DAC_DC_CALI_TRIGGER); loop_dc++) {
|
||||
usleep_range(10000, 11000);
|
||||
ret = rt712_sdca_index_read(rt712, RT712_VENDOR_CALI,
|
||||
RT712_DAC_DC_CALI_CTL1, &val);
|
||||
|
||||
if (ret < 0)
|
||||
goto _cali_fail_;
|
||||
}
|
||||
|
||||
if (loop_dc == chk_cnt)
|
||||
dev_err(dev, "%s, calibration time-out!\n", __func__);
|
||||
else
|
||||
dev_dbg(dev, "%s, calibration success!\n", __func__);
|
||||
}
|
||||
|
||||
if (loop_dc == chk_cnt || loop_rc == chk_cnt)
|
||||
ret = -ETIMEDOUT;
|
||||
|
|
@ -1759,9 +1829,13 @@ static void rt712_sdca_va_io_init(struct rt712_sdca_priv *rt712)
|
|||
|
||||
static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int jack_func_status, mic_func_status, amp_func_status;
|
||||
struct device *dev = &rt712->slave->dev;
|
||||
unsigned int clk_base;
|
||||
int ret = 0;
|
||||
|
||||
regmap_read(rt712->regmap, RT712_SDW_ROOT_CLK, &clk_base);
|
||||
dev_dbg(dev, "%s clk_base=%x", __func__, clk_base);
|
||||
|
||||
regmap_read(rt712->regmap,
|
||||
SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &jack_func_status);
|
||||
|
|
@ -1773,6 +1847,12 @@ static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712)
|
|||
__func__, jack_func_status, mic_func_status, amp_func_status);
|
||||
|
||||
rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_JD_CTL3, 0x7778);
|
||||
|
||||
if (clk_base == RT712_CLK_FREQ_24_576MHZ) {
|
||||
rt712_sdca_clk_patch(rt712);
|
||||
rt712_sdca_clk_patch2(rt712);
|
||||
}
|
||||
|
||||
/* DMIC */
|
||||
if ((mic_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) {
|
||||
rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_DMIC2_FU_IT_FLOAT_CTL, 0x1526);
|
||||
|
|
|
|||
|
|
@ -162,6 +162,16 @@ struct rt712_dmic_kctrl_priv {
|
|||
#define RT712_EAPD_HIGH 0x2
|
||||
#define RT712_EAPD_LOW 0x0
|
||||
|
||||
/* SDW clock root frequency */
|
||||
#define RT712_SDW_ROOT_CLK 0x004d
|
||||
#define RT712_SDW_SCALE_CLK0 0x0062
|
||||
#define RT712_SDW_SCALE_CLK1 0x0072
|
||||
|
||||
/* PLL2 config */
|
||||
#define RT712_PLL2_CONF1 0x2f3a
|
||||
#define RT712_PLL2_CONF2 0x2f3d
|
||||
#define RT712_PLL2_CONF3 0x2f41
|
||||
|
||||
/* RC Calibration register */
|
||||
#define RT712_RC_CAL 0x3201
|
||||
|
||||
|
|
@ -254,6 +264,14 @@ enum rt712_sdca_version {
|
|||
RT712_VB,
|
||||
};
|
||||
|
||||
enum {
|
||||
RT712_CLK_FREQ_19_2_MHZ = 1,
|
||||
RT712_CLK_FREQ_24MHZ = 2,
|
||||
RT712_CLK_FREQ_24_576MHZ = 3,
|
||||
RT712_CLK_FREQ_22_5792MHZ = 4,
|
||||
};
|
||||
|
||||
|
||||
int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave);
|
||||
int rt712_sdca_init(struct device *dev, struct regmap *regmap,
|
||||
struct regmap *mbq_regmap, struct sdw_slave *slave);
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int re
|
|||
case 0x0310100:
|
||||
case 0x2000000 ... 0x2000003:
|
||||
case 0x2000013:
|
||||
case 0x2000026:
|
||||
case 0x200002c:
|
||||
case 0x200003c:
|
||||
case 0x2000046:
|
||||
|
|
@ -154,6 +155,7 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re
|
|||
case 0x200000d:
|
||||
case 0x2000019:
|
||||
case 0x2000020:
|
||||
case 0x2000026:
|
||||
case 0x200002c:
|
||||
case 0x2000030:
|
||||
case 0x2000046:
|
||||
|
|
@ -167,6 +169,7 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re
|
|||
case 0x5810039:
|
||||
case 0x5b10018:
|
||||
case 0x5b10019:
|
||||
case 0x6100006:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1538,6 +1538,15 @@ int rt721_sdca_init(struct device *dev, struct regmap *regmap,
|
|||
&soc_sdca_dev_rt721, rt721_sdca_dai, ARRAY_SIZE(rt721_sdca_dai));
|
||||
}
|
||||
|
||||
static void rt721_sdca_reset(struct rt721_sdca_priv *rt721)
|
||||
{
|
||||
rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_VENDOR_REG,
|
||||
RT721_VD_HIDDEN_CTRL, RT721_HIDDEN_REG_SW_RESET,
|
||||
RT721_HIDDEN_REG_SW_RESET);
|
||||
rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
|
||||
RT721_HDA_LEGACY_RESET_CTL, 0x1, 0x1);
|
||||
}
|
||||
|
||||
int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave)
|
||||
{
|
||||
struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev);
|
||||
|
|
@ -1571,9 +1580,17 @@ int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave)
|
|||
}
|
||||
|
||||
pm_runtime_get_noresume(&slave->dev);
|
||||
|
||||
if (!rt721->first_hw_init)
|
||||
rt721_sdca_reset(rt721);
|
||||
|
||||
rt721_sdca_dmic_preset(rt721);
|
||||
rt721_sdca_amp_preset(rt721);
|
||||
rt721_sdca_jack_preset(rt721);
|
||||
|
||||
if (rt721->hs_jack && (!rt721->first_hw_init))
|
||||
rt721_sdca_jack_init(rt721);
|
||||
|
||||
if (rt721->first_hw_init) {
|
||||
regcache_cache_bypass(rt721->regmap, false);
|
||||
regcache_mark_dirty(rt721->regmap);
|
||||
|
|
|
|||
|
|
@ -775,9 +775,12 @@ static int wm_adsp_request_firmware_file(struct wm_adsp *dsp,
|
|||
s++;
|
||||
}
|
||||
|
||||
adsp_dbg(dsp, "Try '%s'\n", fw->filename);
|
||||
ret = wm_adsp_firmware_request(&fw->firmware, fw->filename, cs_dsp->dev);
|
||||
if (ret < 0) {
|
||||
adsp_dbg(dsp, "Failed to request '%s': %d\n", fw->filename, ret);
|
||||
if (ret != -ENOENT)
|
||||
adsp_dbg(dsp, "Failed to request '%s': %d\n", fw->filename, ret);
|
||||
|
||||
kfree(fw->filename);
|
||||
fw->filename = NULL;
|
||||
if (ret != -ENOENT)
|
||||
|
|
|
|||
|
|
@ -360,6 +360,15 @@ static const struct dmi_system_id sof_es8336_quirk_table[] = {
|
|||
.driver_data = (void *)(SOF_ES8336_HEADPHONE_GPIO |
|
||||
SOC_ES8336_HEADSET_MIC1)
|
||||
},
|
||||
{
|
||||
.callback = sof_es8336_quirk_cb,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "NDZ-WXX9"),
|
||||
},
|
||||
.driver_data = (void *)(SOF_ES8336_HEADPHONE_GPIO |
|
||||
SOC_ES8336_HEADSET_MIC1)
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,35 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
menu "Mediatek"
|
||||
|
||||
config SND_SOC_MEDIATEK
|
||||
tristate
|
||||
select REGMAP_MMIO
|
||||
|
||||
menu "Airoha"
|
||||
|
||||
config SND_SOC_AN7581
|
||||
tristate "ASoC support for Airoha AN7581 chip"
|
||||
depends on ARCH_AIROHA || COMPILE_TEST
|
||||
select SND_SOC_MEDIATEK
|
||||
help
|
||||
This adds ASoC platform driver support for Airoha AN7581 chip
|
||||
that can be used with other codecs.
|
||||
Select Y if you have such device.
|
||||
If unsure select "N".
|
||||
|
||||
config SND_SOC_AN7581_WM8960
|
||||
tristate "ASoc Audio driver for Airoha AN7581 with WM8960 codec"
|
||||
depends on SND_SOC_AN7581 && I2C
|
||||
select SND_SOC_WM8960
|
||||
help
|
||||
This adds support for ASoC machine driver for Airoha AN7581
|
||||
boards with the WM8960 codecs.
|
||||
Select Y if you have such device.
|
||||
If unsure select "N".
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Mediatek"
|
||||
|
||||
config SND_SOC_MT2701
|
||||
tristate "ASoC support for Mediatek MT2701 chip"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_SND_SOC_MEDIATEK) += common/
|
||||
obj-$(CONFIG_SND_SOC_AN7581) += an7581/
|
||||
obj-$(CONFIG_SND_SOC_MT2701) += mt2701/
|
||||
obj-$(CONFIG_SND_SOC_MT6797) += mt6797/
|
||||
obj-$(CONFIG_SND_SOC_MT7986) += mt7986/
|
||||
|
|
|
|||
9
sound/soc/mediatek/an7581/Makefile
Normal file
9
sound/soc/mediatek/an7581/Makefile
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# platform driver
|
||||
snd-soc-an7581-afe-y := \
|
||||
an7581-afe-pcm.o \
|
||||
an7581-dai-etdm.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_AN7581) += snd-soc-an7581-afe.o
|
||||
obj-$(CONFIG_SND_SOC_AN7581_WM8960) += an7581-wm8960.o
|
||||
49
sound/soc/mediatek/an7581/an7581-afe-common.h
Normal file
49
sound/soc/mediatek/an7581/an7581-afe-common.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* an7581-afe-common.h -- Airoha AN7581 audio driver definitions
|
||||
*/
|
||||
|
||||
#ifndef _AN7581_AFE_COMMON_H_
|
||||
#define _AN7581_AFE_COMMON_H_
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "../../mediatek/common/mtk-base-afe.h"
|
||||
|
||||
enum {
|
||||
AN7581_MEMIF_DL1,
|
||||
AN7581_MEMIF_UL1,
|
||||
AN7581_MEMIF_NUM,
|
||||
AN7581_DAI_ETDM = AN7581_MEMIF_NUM,
|
||||
AN7581_DAI_NUM,
|
||||
};
|
||||
|
||||
enum {
|
||||
AN7581_IRQ_0,
|
||||
AN7581_IRQ_1,
|
||||
AN7581_IRQ_NUM,
|
||||
};
|
||||
|
||||
struct an7581_memif_irq_desc {
|
||||
u8 irq;
|
||||
u32 status_bit;
|
||||
u32 clear_reg;
|
||||
};
|
||||
|
||||
struct an7581_afe_private {
|
||||
unsigned int users;
|
||||
/* protect concurrent ETDM user tracking */
|
||||
struct mutex user_lock;
|
||||
|
||||
void *dai_priv[AN7581_DAI_NUM];
|
||||
};
|
||||
|
||||
unsigned int an7581_afe_rate_transform(struct device *dev,
|
||||
unsigned int rate);
|
||||
|
||||
int an7581_dai_etdm_register(struct mtk_base_afe *afe);
|
||||
|
||||
#endif
|
||||
515
sound/soc/mediatek/an7581/an7581-afe-pcm.c
Normal file
515
sound/soc/mediatek/an7581/an7581-afe-pcm.c
Normal file
|
|
@ -0,0 +1,515 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Airoha ALSA SoC AFE platform driver for AN7581
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include "an7581-afe-common.h"
|
||||
#include "an7581-reg.h"
|
||||
#include "../common/mtk-afe-platform-driver.h"
|
||||
#include "../common/mtk-afe-fe-dai.h"
|
||||
|
||||
enum {
|
||||
ARH_AFE_RATE_8K = 0,
|
||||
ARH_AFE_RATE_12K = 1,
|
||||
ARH_AFE_RATE_16K = 2,
|
||||
ARH_AFE_RATE_24K = 3,
|
||||
ARH_AFE_RATE_32K = 4,
|
||||
ARH_AFE_RATE_48K = 5,
|
||||
ARH_AFE_RATE_96K = 6,
|
||||
ARH_AFE_RATE_192K = 7,
|
||||
ARH_AFE_RATE_384K = 8,
|
||||
ARH_AFE_RATE_7K = 16,
|
||||
ARH_AFE_RATE_11K = 17,
|
||||
ARH_AFE_RATE_14K = 18,
|
||||
ARH_AFE_RATE_22K = 19,
|
||||
ARH_AFE_RATE_29K = 20,
|
||||
ARH_AFE_RATE_44K = 21,
|
||||
ARH_AFE_RATE_88K = 22,
|
||||
ARH_AFE_RATE_176K = 23,
|
||||
ARH_AFE_RATE_352K = 24,
|
||||
};
|
||||
|
||||
unsigned int an7581_afe_rate_transform(struct device *dev, unsigned int rate)
|
||||
{
|
||||
switch (rate) {
|
||||
case 7350:
|
||||
return ARH_AFE_RATE_7K;
|
||||
case 8000:
|
||||
return ARH_AFE_RATE_8K;
|
||||
case 11025:
|
||||
return ARH_AFE_RATE_11K;
|
||||
case 12000:
|
||||
return ARH_AFE_RATE_12K;
|
||||
case 14700:
|
||||
return ARH_AFE_RATE_14K;
|
||||
case 16000:
|
||||
return ARH_AFE_RATE_16K;
|
||||
case 22050:
|
||||
return ARH_AFE_RATE_22K;
|
||||
case 24000:
|
||||
return ARH_AFE_RATE_24K;
|
||||
case 29400:
|
||||
return ARH_AFE_RATE_29K;
|
||||
case 32000:
|
||||
return ARH_AFE_RATE_32K;
|
||||
case 44100:
|
||||
return ARH_AFE_RATE_44K;
|
||||
case 48000:
|
||||
return ARH_AFE_RATE_48K;
|
||||
case 88200:
|
||||
return ARH_AFE_RATE_88K;
|
||||
case 96000:
|
||||
return ARH_AFE_RATE_96K;
|
||||
case 176400:
|
||||
return ARH_AFE_RATE_176K;
|
||||
case 192000:
|
||||
return ARH_AFE_RATE_192K;
|
||||
case 352800:
|
||||
return ARH_AFE_RATE_352K;
|
||||
case 384000:
|
||||
return ARH_AFE_RATE_384K;
|
||||
default:
|
||||
dev_warn_ratelimited(dev, "%s(), rate %u invalid, using %d!\n",
|
||||
__func__, rate, ARH_AFE_RATE_48K);
|
||||
return ARH_AFE_RATE_48K;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct an7581_memif_irq_desc an7581_memif_irq_descs[AN7581_MEMIF_NUM] = {
|
||||
[AN7581_MEMIF_DL1] = {
|
||||
.irq = AN7581_IRQ_0,
|
||||
.status_bit = AFE_IRQ_STS_PLAY,
|
||||
.clear_reg = AFE_IRQ_CON0,
|
||||
},
|
||||
[AN7581_MEMIF_UL1] = {
|
||||
.irq = AN7581_IRQ_1,
|
||||
.status_bit = AFE_IRQ_STS_RECORD,
|
||||
.clear_reg = AFE_IRQ1_CON0,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hardware an7581_afe_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_MMAP_VALID,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.period_bytes_min = 512,
|
||||
.period_bytes_max = 128 * 1024,
|
||||
.periods_min = 2,
|
||||
.periods_max = 256,
|
||||
.buffer_bytes_max = 256 * 1024,
|
||||
};
|
||||
|
||||
static int an7581_memif_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
|
||||
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
|
||||
|
||||
return an7581_afe_rate_transform(afe->dev, rate);
|
||||
}
|
||||
|
||||
static int an7581_afe_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
|
||||
int ret;
|
||||
|
||||
ret = mtk_afe_fe_startup(substream, dai);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
ret = snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
0x2000, UINT_MAX);
|
||||
if (ret < 0)
|
||||
dev_err(afe->dev, "hw_constraint_minmax failed\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct snd_soc_dai_ops an7581_afe_fe_ops = {
|
||||
.startup = an7581_afe_fe_startup,
|
||||
.shutdown = mtk_afe_fe_shutdown,
|
||||
.hw_params = mtk_afe_fe_hw_params,
|
||||
.hw_free = mtk_afe_fe_hw_free,
|
||||
.prepare = mtk_afe_fe_prepare,
|
||||
.trigger = mtk_afe_fe_trigger,
|
||||
};
|
||||
|
||||
#define ARH_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
static struct snd_soc_dai_driver an7581_memif_dai_driver[] = {
|
||||
/* FE DAIs: memory intefaces to CPU */
|
||||
{
|
||||
.name = "DL1",
|
||||
.id = AN7581_MEMIF_DL1,
|
||||
.playback = {
|
||||
.stream_name = "DL1",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = ARH_PCM_FORMATS,
|
||||
},
|
||||
.ops = &an7581_afe_fe_ops,
|
||||
},
|
||||
{
|
||||
.name = "UL1",
|
||||
.id = AN7581_MEMIF_UL1,
|
||||
.capture = {
|
||||
.stream_name = "UL1",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = ARH_PCM_FORMATS,
|
||||
},
|
||||
.ops = &an7581_afe_fe_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget an7581_memif_widgets[] = {
|
||||
/* DL */
|
||||
SND_SOC_DAPM_MIXER("I032", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_MIXER("I033", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
|
||||
/* UL */
|
||||
SND_SOC_DAPM_MIXER("O018", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_MIXER("O019", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route an7581_memif_routes[] = {
|
||||
{"I032", NULL, "DL1"},
|
||||
{"I033", NULL, "DL1"},
|
||||
{"UL1", NULL, "O018"},
|
||||
{"UL1", NULL, "O019"},
|
||||
{"O018", NULL, "I150"},
|
||||
{"O019", NULL, "I151"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver an7581_afe_pcm_dai_component = {
|
||||
.name = "an7581-afe-pcm-dai",
|
||||
};
|
||||
|
||||
static const struct mtk_base_memif_data memif_data[AN7581_MEMIF_NUM] = {
|
||||
[AN7581_MEMIF_DL1] = {
|
||||
.name = "DL1",
|
||||
.id = AN7581_MEMIF_DL1,
|
||||
.reg_ofs_base = AFE_DL1_BASE,
|
||||
.reg_ofs_cur = AFE_DL1_CUR,
|
||||
.reg_ofs_end = AFE_DL1_END,
|
||||
.fs_reg = -1,
|
||||
.fs_shift = -1,
|
||||
.fs_maskbit = -1,
|
||||
.mono_reg = -1,
|
||||
.mono_shift = -1,
|
||||
.hd_reg = AFE_DL1_CON0,
|
||||
.hd_shift = AFE_HD_SHIFT,
|
||||
.hd_align_reg = -1,
|
||||
.hd_align_mshift = -1,
|
||||
.enable_reg = AFE_DAC_CON0,
|
||||
.enable_shift = AFE_DL1_ENABLE_SHIFT,
|
||||
.msb_reg = -1,
|
||||
.msb_shift = -1,
|
||||
.agent_disable_reg = -1,
|
||||
.agent_disable_shift = -1,
|
||||
},
|
||||
[AN7581_MEMIF_UL1] = {
|
||||
.name = "UL1",
|
||||
.id = AN7581_MEMIF_UL1,
|
||||
.reg_ofs_base = AFE_UL1_BASE,
|
||||
.reg_ofs_cur = AFE_UL1_CUR,
|
||||
.reg_ofs_end = AFE_UL1_END,
|
||||
.fs_reg = -1,
|
||||
.fs_shift = -1,
|
||||
.fs_maskbit = -1,
|
||||
.mono_reg = -1,
|
||||
.mono_shift = -1,
|
||||
.hd_reg = AFE_UL1_CON0,
|
||||
.hd_shift = AFE_HD_SHIFT,
|
||||
.hd_align_reg = AFE_UL1_CON0,
|
||||
.hd_align_mshift = AFE_HD_ALIGN_SHIFT,
|
||||
.enable_reg = AFE_DAC_CON0,
|
||||
.enable_shift = AFE_UL1_ENABLE_SHIFT,
|
||||
.msb_reg = -1,
|
||||
.msb_shift = -1,
|
||||
.agent_disable_reg = -1,
|
||||
.agent_disable_shift = -1,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct mtk_base_irq_data irq_data[AN7581_IRQ_NUM] = {
|
||||
[AN7581_IRQ_0] = {
|
||||
.id = AN7581_IRQ_0,
|
||||
.irq_cnt_reg = AFE_IRQ_CNT,
|
||||
.irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
|
||||
.irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
|
||||
.irq_en_reg = AFE_IRQ_CON0,
|
||||
.irq_en_shift = AFE_IRQ_ON_SHIFT,
|
||||
.irq_fs_reg = -1,
|
||||
.irq_fs_shift = -1,
|
||||
.irq_fs_maskbit = -1,
|
||||
.irq_clr_reg = -1,
|
||||
.irq_clr_shift = -1,
|
||||
},
|
||||
[AN7581_IRQ_1] = {
|
||||
.id = AN7581_IRQ_1,
|
||||
.irq_cnt_reg = AFE_IRQ1_CNT,
|
||||
.irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
|
||||
.irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
|
||||
.irq_en_reg = AFE_IRQ1_CON0,
|
||||
.irq_en_shift = AFE_IRQ_ON_SHIFT,
|
||||
.irq_fs_reg = -1,
|
||||
.irq_fs_shift = -1,
|
||||
.irq_fs_maskbit = -1,
|
||||
.irq_clr_reg = -1,
|
||||
.irq_clr_shift = -1,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_config an7581_afe_regmap_config = {
|
||||
.name = "afe",
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = AFE_MAX_REGISTER,
|
||||
};
|
||||
|
||||
static const struct regmap_config an7581_afe_irq1_regmap_config = {
|
||||
.name = "afe_irq1",
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = AFE_IRQ1_MAX_REGISTER,
|
||||
};
|
||||
|
||||
static irqreturn_t an7581_afe_irq_handler(int irq_id, void *dev)
|
||||
{
|
||||
const struct an7581_memif_irq_desc *irq_desc;
|
||||
struct mtk_base_afe *afe = dev;
|
||||
u32 status = 0;
|
||||
int i;
|
||||
|
||||
regmap_read(afe->regmap, AFE_IRQ_STS, &status);
|
||||
|
||||
for (i = 0; i < AN7581_MEMIF_NUM; i++) {
|
||||
struct mtk_base_afe_memif *memif = &afe->memif[i];
|
||||
|
||||
if (!memif->substream)
|
||||
continue;
|
||||
|
||||
if (memif->irq_usage < 0)
|
||||
continue;
|
||||
|
||||
irq_desc = &an7581_memif_irq_descs[i];
|
||||
if (status & irq_desc->status_bit)
|
||||
snd_pcm_period_elapsed(memif->substream);
|
||||
}
|
||||
|
||||
for (i = 0; i < AN7581_MEMIF_NUM; i++) {
|
||||
struct regmap *irq_regmap;
|
||||
|
||||
irq_desc = &an7581_memif_irq_descs[i];
|
||||
if (!(status & irq_desc->status_bit))
|
||||
continue;
|
||||
|
||||
irq_regmap = afe->irqs[irq_desc->irq].regmap;
|
||||
regmap_set_bits(irq_regmap, irq_desc->clear_reg,
|
||||
BIT(AFE_IRQ_CLR_SHIFT));
|
||||
regmap_clear_bits(irq_regmap, irq_desc->clear_reg,
|
||||
BIT(AFE_IRQ_CLR_SHIFT));
|
||||
|
||||
regmap_set_bits(irq_regmap, irq_desc->clear_reg,
|
||||
BIT(AFE_IRQ_MISS_FLG_CLR_SHIFT));
|
||||
regmap_clear_bits(irq_regmap, irq_desc->clear_reg,
|
||||
BIT(AFE_IRQ_MISS_FLG_CLR_SHIFT));
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int an7581_dai_memif_register(struct mtk_base_afe *afe)
|
||||
{
|
||||
struct mtk_base_afe_dai *dai;
|
||||
|
||||
dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
|
||||
if (!dai)
|
||||
return -ENOMEM;
|
||||
|
||||
list_add(&dai->list, &afe->sub_dais);
|
||||
|
||||
dai->dai_drivers = an7581_memif_dai_driver;
|
||||
dai->num_dai_drivers = ARRAY_SIZE(an7581_memif_dai_driver);
|
||||
|
||||
dai->dapm_widgets = an7581_memif_widgets;
|
||||
dai->num_dapm_widgets = ARRAY_SIZE(an7581_memif_widgets);
|
||||
dai->dapm_routes = an7581_memif_routes;
|
||||
dai->num_dapm_routes = ARRAY_SIZE(an7581_memif_routes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int (*dai_register_cb)(struct mtk_base_afe *);
|
||||
static const dai_register_cb dai_register_cbs[] = {
|
||||
an7581_dai_etdm_register,
|
||||
an7581_dai_memif_register,
|
||||
};
|
||||
|
||||
static int an7581_afe_pcm_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct an7581_afe_private *afe_priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct reset_control *reset;
|
||||
struct mtk_base_afe *afe;
|
||||
int i, irq_id, ret;
|
||||
void *base;
|
||||
|
||||
afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
|
||||
if (!afe)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, afe);
|
||||
|
||||
afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv),
|
||||
GFP_KERNEL);
|
||||
if (!afe->platform_priv)
|
||||
return -ENOMEM;
|
||||
|
||||
afe->irqs_size = AN7581_IRQ_NUM;
|
||||
afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
|
||||
GFP_KERNEL);
|
||||
if (!afe->irqs)
|
||||
return -ENOMEM;
|
||||
|
||||
afe->memif_size = AN7581_MEMIF_NUM;
|
||||
afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
|
||||
GFP_KERNEL);
|
||||
if (!afe->memif)
|
||||
return -ENOMEM;
|
||||
|
||||
afe_priv = afe->platform_priv;
|
||||
mutex_init(&afe_priv->user_lock);
|
||||
afe->dev = &pdev->dev;
|
||||
|
||||
reset = devm_reset_control_get_exclusive(dev, NULL);
|
||||
if (IS_ERR(reset))
|
||||
return PTR_ERR(reset);
|
||||
|
||||
/* Global reset I2S */
|
||||
reset_control_assert(reset);
|
||||
usleep_range(10, 20);
|
||||
reset_control_deassert(reset);
|
||||
|
||||
afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(afe->base_addr))
|
||||
return PTR_ERR(afe->base_addr);
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 1);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
ret = devm_pm_runtime_enable(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
|
||||
&an7581_afe_regmap_config);
|
||||
if (IS_ERR(afe->regmap))
|
||||
return PTR_ERR(afe->regmap);
|
||||
|
||||
mutex_init(&afe->irq_alloc_lock);
|
||||
|
||||
/* irq initialize */
|
||||
for (i = 0; i < afe->irqs_size; i++)
|
||||
afe->irqs[i].irq_data = &irq_data[i];
|
||||
|
||||
afe->irqs[AN7581_IRQ_0].regmap = afe->regmap;
|
||||
afe->irqs[AN7581_IRQ_1].regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
&an7581_afe_irq1_regmap_config);
|
||||
if (IS_ERR(afe->irqs[AN7581_IRQ_1].regmap))
|
||||
return PTR_ERR(afe->irqs[AN7581_IRQ_1].regmap);
|
||||
|
||||
/* request irq */
|
||||
irq_id = platform_get_irq(pdev, 0);
|
||||
if (irq_id < 0)
|
||||
return irq_id;
|
||||
|
||||
/* init memif */
|
||||
for (i = 0; i < afe->memif_size; i++) {
|
||||
int sel_irq = an7581_memif_irq_descs[i].irq;
|
||||
|
||||
afe->memif[i].data = &memif_data[i];
|
||||
afe->memif[i].irq_usage = sel_irq;
|
||||
afe->memif[i].const_irq = 1;
|
||||
afe->irqs[sel_irq].irq_occupyed = true;
|
||||
}
|
||||
|
||||
/* init sub_dais */
|
||||
INIT_LIST_HEAD(&afe->sub_dais);
|
||||
|
||||
ret = devm_request_irq(dev, irq_id, an7581_afe_irq_handler,
|
||||
IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to request irq for asys-isr\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
|
||||
ret = dai_register_cbs[i](afe);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "DAI register failed, i: %d\n", i);
|
||||
}
|
||||
|
||||
/* init dai_driver and component_driver */
|
||||
ret = mtk_afe_combine_sub_dai(afe);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "mtk_afe_combine_sub_dai fail\n");
|
||||
|
||||
afe->mtk_afe_hardware = &an7581_afe_hardware;
|
||||
afe->memif_fs = an7581_memif_irq_fs;
|
||||
afe->irq_fs = an7581_memif_irq_fs;
|
||||
|
||||
/* register component */
|
||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||
&mtk_afe_pcm_platform,
|
||||
NULL, 0);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Cannot register AFE component\n");
|
||||
|
||||
ret = devm_snd_soc_register_component(afe->dev,
|
||||
&an7581_afe_pcm_dai_component,
|
||||
afe->dai_drivers,
|
||||
afe->num_dai_drivers);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Cannot register PCM DAI component\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id an7581_afe_pcm_dt_match[] = {
|
||||
{ .compatible = "airoha,an7581-afe" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, an7581_afe_pcm_dt_match);
|
||||
|
||||
static struct platform_driver an7581_afe_pcm_driver = {
|
||||
.driver = {
|
||||
.name = "an7581-audio",
|
||||
.of_match_table = an7581_afe_pcm_dt_match,
|
||||
},
|
||||
.probe = an7581_afe_pcm_dev_probe,
|
||||
};
|
||||
module_platform_driver(an7581_afe_pcm_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Airoha SoC AFE platform driver for ALSA AN7581");
|
||||
MODULE_LICENSE("GPL");
|
||||
433
sound/soc/mediatek/an7581/an7581-dai-etdm.c
Normal file
433
sound/soc/mediatek/an7581/an7581-dai-etdm.c
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Airoha ALSA SoC Audio DAI eTDM Control
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include "an7581-afe-common.h"
|
||||
#include "an7581-reg.h"
|
||||
|
||||
#define HOPPING_CLK 0
|
||||
#define APLL_CLK 1
|
||||
#define MTK_DAI_ETDM_FORMAT_I2S 0
|
||||
#define MTK_DAI_ETDM_FORMAT_DSPA 4
|
||||
#define MTK_DAI_ETDM_FORMAT_DSPB 5
|
||||
|
||||
enum {
|
||||
MTK_ETDM_RATE_8K = 0,
|
||||
MTK_ETDM_RATE_12K = 1,
|
||||
MTK_ETDM_RATE_16K = 2,
|
||||
MTK_ETDM_RATE_24K = 3,
|
||||
MTK_ETDM_RATE_32K = 4,
|
||||
MTK_ETDM_RATE_48K = 5,
|
||||
MTK_ETDM_RATE_96K = 6,
|
||||
MTK_ETDM_RATE_192K = 7,
|
||||
MTK_ETDM_RATE_384K = 8,
|
||||
MTK_ETDM_RATE_7K = 16,
|
||||
MTK_ETDM_RATE_11K = 17,
|
||||
MTK_ETDM_RATE_14K = 18,
|
||||
MTK_ETDM_RATE_22K = 19,
|
||||
MTK_ETDM_RATE_29K = 20,
|
||||
MTK_ETDM_RATE_44K = 21,
|
||||
MTK_ETDM_RATE_88K = 22,
|
||||
MTK_ETDM_RATE_176K = 23,
|
||||
MTK_ETDM_RATE_352K = 24,
|
||||
};
|
||||
|
||||
struct mtk_dai_etdm_priv {
|
||||
bool bck_inv;
|
||||
bool lrck_inv;
|
||||
bool slave_mode;
|
||||
unsigned int format;
|
||||
};
|
||||
|
||||
static unsigned int an7581_etdm_rate_transform(struct device *dev, unsigned int rate)
|
||||
{
|
||||
switch (rate) {
|
||||
case 7350:
|
||||
return MTK_ETDM_RATE_7K;
|
||||
case 8000:
|
||||
return MTK_ETDM_RATE_8K;
|
||||
case 11025:
|
||||
return MTK_ETDM_RATE_11K;
|
||||
case 12000:
|
||||
return MTK_ETDM_RATE_12K;
|
||||
case 14700:
|
||||
return MTK_ETDM_RATE_14K;
|
||||
case 16000:
|
||||
return MTK_ETDM_RATE_16K;
|
||||
case 22050:
|
||||
return MTK_ETDM_RATE_22K;
|
||||
case 24000:
|
||||
return MTK_ETDM_RATE_24K;
|
||||
case 29400:
|
||||
return MTK_ETDM_RATE_29K;
|
||||
case 32000:
|
||||
return MTK_ETDM_RATE_32K;
|
||||
case 44100:
|
||||
return MTK_ETDM_RATE_44K;
|
||||
case 48000:
|
||||
return MTK_ETDM_RATE_48K;
|
||||
case 88200:
|
||||
return MTK_ETDM_RATE_88K;
|
||||
case 96000:
|
||||
return MTK_ETDM_RATE_96K;
|
||||
case 176400:
|
||||
return MTK_ETDM_RATE_176K;
|
||||
case 192000:
|
||||
return MTK_ETDM_RATE_192K;
|
||||
case 352800:
|
||||
return MTK_ETDM_RATE_352K;
|
||||
case 384000:
|
||||
return MTK_ETDM_RATE_384K;
|
||||
default:
|
||||
dev_warn_ratelimited(dev, "%s(), rate %u invalid, using %d!\n",
|
||||
__func__, rate, MTK_ETDM_RATE_48K);
|
||||
return MTK_ETDM_RATE_48K;
|
||||
}
|
||||
}
|
||||
|
||||
static int get_etdm_wlen(unsigned int bitwidth)
|
||||
{
|
||||
return bitwidth <= 16 ? 16 : 32;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget mtk_dai_etdm_widgets[] = {
|
||||
/* DL */
|
||||
SND_SOC_DAPM_MIXER("I150", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_MIXER("I151", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
|
||||
/* UL */
|
||||
SND_SOC_DAPM_MIXER("O124", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_MIXER("O125", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route mtk_dai_etdm_routes[] = {
|
||||
{"I150", NULL, "ETDM Capture"},
|
||||
{"I151", NULL, "ETDM Capture"},
|
||||
{"ETDM Playback", NULL, "O124"},
|
||||
{"ETDM Playback", NULL, "O125"},
|
||||
{"O124", NULL, "I032"},
|
||||
{"O125", NULL, "I033"},
|
||||
};
|
||||
|
||||
/* dai ops */
|
||||
static int mtk_dai_etdm_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
|
||||
struct an7581_afe_private *afe_priv = afe->platform_priv;
|
||||
|
||||
guard(mutex)(&afe_priv->user_lock);
|
||||
|
||||
if (!afe_priv->users)
|
||||
regmap_set_bits(afe->regmap, AFE_DAC_CON0,
|
||||
BIT(AFE_AFE_ENABLE_SHIFT));
|
||||
afe_priv->users++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtk_dai_etdm_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
|
||||
struct an7581_afe_private *afe_priv = afe->platform_priv;
|
||||
|
||||
guard(mutex)(&afe_priv->user_lock);
|
||||
|
||||
afe_priv->users--;
|
||||
if (!afe_priv->users)
|
||||
regmap_clear_bits(afe->regmap, AFE_DAC_CON0,
|
||||
BIT(AFE_AFE_ENABLE_SHIFT));
|
||||
}
|
||||
|
||||
static unsigned int get_etdm_ch_fixup(unsigned int channels)
|
||||
{
|
||||
if (channels > 16)
|
||||
return 24;
|
||||
|
||||
if (channels > 8)
|
||||
return 16;
|
||||
|
||||
if (channels > 4)
|
||||
return 8;
|
||||
|
||||
if (channels > 2)
|
||||
return 4;
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int mtk_dai_etdm_config(struct mtk_base_afe *afe,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai,
|
||||
int stream)
|
||||
{
|
||||
struct an7581_afe_private *afe_priv = afe->platform_priv;
|
||||
struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai->id];
|
||||
unsigned int rate = params_rate(params);
|
||||
unsigned int etdm_rate = an7581_etdm_rate_transform(afe->dev, rate);
|
||||
unsigned int bit_width = params_width(params);
|
||||
unsigned int mask, mask1;
|
||||
unsigned int val, val1;
|
||||
|
||||
dev_dbg(afe->dev, "%s(), stream %d, rate %u, bitwidth %u\n",
|
||||
__func__, stream, rate, params_width(params));
|
||||
|
||||
/* CON0 */
|
||||
mask = ETDM_SLAVE_MODE | ETDM_BIT_LEN | ETDM_WRD_LEN |
|
||||
ETDM_FMT | ETDM_CH_NUM;
|
||||
val = FIELD_PREP(ETDM_BIT_LEN, params_width(params) - 1) |
|
||||
FIELD_PREP(ETDM_WRD_LEN, get_etdm_wlen(bit_width) - 1) |
|
||||
FIELD_PREP(ETDM_FMT, etdm_data->format) |
|
||||
FIELD_PREP(ETDM_CH_NUM,
|
||||
get_etdm_ch_fixup(params_channels(params)) - 1);
|
||||
if (etdm_data->slave_mode)
|
||||
val |= ETDM_SLAVE_MODE;
|
||||
|
||||
/* CON1 */
|
||||
mask1 = EDTM_LRCK_AUTO_MODE | EDTM_CKEN_SEL | EDTM_LRCK_AUTO_OFF |
|
||||
EDTM_INITIAL_POINT | EDTM_INITIAL_COUNT;
|
||||
val1 = EDTM_LRCK_AUTO_MODE | EDTM_CKEN_SEL | EDTM_LRCK_AUTO_OFF |
|
||||
FIELD_PREP(EDTM_INITIAL_POINT, 14) | FIELD_PREP(EDTM_INITIAL_COUNT, 14);
|
||||
|
||||
switch (stream) {
|
||||
case SNDRV_PCM_STREAM_PLAYBACK:
|
||||
regmap_update_bits(afe->regmap, ETDM_OUT1_CON0, mask, val);
|
||||
|
||||
mask1 |= EDTM_DIRECT_INPUT_MASTER_BCK;
|
||||
val1 |= EDTM_DIRECT_INPUT_MASTER_BCK;
|
||||
|
||||
regmap_update_bits(afe->regmap, ETDM_OUT1_CON1, mask1, val1);
|
||||
|
||||
regmap_update_bits(afe->regmap, ETDM_OUT1_CON4, OUT_SEL_FS,
|
||||
FIELD_PREP(OUT_SEL_FS, etdm_rate));
|
||||
|
||||
regmap_update_bits(afe->irqs[AN7581_IRQ_0].regmap,
|
||||
afe->irqs[AN7581_IRQ_0].irq_data->irq_en_reg,
|
||||
AFE_IRQ_EN_SEL, AFE_IRQ_EN_SEL_I2SOUT);
|
||||
break;
|
||||
case SNDRV_PCM_STREAM_CAPTURE:
|
||||
regmap_update_bits(afe->regmap, ETDM_IN1_CON0, mask, val);
|
||||
|
||||
regmap_update_bits(afe->regmap, ETDM_IN1_CON1, mask1, val1);
|
||||
|
||||
regmap_update_bits(afe->regmap, ETDM_IN1_CON3, IN_SEL_FS,
|
||||
FIELD_PREP(IN_SEL_FS, etdm_rate));
|
||||
|
||||
regmap_update_bits(afe->irqs[AN7581_IRQ_1].regmap,
|
||||
afe->irqs[AN7581_IRQ_1].irq_data->irq_en_reg,
|
||||
AFE_IRQ_EN_SEL, AFE_IRQ_EN_SEL_I2SIN);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
unsigned int rate = params_rate(params);
|
||||
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
regmap_update_bits(afe->regmap, ETDM_COWORK_CON0,
|
||||
EDTM_IN1_SLAVE_SEL,
|
||||
EDTM_IN1_SLAVE_FROM_ETDMIN1_SLAVE);
|
||||
regmap_update_bits(afe->regmap, ETDM_COWORK_CON0,
|
||||
EDTM_OUT1_SLAVE_SEL,
|
||||
EDTM_OUT1_SLAVE_FROM_ETDMOUT1_SLAVE);
|
||||
regmap_update_bits(afe->regmap, ETDM_COWORK_CON1,
|
||||
EDTM_IN1_SDATA0_SEL,
|
||||
EDTM_IN1_SDATA0_FROM_PAD);
|
||||
|
||||
switch (rate) {
|
||||
case 7350:
|
||||
case 8000:
|
||||
case 11025:
|
||||
case 12000:
|
||||
case 14700:
|
||||
case 16000:
|
||||
case 22050:
|
||||
case 24000:
|
||||
case 29400:
|
||||
case 32000:
|
||||
case 44100:
|
||||
case 48000:
|
||||
case 88200:
|
||||
case 96000:
|
||||
case 176400:
|
||||
case 192000:
|
||||
case 352800:
|
||||
case 384000:
|
||||
return mtk_dai_etdm_config(afe, params, dai, substream->stream);
|
||||
default:
|
||||
dev_err(afe->dev, "Sample rate %d invalid\n", rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mtk_dai_etdm_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
dev_dbg(afe->dev, "%s(), cmd %d, dai id %d\n", __func__, cmd, dai->id);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
regmap_set_bits(afe->regmap, ETDM_OUT1_CON0,
|
||||
ETDM_OUT_EN);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
regmap_set_bits(afe->regmap, ETDM_IN1_CON0,
|
||||
ETDM_IN_EN);
|
||||
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
regmap_clear_bits(afe->regmap, ETDM_OUT1_CON0,
|
||||
ETDM_OUT_EN);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
regmap_clear_bits(afe->regmap, ETDM_IN1_CON0,
|
||||
ETDM_IN_EN);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_dai_etdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
|
||||
struct an7581_afe_private *afe_priv = afe->platform_priv;
|
||||
struct mtk_dai_etdm_priv *etdm_data;
|
||||
void *priv_data;
|
||||
|
||||
priv_data = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_etdm_priv),
|
||||
GFP_KERNEL);
|
||||
if (!priv_data)
|
||||
return -ENOMEM;
|
||||
|
||||
afe_priv->dai_priv[dai->id] = priv_data;
|
||||
etdm_data = afe_priv->dai_priv[dai->id];
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
etdm_data->format = MTK_DAI_ETDM_FORMAT_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPA;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPB;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
etdm_data->bck_inv = false;
|
||||
etdm_data->lrck_inv = false;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
etdm_data->bck_inv = false;
|
||||
etdm_data->lrck_inv = true;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
etdm_data->bck_inv = true;
|
||||
etdm_data->lrck_inv = false;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
etdm_data->bck_inv = true;
|
||||
etdm_data->lrck_inv = true;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
|
||||
case SND_SOC_DAIFMT_BP_FP:
|
||||
etdm_data->slave_mode = false;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_BC_FC:
|
||||
etdm_data->slave_mode = true;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops mtk_dai_etdm_ops = {
|
||||
.startup = mtk_dai_etdm_startup,
|
||||
.shutdown = mtk_dai_etdm_shutdown,
|
||||
.hw_params = mtk_dai_etdm_hw_params,
|
||||
.trigger = mtk_dai_etdm_trigger,
|
||||
.set_fmt = mtk_dai_etdm_set_fmt,
|
||||
};
|
||||
|
||||
/* dai driver */
|
||||
#define MTK_ETDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE |\
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = {
|
||||
{
|
||||
.name = "ETDM",
|
||||
.id = AN7581_DAI_ETDM,
|
||||
.capture = {
|
||||
.stream_name = "ETDM Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = MTK_ETDM_FORMATS,
|
||||
},
|
||||
.playback = {
|
||||
.stream_name = "ETDM Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = MTK_ETDM_FORMATS,
|
||||
},
|
||||
.ops = &mtk_dai_etdm_ops,
|
||||
.symmetric_rate = 1,
|
||||
.symmetric_sample_bits = 1,
|
||||
},
|
||||
};
|
||||
|
||||
int an7581_dai_etdm_register(struct mtk_base_afe *afe)
|
||||
{
|
||||
struct mtk_base_afe_dai *dai;
|
||||
|
||||
dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
|
||||
if (!dai)
|
||||
return -ENOMEM;
|
||||
|
||||
list_add(&dai->list, &afe->sub_dais);
|
||||
|
||||
dai->dai_drivers = mtk_dai_etdm_driver;
|
||||
dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_etdm_driver);
|
||||
|
||||
dai->dapm_widgets = mtk_dai_etdm_widgets;
|
||||
dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_etdm_widgets);
|
||||
dai->dapm_routes = mtk_dai_etdm_routes;
|
||||
dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_etdm_routes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
114
sound/soc/mediatek/an7581/an7581-reg.h
Normal file
114
sound/soc/mediatek/an7581/an7581-reg.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* an7581-reg.h -- Airoha AN7581 audio driver reg definition
|
||||
*/
|
||||
|
||||
#ifndef _AN7581_REG_H_
|
||||
#define _AN7581_REG_H_
|
||||
|
||||
#define AFE_DAC_CON0 0x0
|
||||
#define AFE_DL1_ENABLE_SHIFT 17
|
||||
#define AFE_UL1_ENABLE_SHIFT 1
|
||||
#define AFE_AFE_ENABLE_SHIFT 0
|
||||
|
||||
#define ETDM_COWORK_CON0 0x4c
|
||||
#define EDTM_IN1_SLAVE_SEL GENMASK(27, 24)
|
||||
#define EDTM_IN1_SLAVE_FROM_ETDMIN1_SLAVE FIELD_PREP_CONST(EDTM_IN1_SLAVE_SEL, 0x1)
|
||||
#define EDTM_IN1_SLAVE_FROM_ETDMIN2_MASTER FIELD_PREP_CONST(EDTM_IN1_SLAVE_SEL, 0x2)
|
||||
#define EDTM_IN1_SLAVE_FROM_ETDMIN2_SLAVE FIELD_PREP_CONST(EDTM_IN1_SLAVE_SEL, 0x3)
|
||||
#define EDTM_IN1_SLAVE_FROM_ETDMOUT1_MASTER FIELD_PREP_CONST(EDTM_IN1_SLAVE_SEL, 0x8)
|
||||
#define EDTM_IN1_SLAVE_FROM_ETDMOUT1_SLAVE FIELD_PREP_CONST(EDTM_IN1_SLAVE_SEL, 0x9)
|
||||
#define EDTM_IN1_SLAVE_FROM_ETDMOUT2_MASTER FIELD_PREP_CONST(EDTM_IN1_SLAVE_SEL, 0xa)
|
||||
#define EDTM_IN1_SLAVE_FROM_ETDMOUT2_SLAVE FIELD_PREP_CONST(EDTM_IN1_SLAVE_SEL, 0xb)
|
||||
#define EDTM_OUT1_SLAVE_SEL GENMASK(11, 8)
|
||||
#define EDTM_OUT1_SLAVE_FROM_ETDMIN1_MASTER FIELD_PREP_CONST(EDTM_OUT1_SLAVE_SEL, 0x0)
|
||||
#define EDTM_OUT1_SLAVE_FROM_ETDMIN1_SLAVE FIELD_PREP_CONST(EDTM_OUT1_SLAVE_SEL, 0x1)
|
||||
#define EDTM_OUT1_SLAVE_FROM_ETDMIN2_MASTER FIELD_PREP_CONST(EDTM_OUT1_SLAVE_SEL, 0x2)
|
||||
#define EDTM_OUT1_SLAVE_FROM_ETDMIN2_SLAVE FIELD_PREP_CONST(EDTM_OUT1_SLAVE_SEL, 0x3)
|
||||
#define EDTM_OUT1_SLAVE_FROM_ETDMOUT1_SLAVE FIELD_PREP_CONST(EDTM_OUT1_SLAVE_SEL, 0x9)
|
||||
#define EDTM_OUT1_SLAVE_FROM_ETDMOUT2_MASTER FIELD_PREP_CONST(EDTM_OUT1_SLAVE_SEL, 0xa)
|
||||
#define EDTM_OUT1_SLAVE_FROM_ETDMOUT2_SLAVE FIELD_PREP_CONST(EDTM_OUT1_SLAVE_SEL, 0xb)
|
||||
#define ETDM_COWORK_CON1 0x50
|
||||
#define EDTM_IN1_SDATA0_SEL GENMASK(3, 0)
|
||||
#define EDTM_IN1_SDATA0_FROM_PAD FIELD_PREP_CONST(EDTM_IN1_SDATA0_SEL, 0x0)
|
||||
#define EDTM_IN1_SDATA0_FROM_ETDMIN2 FIELD_PREP_CONST(EDTM_IN1_SDATA0_SEL, 0x2)
|
||||
#define EDTM_IN1_SDATA0_FROM_ETDMOUT1 FIELD_PREP_CONST(EDTM_IN1_SDATA0_SEL, 0x8)
|
||||
#define EDTM_IN1_SDATA0_FROM_ETDMOUT2 FIELD_PREP_CONST(EDTM_IN1_SDATA0_SEL, 0xa)
|
||||
#define ETDM_IN1_CON0 0x5c
|
||||
#define ETDM_CH_NUM GENMASK(26, 23)
|
||||
#define ETDM_WRD_LEN GENMASK(20, 16)
|
||||
#define ETDM_BIT_LEN GENMASK(15, 11)
|
||||
#define ETDM_FMT GENMASK(8, 6)
|
||||
#define ETDM_SLAVE_MODE BIT(5)
|
||||
#define ETDM_IN_EN BIT(0)
|
||||
#define ETDM_IN1_CON1 0x60
|
||||
#define EDTM_LRCK_AUTO_MODE BIT(29)
|
||||
#define EDTM_DIRECT_INPUT_MASTER_BCK BIT(30)
|
||||
#define EDTM_CKEN_SEL BIT(12)
|
||||
#define EDTM_LRCK_AUTO_OFF BIT(10)
|
||||
#define EDTM_INITIAL_POINT GENMASK(9, 5)
|
||||
#define EDTM_INITIAL_COUNT GENMASK(4, 0)
|
||||
#define ETDM_IN1_CON2 0x64
|
||||
#define IN_CLK_SRC GENMASK(12, 10)
|
||||
#define ETDM_IN1_CON3 0x68
|
||||
#define IN_SEL_FS GENMASK(30, 26)
|
||||
#define ETDM_IN1_CON4 0x6c
|
||||
#define IN_RELATCH GENMASK(24, 20)
|
||||
#define IN_CLK_INV BIT(18)
|
||||
#define ETDM_IN1_CON5 0x70
|
||||
#define ETDM_IN1_CON6 0x74
|
||||
#define ETDM_OUT1_CON0 0x7c
|
||||
#define ETDM_SYNC_MODE BIT(1)
|
||||
#define ETDM_OUT_EN BIT(0)
|
||||
#define ETDM_OUT1_CON1 0x80
|
||||
#define ETDM_OUT1_CON2 0x84
|
||||
#define ETDM_OUT1_CON3 0x88
|
||||
#define ETDM_OUT1_CON4 0x8c
|
||||
#define OUT_RELATCH GENMASK(28, 24)
|
||||
#define OUT_CLK_SRC GENMASK(8, 6)
|
||||
#define OUT_SEL_FS GENMASK(4, 0)
|
||||
#define ETDM_OUT1_CON5 0x90
|
||||
#define ETDM_CLK_DIV BIT(12)
|
||||
#define OUT_CLK_INV BIT(9)
|
||||
#define ETDM_OUT1_CON6 0x94
|
||||
#define ETDM_OUT1_CON7 0x98
|
||||
|
||||
#define AFE_DL1_BASE 0xa8
|
||||
#define AFE_DL1_END 0xb0
|
||||
#define AFE_DL1_CUR 0xac
|
||||
#define AFE_DL1_CON0 0xb4
|
||||
#define AFE_PBUF_SIZE_SHIFT 16
|
||||
#define AFE_PBUF_SIZE_MASK GENMASK(1, 0)
|
||||
#define AFE_MINLEN_SHIFT 8
|
||||
#define AFE_MINLEN_MASK GENMASK(3, 0)
|
||||
#define AFE_HD_SHIFT 5
|
||||
|
||||
#define AFE_UL1_BASE 0xc4
|
||||
#define AFE_UL1_END 0xc8
|
||||
#define AFE_UL1_CUR 0xcc
|
||||
#define AFE_UL1_CON0 0xd0
|
||||
#define AFE_HD_ALIGN_SHIFT 6
|
||||
|
||||
#define AFE_IRQ_CON0 0xe4
|
||||
#define AFE_IRQ_EN_SEL BIT(4)
|
||||
#define AFE_IRQ_EN_SEL_I2SIN FIELD_PREP_CONST(AFE_IRQ_EN_SEL, 0x0)
|
||||
#define AFE_IRQ_EN_SEL_I2SOUT FIELD_PREP_CONST(AFE_IRQ_EN_SEL, 0x1)
|
||||
#define AFE_IRQ_MISS_FLG_CLR_SHIFT 3
|
||||
#define AFE_IRQ_CLR_SHIFT 2
|
||||
#define AFE_IRQ_ON_SHIFT 0
|
||||
#define AFE_IRQ_CNT 0xe8
|
||||
#define AFE_IRQ_CNT_SHIFT 0
|
||||
#define AFE_IRQ_CNT_MASK GENMASK(31, 0)
|
||||
|
||||
#define AFE_IRQ_STS 0xf8
|
||||
#define AFE_IRQ_STS_PLAY BIT(1)
|
||||
#define AFE_IRQ_STS_RECORD BIT(0)
|
||||
|
||||
#define AFE_MAX_REGISTER AFE_IRQ_STS
|
||||
|
||||
#define AFE_IRQ1_CON0 0x0
|
||||
#define AFE_IRQ1_CNT 0x4
|
||||
|
||||
#define AFE_IRQ1_MAX_REGISTER AFE_IRQ1_CNT
|
||||
|
||||
#endif
|
||||
161
sound/soc/mediatek/an7581/an7581-wm8960.c
Normal file
161
sound/soc/mediatek/an7581/an7581-wm8960.c
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Airoha ALSA SoC I2S platform driver for AN7581
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "an7581-afe-common.h"
|
||||
|
||||
SND_SOC_DAILINK_DEFS(playback,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
|
||||
DAILINK_COMP_ARRAY(COMP_DUMMY()),
|
||||
DAILINK_COMP_ARRAY(COMP_EMPTY(), COMP_EMPTY()));
|
||||
|
||||
SND_SOC_DAILINK_DEFS(capture,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
|
||||
DAILINK_COMP_ARRAY(COMP_DUMMY()),
|
||||
DAILINK_COMP_ARRAY(COMP_EMPTY(), COMP_EMPTY()));
|
||||
|
||||
SND_SOC_DAILINK_DEFS(codec,
|
||||
DAILINK_COMP_ARRAY(COMP_CPU("ETDM")),
|
||||
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8960-hifi")),
|
||||
DAILINK_COMP_ARRAY(COMP_EMPTY(), COMP_EMPTY()));
|
||||
|
||||
static struct snd_soc_dai_link an7581_wm8960_dai_links[] = {
|
||||
/* FE */
|
||||
{
|
||||
.name = "wm8960-playback",
|
||||
.stream_name = "wm8960-playback",
|
||||
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
|
||||
SND_SOC_DPCM_TRIGGER_POST},
|
||||
.dynamic = 1,
|
||||
.playback_only = 1,
|
||||
SND_SOC_DAILINK_REG(playback),
|
||||
},
|
||||
{
|
||||
.name = "wm8960-capture",
|
||||
.stream_name = "wm8960-capture",
|
||||
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
|
||||
SND_SOC_DPCM_TRIGGER_POST},
|
||||
.dynamic = 1,
|
||||
.capture_only = 1,
|
||||
SND_SOC_DAILINK_REG(capture),
|
||||
},
|
||||
/* BE */
|
||||
{
|
||||
.name = "wm8960-codec",
|
||||
.no_pcm = 1,
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBP_CFP |
|
||||
SND_SOC_DAIFMT_GATED,
|
||||
SND_SOC_DAILINK_REG(codec),
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card an7581_wm8960_card = {
|
||||
.name = "an7581-wm8960",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = an7581_wm8960_dai_links,
|
||||
.num_links = ARRAY_SIZE(an7581_wm8960_dai_links),
|
||||
};
|
||||
|
||||
static int an7581_wm8960_machine_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *platform_dai_node, *codec_dai_node;
|
||||
struct snd_soc_card *card = &an7581_wm8960_card;
|
||||
struct device_node *of_platform, *codec;
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
int i, d, ret;
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
|
||||
of_platform = of_get_child_by_name(pdev->dev.of_node, "platform");
|
||||
|
||||
if (of_platform) {
|
||||
platform_dai_node = of_parse_phandle(of_platform, "sound-dai", 0);
|
||||
of_node_put(of_platform);
|
||||
|
||||
if (!platform_dai_node) {
|
||||
dev_err(&pdev->dev, "Failed to parse platform/sound-dai property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for_each_card_prelinks(card, i, dai_link) {
|
||||
struct snd_soc_dai_link_component *platform;
|
||||
|
||||
dai_link->num_platforms = 2;
|
||||
for_each_link_platforms(dai_link, d, platform) {
|
||||
if (platform->name)
|
||||
continue;
|
||||
platform->of_node = platform_dai_node;
|
||||
}
|
||||
}
|
||||
|
||||
codec = of_get_child_by_name(pdev->dev.of_node, "codec");
|
||||
|
||||
if (codec) {
|
||||
codec_dai_node = of_parse_phandle(codec, "sound-dai", 0);
|
||||
of_node_put(codec);
|
||||
|
||||
if (!codec_dai_node) {
|
||||
of_node_put(platform_dai_node);
|
||||
dev_err(&pdev->dev, "Failed to parse codec/sound-dai property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
of_node_put(platform_dai_node);
|
||||
dev_err(&pdev->dev, "Property 'codec' missing or invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for_each_card_prelinks(card, i, dai_link) {
|
||||
if (dai_link->codecs->name)
|
||||
continue;
|
||||
dai_link->codecs->of_node = codec_dai_node;
|
||||
}
|
||||
|
||||
ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to parse audio-routing: %d\n", ret);
|
||||
goto err_of_node_put;
|
||||
}
|
||||
|
||||
ret = devm_snd_soc_register_card(&pdev->dev, card);
|
||||
if (ret) {
|
||||
dev_err_probe(&pdev->dev, ret, "%s snd_soc_register_card fail\n", __func__);
|
||||
goto err_of_node_put;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_of_node_put:
|
||||
of_node_put(platform_dai_node);
|
||||
of_node_put(codec_dai_node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id an7581_wm8960_machine_dt_match[] = {
|
||||
{ .compatible = "airoha,an7581-wm8960-sound" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, an7581_wm8960_machine_dt_match);
|
||||
|
||||
static struct platform_driver an7581_wm8960_driver = {
|
||||
.driver = {
|
||||
.name = "an7581-wm8960",
|
||||
.of_match_table = an7581_wm8960_machine_dt_match,
|
||||
},
|
||||
.probe = an7581_wm8960_machine_probe,
|
||||
};
|
||||
module_platform_driver(an7581_wm8960_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Airoha SoC I2S platform driver for ALSA AN7581");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -204,11 +204,15 @@ int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage];
|
||||
const struct mtk_base_irq_data *irq_data = irqs->irq_data;
|
||||
unsigned int counter = runtime->period_size;
|
||||
struct regmap *regmap = afe->regmap;
|
||||
int fs;
|
||||
int ret;
|
||||
|
||||
dev_dbg(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
|
||||
|
||||
if (irqs->regmap)
|
||||
regmap = irqs->regmap;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
|
|
@ -220,7 +224,7 @@ int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
}
|
||||
|
||||
/* set irq counter */
|
||||
mtk_regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
|
||||
mtk_regmap_update_bits(regmap, irq_data->irq_cnt_reg,
|
||||
irq_data->irq_cnt_maskbit, counter,
|
||||
irq_data->irq_cnt_shift);
|
||||
|
||||
|
|
@ -230,12 +234,12 @@ int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
if (fs < 0)
|
||||
return -EINVAL;
|
||||
|
||||
mtk_regmap_update_bits(afe->regmap, irq_data->irq_fs_reg,
|
||||
mtk_regmap_update_bits(regmap, irq_data->irq_fs_reg,
|
||||
irq_data->irq_fs_maskbit, fs,
|
||||
irq_data->irq_fs_shift);
|
||||
|
||||
/* enable interrupt */
|
||||
mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
|
||||
mtk_regmap_update_bits(regmap, irq_data->irq_en_reg,
|
||||
1, 1, irq_data->irq_en_shift);
|
||||
|
||||
return 0;
|
||||
|
|
@ -248,10 +252,10 @@ int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
}
|
||||
|
||||
/* disable interrupt */
|
||||
mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
|
||||
mtk_regmap_update_bits(regmap, irq_data->irq_en_reg,
|
||||
1, 0, irq_data->irq_en_shift);
|
||||
/* and clear pending IRQ */
|
||||
mtk_regmap_write(afe->regmap, irq_data->irq_clr_reg,
|
||||
mtk_regmap_write(regmap, irq_data->irq_clr_reg,
|
||||
1 << irq_data->irq_clr_shift);
|
||||
return ret;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -153,6 +153,8 @@ struct mtk_base_afe_memif {
|
|||
struct mtk_base_afe_irq {
|
||||
const struct mtk_base_irq_data *irq_data;
|
||||
int irq_occupyed;
|
||||
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
struct mtk_base_afe_dai {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
#include <sound/soc-dai.h>
|
||||
#include <sound/soc_sdw_utils.h>
|
||||
|
||||
#define CS_AMP_CHANNELS_PER_AMP 4
|
||||
#define CS35L56_SPK_VOLUME_0DB 400 /* 0dB Max */
|
||||
|
||||
int asoc_sdw_cs35l56_volume_limit(struct snd_soc_card *card, const char *name_prefix)
|
||||
|
|
@ -64,51 +63,6 @@ int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai
|
|||
}
|
||||
EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_rtd_init, "SND_SOC_SDW_UTILS");
|
||||
|
||||
int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
|
||||
{
|
||||
const struct snd_soc_dai_link *dai_link = rtd->dai_link;
|
||||
const struct snd_soc_dai_link_ch_map *ch_map;
|
||||
const struct snd_soc_dai_link_component *codec_dlc;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
u8 ch_slot[8] = {};
|
||||
unsigned int amps_per_bus, ch_per_amp, mask;
|
||||
int i, ret;
|
||||
|
||||
WARN_ON(dai_link->num_cpus > ARRAY_SIZE(ch_slot));
|
||||
|
||||
/*
|
||||
* CS35L56 has 4 TX channels. When the capture is aggregated the
|
||||
* same bus slots will be allocated to all the amps on a bus. Only
|
||||
* one amp on that bus can be transmitting in each slot so divide
|
||||
* the available 4 slots between all the amps on a bus.
|
||||
*/
|
||||
amps_per_bus = dai_link->num_codecs / dai_link->num_cpus;
|
||||
if ((amps_per_bus == 0) || (amps_per_bus > CS_AMP_CHANNELS_PER_AMP)) {
|
||||
dev_err(rtd->card->dev, "Illegal num_codecs:%u / num_cpus:%u\n",
|
||||
dai_link->num_codecs, dai_link->num_cpus);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ch_per_amp = CS_AMP_CHANNELS_PER_AMP / amps_per_bus;
|
||||
|
||||
for_each_rtd_ch_maps(rtd, i, ch_map) {
|
||||
codec_dlc = snd_soc_link_to_codec(rtd->dai_link, i);
|
||||
codec_dai = snd_soc_find_dai(codec_dlc);
|
||||
mask = GENMASK(ch_per_amp - 1, 0) << ch_slot[ch_map->cpu];
|
||||
|
||||
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, mask, 4, 32);
|
||||
if (ret < 0) {
|
||||
dev_err(rtd->card->dev, "Failed to set TDM slot:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ch_slot[ch_map->cpu] += ch_per_amp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_feedback_rtd_init, "SND_SOC_SDW_UTILS");
|
||||
|
||||
int asoc_sdw_cs_amp_init(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *dai_links,
|
||||
struct asoc_sdw_codec_info *info,
|
||||
|
|
|
|||
|
|
@ -820,7 +820,6 @@ struct asoc_sdw_codec_info codec_info_list[] = {
|
|||
.dai_name = "cs35l56-sdw1c",
|
||||
.dai_type = SOC_SDW_DAI_TYPE_AMP,
|
||||
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
|
||||
.rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
|
||||
},
|
||||
},
|
||||
.dai_num = 2,
|
||||
|
|
@ -849,7 +848,6 @@ struct asoc_sdw_codec_info codec_info_list[] = {
|
|||
.dai_name = "cs35l56-sdw1c",
|
||||
.dai_type = SOC_SDW_DAI_TYPE_AMP,
|
||||
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
|
||||
.rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
|
||||
},
|
||||
},
|
||||
.dai_num = 2,
|
||||
|
|
@ -878,7 +876,6 @@ struct asoc_sdw_codec_info codec_info_list[] = {
|
|||
.dai_name = "cs35l56-sdw1c",
|
||||
.dai_type = SOC_SDW_DAI_TYPE_AMP,
|
||||
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
|
||||
.rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
|
||||
},
|
||||
},
|
||||
.dai_num = 2,
|
||||
|
|
@ -907,7 +904,6 @@ struct asoc_sdw_codec_info codec_info_list[] = {
|
|||
.dai_name = "cs35l56-sdw1c",
|
||||
.dai_type = SOC_SDW_DAI_TYPE_AMP,
|
||||
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
|
||||
.rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
|
||||
},
|
||||
},
|
||||
.dai_num = 2,
|
||||
|
|
@ -1731,7 +1727,7 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
|
||||
struct snd_soc_dai_link_ch_map *ch_maps;
|
||||
int ch = params_channels(params);
|
||||
unsigned int ch_mask;
|
||||
unsigned int cpu_ch_mask, codec_ch_mask;
|
||||
int num_codecs;
|
||||
int step;
|
||||
int i;
|
||||
|
|
@ -1741,8 +1737,9 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
/* Identical data will be sent to all codecs in playback */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
ch_mask = GENMASK(ch - 1, 0);
|
||||
cpu_ch_mask = GENMASK(ch - 1, 0);
|
||||
step = 0;
|
||||
codec_ch_mask = 0;
|
||||
} else {
|
||||
num_codecs = rtd->dai_link->num_codecs;
|
||||
|
||||
|
|
@ -1752,17 +1749,24 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
ch_mask = GENMASK(ch / num_codecs - 1, 0);
|
||||
step = hweight_long(ch_mask);
|
||||
cpu_ch_mask = GENMASK(ch / num_codecs - 1, 0);
|
||||
step = hweight_long(cpu_ch_mask);
|
||||
codec_ch_mask = cpu_ch_mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* The captured data will be combined from each cpu DAI if the dai
|
||||
* link has more than one codec DAIs. Set codec channel mask and
|
||||
* ASoC will set the corresponding channel numbers for each cpu dai.
|
||||
*
|
||||
* sdw_stream_add_slave() assigns different payload offsets to each
|
||||
* codec in a capture stream, so that the same channels on each
|
||||
* codec map to different channels on the CPU.
|
||||
*/
|
||||
for_each_link_ch_maps(rtd->dai_link, i, ch_maps)
|
||||
ch_maps->ch_mask = ch_mask << (i * step);
|
||||
for_each_link_ch_maps(rtd->dai_link, i, ch_maps) {
|
||||
ch_maps->cpu_ch_mask = cpu_ch_mask << (i * step);
|
||||
ch_maps->codec_ch_mask = codec_ch_mask;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1107,7 +1107,9 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
goto out;
|
||||
|
||||
for_each_rtd_codec_dais(rtd, i, codec_dai) {
|
||||
unsigned int tdm_mask = snd_soc_dai_tdm_mask_get(codec_dai, substream->stream);
|
||||
unsigned int ch_mask = snd_soc_dai_tdm_mask_get(codec_dai, substream->stream);
|
||||
struct snd_soc_dai_link_ch_map *ch_maps;
|
||||
int j;
|
||||
|
||||
/*
|
||||
* Skip CODECs which don't support the current stream type,
|
||||
|
|
@ -1129,9 +1131,15 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
/* copy params for each codec */
|
||||
tmp_params = *params;
|
||||
|
||||
/* fixup params based on TDM slot masks */
|
||||
if (tdm_mask)
|
||||
soc_pcm_codec_params_fixup(&tmp_params, tdm_mask);
|
||||
/* fixup params based on TDM or ch_map masks */
|
||||
if (!ch_mask) {
|
||||
for_each_rtd_ch_maps(rtd, j, ch_maps)
|
||||
if (ch_maps->codec == i)
|
||||
ch_mask |= ch_maps->codec_ch_mask;
|
||||
}
|
||||
|
||||
if (ch_mask)
|
||||
soc_pcm_codec_params_fixup(&tmp_params, ch_mask);
|
||||
|
||||
ret = snd_soc_dai_hw_params(codec_dai, substream,
|
||||
&tmp_params);
|
||||
|
|
@ -1165,7 +1173,7 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
*/
|
||||
for_each_rtd_ch_maps(rtd, j, ch_maps)
|
||||
if (ch_maps->cpu == i)
|
||||
ch_mask |= ch_maps->ch_mask;
|
||||
ch_mask |= ch_maps->cpu_ch_mask;
|
||||
|
||||
/* fixup cpu channel number */
|
||||
if (ch_mask)
|
||||
|
|
|
|||
|
|
@ -147,8 +147,8 @@ enum msp_direction {
|
|||
#define RCKPOL_MASK BIT(0)
|
||||
#define TCKPOL_MASK BIT(0)
|
||||
#define SPICKM_MASK (BIT(1) | BIT(0))
|
||||
#define MSP_RX_CLKPOL_BIT(n) ((n & RCKPOL_MASK) << RCKPOL_SHIFT)
|
||||
#define MSP_TX_CLKPOL_BIT(n) ((n & TCKPOL_MASK) << TCKPOL_SHIFT)
|
||||
#define MSP_RX_CLKPOL_BIT(n) (((n) & RCKPOL_MASK) << RCKPOL_SHIFT)
|
||||
#define MSP_TX_CLKPOL_BIT(n) (((n) & TCKPOL_MASK) << TCKPOL_SHIFT)
|
||||
|
||||
#define P1ELEN_SHIFT 0
|
||||
#define P1FLEN_SHIFT 3
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user