Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
# SPDX-License-Identifier: GPL-2.0
menu "UniPhier"
depends on (ARCH_UNIPHIER || COMPILE_TEST)
config SND_SOC_UNIPHIER_AIO
tristate "UniPhier AIO DAI Driver"
select REGMAP_MMIO
select SND_SOC_COMPRESS
help
This adds ASoC driver support for Socionext UniPhier
'AIO' Audio Input/Output subsystem.
Select Y if you use such device.
If unsure select "N".
config SND_SOC_UNIPHIER_LD11
tristate "UniPhier LD11/LD20 Device Driver"
select SND_SOC_UNIPHIER_AIO
help
This adds ASoC driver for Socionext UniPhier LD11/LD20
input and output that can be used with other codecs.
Select Y if you use such device.
If unsure select "N".
config SND_SOC_UNIPHIER_PXS2
tristate "UniPhier PXs2 Device Driver"
select SND_SOC_UNIPHIER_AIO
help
This adds ASoC driver for Socionext UniPhier PXs2
input and output that can be used with other codecs.
Select Y if you use such device.
If unsure select "N".
config SND_SOC_UNIPHIER_EVEA_CODEC
tristate "UniPhier SoC internal audio codec"
select REGMAP_MMIO
help
This adds Codec driver for Socionext UniPhier LD11/20 SoC
internal DAC. This driver supports Line In / Out and HeadPhone.
Select Y if you use such device.
If unsure select "N".
endmenu
+11
View File
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: GPL-2.0
snd-soc-uniphier-aio-cpu-y := aio-core.o aio-dma.o aio-cpu.o aio-compress.o
snd-soc-uniphier-aio-ld11-y := aio-ld11.o
snd-soc-uniphier-aio-pxs2-y := aio-pxs2.o
obj-$(CONFIG_SND_SOC_UNIPHIER_AIO) += snd-soc-uniphier-aio-cpu.o
obj-$(CONFIG_SND_SOC_UNIPHIER_LD11) += snd-soc-uniphier-aio-ld11.o
obj-$(CONFIG_SND_SOC_UNIPHIER_PXS2) += snd-soc-uniphier-aio-pxs2.o
snd-soc-uniphier-evea-y := evea.o
obj-$(CONFIG_SND_SOC_UNIPHIER_EVEA_CODEC) += snd-soc-uniphier-evea.o
@@ -0,0 +1,423 @@
// SPDX-License-Identifier: GPL-2.0
//
// Socionext UniPhier AIO Compress Audio driver.
//
// Copyright (c) 2017-2018 Socionext Inc.
#include <linux/bitfield.h>
#include <linux/circ_buf.h>
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include "aio.h"
static int uniphier_aio_compr_prepare(struct snd_soc_component *component,
struct snd_compr_stream *cstream);
static int uniphier_aio_compr_hw_free(struct snd_soc_component *component,
struct snd_compr_stream *cstream);
static int uniphier_aio_comprdma_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_compr *compr = rtd->compr;
struct device *dev = compr->card->dev;
struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));
struct uniphier_aio_sub *sub = &aio->sub[compr->direction];
size_t size = AUD_RING_SIZE;
int dma_dir = DMA_FROM_DEVICE, ret;
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33));
if (ret)
return ret;
sub->compr_area = kzalloc(size, GFP_KERNEL);
if (!sub->compr_area)
return -ENOMEM;
if (sub->swm->dir == PORT_DIR_OUTPUT)
dma_dir = DMA_TO_DEVICE;
sub->compr_addr = dma_map_single(dev, sub->compr_area, size, dma_dir);
if (dma_mapping_error(dev, sub->compr_addr)) {
kfree(sub->compr_area);
sub->compr_area = NULL;
return -ENOMEM;
}
sub->compr_bytes = size;
return 0;
}
static int uniphier_aio_comprdma_free(struct snd_soc_pcm_runtime *rtd)
{
struct snd_compr *compr = rtd->compr;
struct device *dev = compr->card->dev;
struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));
struct uniphier_aio_sub *sub = &aio->sub[compr->direction];
int dma_dir = DMA_FROM_DEVICE;
if (sub->swm->dir == PORT_DIR_OUTPUT)
dma_dir = DMA_TO_DEVICE;
dma_unmap_single(dev, sub->compr_addr, sub->compr_bytes, dma_dir);
kfree(sub->compr_area);
sub->compr_area = NULL;
return 0;
}
static int uniphier_aio_compr_open(struct snd_soc_component *component,
struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
int ret;
if (sub->cstream)
return -EBUSY;
sub->cstream = cstream;
sub->pass_through = 1;
sub->use_mmap = false;
ret = uniphier_aio_comprdma_new(rtd);
if (ret)
return ret;
ret = aio_init(sub);
if (ret)
return ret;
return 0;
}
static int uniphier_aio_compr_free(struct snd_soc_component *component,
struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
int ret;
ret = uniphier_aio_compr_hw_free(component, cstream);
if (ret)
return ret;
ret = uniphier_aio_comprdma_free(rtd);
if (ret)
return ret;
sub->cstream = NULL;
return 0;
}
static int uniphier_aio_compr_get_params(struct snd_soc_component *component,
struct snd_compr_stream *cstream,
struct snd_codec *params)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
*params = sub->cparams.codec;
return 0;
}
static int uniphier_aio_compr_set_params(struct snd_soc_component *component,
struct snd_compr_stream *cstream,
struct snd_compr_params *params)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
struct device *dev = &aio->chip->pdev->dev;
if (params->codec.id != SND_AUDIOCODEC_IEC61937) {
dev_err(dev, "Codec ID is not supported(%d)\n",
params->codec.id);
return -EINVAL;
}
if (params->codec.profile != SND_AUDIOPROFILE_IEC61937_SPDIF) {
dev_err(dev, "Codec profile is not supported(%d)\n",
params->codec.profile);
return -EINVAL;
}
/* IEC frame type will be changed after received valid data */
sub->iec_pc = IEC61937_PC_AAC;
sub->cparams = *params;
sub->setting = 1;
aio_port_reset(sub);
aio_src_reset(sub);
return uniphier_aio_compr_prepare(component, cstream);
}
static int uniphier_aio_compr_hw_free(struct snd_soc_component *component,
struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
sub->setting = 0;
return 0;
}
static int uniphier_aio_compr_prepare(struct snd_soc_component *component,
struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct snd_compr_runtime *runtime = cstream->runtime;
struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
int bytes = runtime->fragment_size;
int ret;
ret = aiodma_ch_set_param(sub);
if (ret)
return ret;
scoped_guard(spinlock_irqsave, &sub->lock)
ret = aiodma_rb_set_buffer(sub, sub->compr_addr,
sub->compr_addr + sub->compr_bytes,
bytes);
if (ret)
return ret;
ret = aio_port_set_param(sub, sub->pass_through, &sub->params);
if (ret)
return ret;
ret = aio_oport_set_stream_type(sub, sub->iec_pc);
if (ret)
return ret;
aio_port_set_enable(sub, 1);
ret = aio_if_set_param(sub, sub->pass_through);
if (ret)
return ret;
return 0;
}
static int uniphier_aio_compr_trigger(struct snd_soc_component *component,
struct snd_compr_stream *cstream,
int cmd)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct snd_compr_runtime *runtime = cstream->runtime;
struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
struct device *dev = &aio->chip->pdev->dev;
int bytes = runtime->fragment_size, ret = 0;
guard(spinlock_irqsave)(&sub->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
aiodma_ch_set_enable(sub, 1);
sub->running = 1;
break;
case SNDRV_PCM_TRIGGER_STOP:
sub->running = 0;
aiodma_ch_set_enable(sub, 0);
break;
default:
dev_warn(dev, "Unknown trigger(%d)\n", cmd);
ret = -EINVAL;
}
return ret;
}
static int uniphier_aio_compr_pointer(struct snd_soc_component *component,
struct snd_compr_stream *cstream,
struct snd_compr_tstamp64 *tstamp)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct snd_compr_runtime *runtime = cstream->runtime;
struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
int bytes = runtime->fragment_size;
u32 pos;
guard(spinlock_irqsave)(&sub->lock);
aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
if (sub->swm->dir == PORT_DIR_OUTPUT) {
pos = sub->rd_offs;
/* Size of AIO output format is double of IEC61937 */
tstamp->copied_total = sub->rd_total / 2;
} else {
pos = sub->wr_offs;
tstamp->copied_total = sub->rd_total;
}
tstamp->byte_offset = pos;
return 0;
}
static int aio_compr_send_to_hw(struct uniphier_aio_sub *sub,
char __user *buf, size_t dstsize)
{
u32 __user *srcbuf = (u32 __user *)buf;
u32 *dstbuf = (u32 *)(sub->compr_area + sub->wr_offs);
int src = 0, dst = 0, ret;
u32 frm, frm_a, frm_b;
while (dstsize > 0) {
ret = get_user(frm, srcbuf + src);
if (ret)
return ret;
src++;
frm_a = frm & 0xffff;
frm_b = (frm >> 16) & 0xffff;
if (frm == IEC61937_HEADER_SIGN) {
frm_a |= 0x01000000;
/* Next data is Pc and Pd */
sub->iec_header = true;
} else {
u16 pc = be16_to_cpu((__be16)frm_a);
if (sub->iec_header && sub->iec_pc != pc) {
/* Force overwrite IEC frame type */
sub->iec_pc = pc;
ret = aio_oport_set_stream_type(sub, pc);
if (ret)
return ret;
}
sub->iec_header = false;
}
dstbuf[dst++] = frm_a;
dstbuf[dst++] = frm_b;
dstsize -= sizeof(u32) * 2;
}
return 0;
}
static int uniphier_aio_compr_copy(struct snd_soc_component *component,
struct snd_compr_stream *cstream,
char __user *buf, size_t count)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct snd_compr_runtime *runtime = cstream->runtime;
struct device *carddev = rtd->compr->card->dev;
struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
size_t cnt = min_t(size_t, count, aio_rb_space_to_end(sub) / 2);
int bytes = runtime->fragment_size;
size_t s;
int ret;
if (cnt < sizeof(u32))
return 0;
if (sub->swm->dir == PORT_DIR_OUTPUT) {
dma_addr_t dmapos = sub->compr_addr + sub->wr_offs;
/* Size of AIO output format is double of IEC61937 */
s = cnt * 2;
dma_sync_single_for_cpu(carddev, dmapos, s, DMA_TO_DEVICE);
ret = aio_compr_send_to_hw(sub, buf, s);
dma_sync_single_for_device(carddev, dmapos, s, DMA_TO_DEVICE);
} else {
dma_addr_t dmapos = sub->compr_addr + sub->rd_offs;
s = cnt;
dma_sync_single_for_cpu(carddev, dmapos, s, DMA_FROM_DEVICE);
ret = copy_to_user(buf, sub->compr_area + sub->rd_offs, s);
dma_sync_single_for_device(carddev, dmapos, s, DMA_FROM_DEVICE);
}
if (ret)
return -EFAULT;
guard(spinlock_irqsave)(&sub->lock);
sub->threshold = 2 * bytes;
aiodma_rb_set_threshold(sub, sub->compr_bytes, 2 * bytes);
if (sub->swm->dir == PORT_DIR_OUTPUT) {
sub->wr_offs += s;
if (sub->wr_offs >= sub->compr_bytes)
sub->wr_offs -= sub->compr_bytes;
} else {
sub->rd_offs += s;
if (sub->rd_offs >= sub->compr_bytes)
sub->rd_offs -= sub->compr_bytes;
}
aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
return cnt;
}
static int uniphier_aio_compr_get_caps(struct snd_soc_component *component,
struct snd_compr_stream *cstream,
struct snd_compr_caps *caps)
{
caps->num_codecs = 1;
caps->min_fragment_size = AUD_MIN_FRAGMENT_SIZE;
caps->max_fragment_size = AUD_MAX_FRAGMENT_SIZE;
caps->min_fragments = AUD_MIN_FRAGMENT;
caps->max_fragments = AUD_MAX_FRAGMENT;
caps->codecs[0] = SND_AUDIOCODEC_IEC61937;
return 0;
}
static const struct snd_compr_codec_caps caps_iec = {
.num_descriptors = 1,
.descriptor[0].max_ch = 8,
.descriptor[0].num_sample_rates = 0,
.descriptor[0].num_bitrates = 0,
.descriptor[0].profiles = SND_AUDIOPROFILE_IEC61937_SPDIF,
.descriptor[0].modes = SND_AUDIOMODE_IEC_AC3 |
SND_AUDIOMODE_IEC_MPEG1 |
SND_AUDIOMODE_IEC_MP3 |
SND_AUDIOMODE_IEC_DTS,
.descriptor[0].formats = 0,
};
static int uniphier_aio_compr_get_codec_caps(struct snd_soc_component *component,
struct snd_compr_stream *stream,
struct snd_compr_codec_caps *codec)
{
if (codec->codec == SND_AUDIOCODEC_IEC61937)
*codec = caps_iec;
else
return -EINVAL;
return 0;
}
const struct snd_compress_ops uniphier_aio_compress_ops = {
.open = uniphier_aio_compr_open,
.free = uniphier_aio_compr_free,
.get_params = uniphier_aio_compr_get_params,
.set_params = uniphier_aio_compr_set_params,
.trigger = uniphier_aio_compr_trigger,
.pointer = uniphier_aio_compr_pointer,
.copy = uniphier_aio_compr_copy,
.get_caps = uniphier_aio_compr_get_caps,
.get_codec_caps = uniphier_aio_compr_get_codec_caps,
};
File diff suppressed because it is too large Load Diff
+832
View File
@@ -0,0 +1,832 @@
// SPDX-License-Identifier: GPL-2.0
//
// Socionext UniPhier AIO ALSA CPU DAI driver.
//
// Copyright (c) 2016-2018 Socionext Inc.
#include <linux/clk.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "aio.h"
static bool is_valid_pll(struct uniphier_aio_chip *chip, int pll_id)
{
struct device *dev = &chip->pdev->dev;
if (pll_id < 0 || chip->num_plls <= pll_id) {
dev_err(dev, "PLL(%d) is not supported\n", pll_id);
return false;
}
return chip->plls[pll_id].enable;
}
/**
* find_volume - find volume supported HW port by HW port number
* @chip: the AIO chip pointer
* @oport_hw: HW port number, one of AUD_HW_XXXX
*
* Find AIO device from device list by HW port number. Volume feature is
* available only in Output and PCM ports, this limitation comes from HW
* specifications.
*
* Return: The pointer of AIO substream if successful, otherwise NULL on error.
*/
static struct uniphier_aio_sub *find_volume(struct uniphier_aio_chip *chip,
int oport_hw)
{
int i;
for (i = 0; i < chip->num_aios; i++) {
struct uniphier_aio_sub *sub = &chip->aios[i].sub[0];
if (!sub->swm)
continue;
if (sub->swm->oport.hw == oport_hw)
return sub;
}
return NULL;
}
static bool match_spec(const struct uniphier_aio_spec *spec,
const char *name, int dir)
{
if (dir == SNDRV_PCM_STREAM_PLAYBACK &&
spec->swm.dir != PORT_DIR_OUTPUT) {
return false;
}
if (dir == SNDRV_PCM_STREAM_CAPTURE &&
spec->swm.dir != PORT_DIR_INPUT) {
return false;
}
if (spec->name && strcmp(spec->name, name) == 0)
return true;
if (spec->gname && strcmp(spec->gname, name) == 0)
return true;
return false;
}
/**
* find_spec - find HW specification info by name
* @aio: the AIO device pointer
* @name: name of device
* @direction: the direction of substream, SNDRV_PCM_STREAM_*
*
* Find hardware specification information from list by device name. This
* information is used for telling the difference of SoCs to driver.
*
* Specification list is array of 'struct uniphier_aio_spec' which is defined
* in each drivers (see: aio-i2s.c).
*
* Return: The pointer of hardware specification of AIO if successful,
* otherwise NULL on error.
*/
static const struct uniphier_aio_spec *find_spec(struct uniphier_aio *aio,
const char *name,
int direction)
{
const struct uniphier_aio_chip_spec *chip_spec = aio->chip->chip_spec;
int i;
for (i = 0; i < chip_spec->num_specs; i++) {
const struct uniphier_aio_spec *spec = &chip_spec->specs[i];
if (match_spec(spec, name, direction))
return spec;
}
return NULL;
}
/**
* find_divider - find clock divider by frequency
* @aio: the AIO device pointer
* @pll_id: PLL ID, should be AUD_PLL_XX
* @freq: required frequency
*
* Find suitable clock divider by frequency.
*
* Return: The ID of PLL if successful, otherwise negative error value.
*/
static int find_divider(struct uniphier_aio *aio, int pll_id, unsigned int freq)
{
struct uniphier_aio_pll *pll;
static const int mul[] = { 1, 1, 1, 2, };
static const int div[] = { 2, 3, 1, 3, };
int i;
if (!is_valid_pll(aio->chip, pll_id))
return -EINVAL;
pll = &aio->chip->plls[pll_id];
for (i = 0; i < ARRAY_SIZE(mul); i++)
if (pll->freq * mul[i] / div[i] == freq)
return i;
return -ENOTSUPP;
}
static int uniphier_aio_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct device *dev = &aio->chip->pdev->dev;
bool pll_auto = false;
int pll_id, div_id;
switch (clk_id) {
case AUD_CLK_IO:
return -ENOTSUPP;
case AUD_CLK_A1:
pll_id = AUD_PLL_A1;
break;
case AUD_CLK_F1:
pll_id = AUD_PLL_F1;
break;
case AUD_CLK_A2:
pll_id = AUD_PLL_A2;
break;
case AUD_CLK_F2:
pll_id = AUD_PLL_F2;
break;
case AUD_CLK_A:
pll_id = AUD_PLL_A1;
pll_auto = true;
break;
case AUD_CLK_F:
pll_id = AUD_PLL_F1;
pll_auto = true;
break;
case AUD_CLK_APLL:
pll_id = AUD_PLL_APLL;
break;
case AUD_CLK_RX0:
pll_id = AUD_PLL_RX0;
break;
case AUD_CLK_USB0:
pll_id = AUD_PLL_USB0;
break;
case AUD_CLK_HSC0:
pll_id = AUD_PLL_HSC0;
break;
default:
dev_err(dev, "Sysclk(%d) is not supported\n", clk_id);
return -EINVAL;
}
if (pll_auto) {
for (pll_id = 0; pll_id < aio->chip->num_plls; pll_id++) {
div_id = find_divider(aio, pll_id, freq);
if (div_id >= 0) {
aio->plldiv = div_id;
break;
}
}
if (pll_id == aio->chip->num_plls) {
dev_err(dev, "Sysclk frequency is not supported(%d)\n",
freq);
return -EINVAL;
}
}
if (dir == SND_SOC_CLOCK_OUT)
aio->pll_out = pll_id;
else
aio->pll_in = pll_id;
return 0;
}
static int uniphier_aio_set_pll(struct snd_soc_dai *dai, int pll_id,
int source, unsigned int freq_in,
unsigned int freq_out)
{
struct uniphier_aio *aio = uniphier_priv(dai);
int ret;
if (!is_valid_pll(aio->chip, pll_id))
return -EINVAL;
ret = aio_chip_set_pll(aio->chip, pll_id, freq_out);
if (ret < 0)
return ret;
return 0;
}
static int uniphier_aio_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct device *dev = &aio->chip->pdev->dev;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_LEFT_J:
case SND_SOC_DAIFMT_RIGHT_J:
case SND_SOC_DAIFMT_I2S:
aio->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
break;
default:
dev_err(dev, "Format is not supported(%d)\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
}
return 0;
}
static int uniphier_aio_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
sub->substream = substream;
sub->pass_through = 0;
sub->use_mmap = true;
return aio_init(sub);
}
static void uniphier_aio_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
sub->substream = NULL;
}
static int uniphier_aio_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
struct device *dev = &aio->chip->pdev->dev;
int freq, ret;
switch (params_rate(params)) {
case 48000:
case 32000:
case 24000:
freq = 12288000;
break;
case 44100:
case 22050:
freq = 11289600;
break;
default:
dev_err(dev, "Rate is not supported(%d)\n",
params_rate(params));
return -EINVAL;
}
ret = snd_soc_dai_set_sysclk(dai, AUD_CLK_A,
freq, SND_SOC_CLOCK_OUT);
if (ret)
return ret;
sub->params = *params;
sub->setting = 1;
aio_port_reset(sub);
aio_port_set_volume(sub, sub->vol);
aio_src_reset(sub);
return 0;
}
static int uniphier_aio_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
sub->setting = 0;
return 0;
}
static int uniphier_aio_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
int ret;
ret = aio_port_set_param(sub, sub->pass_through, &sub->params);
if (ret)
return ret;
ret = aio_src_set_param(sub, &sub->params);
if (ret)
return ret;
aio_port_set_enable(sub, 1);
ret = aio_if_set_param(sub, sub->pass_through);
if (ret)
return ret;
if (sub->swm->type == PORT_TYPE_CONV) {
ret = aio_srcif_set_param(sub);
if (ret)
return ret;
ret = aio_srcch_set_param(sub);
if (ret)
return ret;
aio_srcch_set_enable(sub, 1);
}
return 0;
}
static int uniphier_aio_dai_probe(struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
int i;
for (i = 0; i < ARRAY_SIZE(aio->sub); i++) {
struct uniphier_aio_sub *sub = &aio->sub[i];
const struct uniphier_aio_spec *spec;
spec = find_spec(aio, dai->name, i);
if (!spec)
continue;
sub->swm = &spec->swm;
sub->spec = spec;
sub->vol = AUD_VOL_INIT;
}
aio_iecout_set_enable(aio->chip, true);
aio_chip_init(aio->chip);
aio->chip->active = 1;
return 0;
}
static int uniphier_aio_dai_remove(struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
aio->chip->active = 0;
return 0;
}
static int uniphier_aio_ld11_probe(struct snd_soc_dai *dai)
{
int ret;
ret = uniphier_aio_dai_probe(dai);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(dai, AUD_PLL_A1, 0, 0, 36864000);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(dai, AUD_PLL_F1, 0, 0, 36864000);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(dai, AUD_PLL_A2, 0, 0, 33868800);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(dai, AUD_PLL_F2, 0, 0, 33868800);
if (ret < 0)
return ret;
return 0;
}
static int uniphier_aio_pxs2_probe(struct snd_soc_dai *dai)
{
int ret;
ret = uniphier_aio_dai_probe(dai);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(dai, AUD_PLL_A1, 0, 0, 36864000);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(dai, AUD_PLL_F1, 0, 0, 36864000);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(dai, AUD_PLL_A2, 0, 0, 33868800);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_pll(dai, AUD_PLL_F2, 0, 0, 33868800);
if (ret < 0)
return ret;
return 0;
}
const struct snd_soc_dai_ops uniphier_aio_i2s_ld11_ops = {
.probe = uniphier_aio_ld11_probe,
.remove = uniphier_aio_dai_remove,
.set_sysclk = uniphier_aio_set_sysclk,
.set_pll = uniphier_aio_set_pll,
.set_fmt = uniphier_aio_set_fmt,
.startup = uniphier_aio_startup,
.shutdown = uniphier_aio_shutdown,
.hw_params = uniphier_aio_hw_params,
.hw_free = uniphier_aio_hw_free,
.prepare = uniphier_aio_prepare,
};
EXPORT_SYMBOL_GPL(uniphier_aio_i2s_ld11_ops);
const struct snd_soc_dai_ops uniphier_aio_spdif_ld11_ops = {
.probe = uniphier_aio_ld11_probe,
.remove = uniphier_aio_dai_remove,
.set_sysclk = uniphier_aio_set_sysclk,
.set_pll = uniphier_aio_set_pll,
.startup = uniphier_aio_startup,
.shutdown = uniphier_aio_shutdown,
.hw_params = uniphier_aio_hw_params,
.hw_free = uniphier_aio_hw_free,
.prepare = uniphier_aio_prepare,
};
EXPORT_SYMBOL_GPL(uniphier_aio_spdif_ld11_ops);
const struct snd_soc_dai_ops uniphier_aio_spdif_ld11_ops2 = {
.probe = uniphier_aio_ld11_probe,
.remove = uniphier_aio_dai_remove,
.set_sysclk = uniphier_aio_set_sysclk,
.set_pll = uniphier_aio_set_pll,
.startup = uniphier_aio_startup,
.shutdown = uniphier_aio_shutdown,
.hw_params = uniphier_aio_hw_params,
.hw_free = uniphier_aio_hw_free,
.prepare = uniphier_aio_prepare,
.compress_new = snd_soc_new_compress,
};
EXPORT_SYMBOL_GPL(uniphier_aio_spdif_ld11_ops2);
const struct snd_soc_dai_ops uniphier_aio_i2s_pxs2_ops = {
.probe = uniphier_aio_pxs2_probe,
.remove = uniphier_aio_dai_remove,
.set_sysclk = uniphier_aio_set_sysclk,
.set_pll = uniphier_aio_set_pll,
.set_fmt = uniphier_aio_set_fmt,
.startup = uniphier_aio_startup,
.shutdown = uniphier_aio_shutdown,
.hw_params = uniphier_aio_hw_params,
.hw_free = uniphier_aio_hw_free,
.prepare = uniphier_aio_prepare,
};
EXPORT_SYMBOL_GPL(uniphier_aio_i2s_pxs2_ops);
const struct snd_soc_dai_ops uniphier_aio_spdif_pxs2_ops = {
.probe = uniphier_aio_pxs2_probe,
.remove = uniphier_aio_dai_remove,
.set_sysclk = uniphier_aio_set_sysclk,
.set_pll = uniphier_aio_set_pll,
.startup = uniphier_aio_startup,
.shutdown = uniphier_aio_shutdown,
.hw_params = uniphier_aio_hw_params,
.hw_free = uniphier_aio_hw_free,
.prepare = uniphier_aio_prepare,
};
EXPORT_SYMBOL_GPL(uniphier_aio_spdif_pxs2_ops);
const struct snd_soc_dai_ops uniphier_aio_spdif_pxs2_ops2 = {
.probe = uniphier_aio_pxs2_probe,
.remove = uniphier_aio_dai_remove,
.set_sysclk = uniphier_aio_set_sysclk,
.set_pll = uniphier_aio_set_pll,
.startup = uniphier_aio_startup,
.shutdown = uniphier_aio_shutdown,
.hw_params = uniphier_aio_hw_params,
.hw_free = uniphier_aio_hw_free,
.prepare = uniphier_aio_prepare,
.compress_new = snd_soc_new_compress,
};
EXPORT_SYMBOL_GPL(uniphier_aio_spdif_pxs2_ops2);
static void uniphier_aio_dai_suspend(struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
if (!snd_soc_dai_active(dai))
return;
aio->chip->num_wup_aios--;
if (!aio->chip->num_wup_aios) {
reset_control_assert(aio->chip->rst);
clk_disable_unprepare(aio->chip->clk);
}
}
static int uniphier_aio_suspend(struct snd_soc_component *component)
{
struct snd_soc_dai *dai;
for_each_component_dais(component, dai)
uniphier_aio_dai_suspend(dai);
return 0;
}
static int uniphier_aio_dai_resume(struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
int ret, i;
if (!snd_soc_dai_active(dai))
return 0;
if (!aio->chip->active)
return 0;
if (!aio->chip->num_wup_aios) {
ret = clk_prepare_enable(aio->chip->clk);
if (ret)
return ret;
ret = reset_control_deassert(aio->chip->rst);
if (ret)
goto err_out_clock;
}
aio_iecout_set_enable(aio->chip, true);
aio_chip_init(aio->chip);
for (i = 0; i < ARRAY_SIZE(aio->sub); i++) {
struct uniphier_aio_sub *sub = &aio->sub[i];
if (!sub->spec || !sub->substream)
continue;
ret = aio_init(sub);
if (ret)
goto err_out_reset;
if (!sub->setting)
continue;
aio_port_reset(sub);
aio_src_reset(sub);
}
aio->chip->num_wup_aios++;
return 0;
err_out_reset:
if (!aio->chip->num_wup_aios)
reset_control_assert(aio->chip->rst);
err_out_clock:
if (!aio->chip->num_wup_aios)
clk_disable_unprepare(aio->chip->clk);
return ret;
}
static int uniphier_aio_resume(struct snd_soc_component *component)
{
struct snd_soc_dai *dai;
int ret = 0;
for_each_component_dais(component, dai)
ret |= uniphier_aio_dai_resume(dai);
return ret;
}
static int uniphier_aio_vol_info(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 = AUD_VOL_MAX;
return 0;
}
static int uniphier_aio_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
struct uniphier_aio_chip *chip = snd_soc_component_get_drvdata(comp);
struct uniphier_aio_sub *sub;
int oport_hw = kcontrol->private_value;
sub = find_volume(chip, oport_hw);
if (!sub)
return 0;
ucontrol->value.integer.value[0] = sub->vol;
return 0;
}
static int uniphier_aio_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
struct uniphier_aio_chip *chip = snd_soc_component_get_drvdata(comp);
struct uniphier_aio_sub *sub;
int oport_hw = kcontrol->private_value;
sub = find_volume(chip, oport_hw);
if (!sub)
return 0;
if (sub->vol == ucontrol->value.integer.value[0])
return 0;
sub->vol = ucontrol->value.integer.value[0];
aio_port_set_volume(sub, sub->vol);
return 0;
}
static const struct snd_kcontrol_new uniphier_aio_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.name = "HPCMOUT1 Volume",
.info = uniphier_aio_vol_info,
.get = uniphier_aio_vol_get,
.put = uniphier_aio_vol_put,
.private_value = AUD_HW_HPCMOUT1,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.name = "PCMOUT1 Volume",
.info = uniphier_aio_vol_info,
.get = uniphier_aio_vol_get,
.put = uniphier_aio_vol_put,
.private_value = AUD_HW_PCMOUT1,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.name = "PCMOUT2 Volume",
.info = uniphier_aio_vol_info,
.get = uniphier_aio_vol_get,
.put = uniphier_aio_vol_put,
.private_value = AUD_HW_PCMOUT2,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.name = "PCMOUT3 Volume",
.info = uniphier_aio_vol_info,
.get = uniphier_aio_vol_get,
.put = uniphier_aio_vol_put,
.private_value = AUD_HW_PCMOUT3,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.name = "HIECOUT1 Volume",
.info = uniphier_aio_vol_info,
.get = uniphier_aio_vol_get,
.put = uniphier_aio_vol_put,
.private_value = AUD_HW_HIECOUT1,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.name = "IECOUT1 Volume",
.info = uniphier_aio_vol_info,
.get = uniphier_aio_vol_get,
.put = uniphier_aio_vol_put,
.private_value = AUD_HW_IECOUT1,
},
};
static const struct snd_soc_component_driver uniphier_aio_component = {
.name = "uniphier-aio",
.controls = uniphier_aio_controls,
.num_controls = ARRAY_SIZE(uniphier_aio_controls),
.suspend = uniphier_aio_suspend,
.resume = uniphier_aio_resume,
};
int uniphier_aio_probe(struct platform_device *pdev)
{
struct uniphier_aio_chip *chip;
struct device *dev = &pdev->dev;
int ret, i, j;
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->chip_spec = of_device_get_match_data(dev);
if (!chip->chip_spec)
return -EINVAL;
chip->regmap_sg = syscon_regmap_lookup_by_phandle(dev->of_node,
"socionext,syscon");
if (IS_ERR(chip->regmap_sg)) {
if (PTR_ERR(chip->regmap_sg) == -EPROBE_DEFER)
return -EPROBE_DEFER;
chip->regmap_sg = NULL;
}
chip->clk = devm_clk_get(dev, "aio");
if (IS_ERR(chip->clk))
return PTR_ERR(chip->clk);
chip->rst = devm_reset_control_get_shared(dev, "aio");
if (IS_ERR(chip->rst))
return PTR_ERR(chip->rst);
chip->num_aios = chip->chip_spec->num_dais;
chip->num_wup_aios = chip->num_aios;
chip->aios = devm_kcalloc(dev,
chip->num_aios, sizeof(struct uniphier_aio),
GFP_KERNEL);
if (!chip->aios)
return -ENOMEM;
chip->num_plls = chip->chip_spec->num_plls;
chip->plls = devm_kmemdup_array(dev, chip->chip_spec->plls, chip->num_plls,
sizeof(*chip->chip_spec->plls), GFP_KERNEL);
if (!chip->plls)
return -ENOMEM;
for (i = 0; i < chip->num_aios; i++) {
struct uniphier_aio *aio = &chip->aios[i];
aio->chip = chip;
aio->fmt = SND_SOC_DAIFMT_I2S;
for (j = 0; j < ARRAY_SIZE(aio->sub); j++) {
struct uniphier_aio_sub *sub = &aio->sub[j];
sub->aio = aio;
spin_lock_init(&sub->lock);
}
}
chip->pdev = pdev;
platform_set_drvdata(pdev, chip);
ret = clk_prepare_enable(chip->clk);
if (ret)
return ret;
ret = reset_control_deassert(chip->rst);
if (ret)
goto err_out_clock;
ret = devm_snd_soc_register_component(dev, &uniphier_aio_component,
chip->chip_spec->dais,
chip->chip_spec->num_dais);
if (ret) {
dev_err(dev, "Register component failed.\n");
goto err_out_reset;
}
ret = uniphier_aiodma_soc_register_platform(pdev);
if (ret) {
dev_err(dev, "Register platform failed.\n");
goto err_out_reset;
}
return 0;
err_out_reset:
reset_control_assert(chip->rst);
err_out_clock:
clk_disable_unprepare(chip->clk);
return ret;
}
EXPORT_SYMBOL_GPL(uniphier_aio_probe);
void uniphier_aio_remove(struct platform_device *pdev)
{
struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
reset_control_assert(chip->rst);
clk_disable_unprepare(chip->clk);
}
EXPORT_SYMBOL_GPL(uniphier_aio_remove);
MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
MODULE_DESCRIPTION("UniPhier AIO CPU DAI driver.");
MODULE_LICENSE("GPL v2");
+273
View File
@@ -0,0 +1,273 @@
// SPDX-License-Identifier: GPL-2.0
//
// Socionext UniPhier AIO DMA driver.
//
// Copyright (c) 2016-2018 Socionext Inc.
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include "aio.h"
static const struct snd_pcm_hardware uniphier_aiodma_hw = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED,
.period_bytes_min = 256,
.period_bytes_max = 4096,
.periods_min = 4,
.periods_max = 1024,
.buffer_bytes_max = 128 * 1024,
};
static void aiodma_pcm_irq(struct uniphier_aio_sub *sub)
{
struct snd_pcm_runtime *runtime = sub->substream->runtime;
int bytes = runtime->period_size *
runtime->channels * samples_to_bytes(runtime, 1);
int ret;
scoped_guard(spinlock, &sub->lock) {
ret = aiodma_rb_set_threshold(sub, runtime->dma_bytes,
sub->threshold + bytes);
if (!ret)
sub->threshold += bytes;
aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes, bytes);
aiodma_rb_clear_irq(sub);
}
snd_pcm_period_elapsed(sub->substream);
}
static void aiodma_compr_irq(struct uniphier_aio_sub *sub)
{
struct snd_compr_runtime *runtime = sub->cstream->runtime;
int bytes = runtime->fragment_size;
int ret;
scoped_guard(spinlock, &sub->lock) {
ret = aiodma_rb_set_threshold(sub, sub->compr_bytes,
sub->threshold + bytes);
if (!ret)
sub->threshold += bytes;
aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
aiodma_rb_clear_irq(sub);
}
snd_compr_fragment_elapsed(sub->cstream);
}
static irqreturn_t aiodma_irq(int irq, void *p)
{
struct platform_device *pdev = p;
struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
irqreturn_t ret = IRQ_NONE;
int i, j;
for (i = 0; i < chip->num_aios; i++) {
struct uniphier_aio *aio = &chip->aios[i];
for (j = 0; j < ARRAY_SIZE(aio->sub); j++) {
struct uniphier_aio_sub *sub = &aio->sub[j];
/* Skip channel that does not trigger */
if (!sub->running || !aiodma_rb_is_irq(sub))
continue;
if (sub->substream)
aiodma_pcm_irq(sub);
if (sub->cstream)
aiodma_compr_irq(sub);
ret = IRQ_HANDLED;
}
}
return ret;
}
static int uniphier_aiodma_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_soc_set_runtime_hwparams(substream, &uniphier_aiodma_hw);
return snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256);
}
static int uniphier_aiodma_prepare(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
int bytes = runtime->period_size *
runtime->channels * samples_to_bytes(runtime, 1);
int ret;
ret = aiodma_ch_set_param(sub);
if (ret)
return ret;
scoped_guard(spinlock_irqsave, &sub->lock)
ret = aiodma_rb_set_buffer(sub, runtime->dma_addr,
runtime->dma_addr + runtime->dma_bytes,
bytes);
if (ret)
return ret;
return 0;
}
static int uniphier_aiodma_trigger(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
struct device *dev = &aio->chip->pdev->dev;
int bytes = runtime->period_size *
runtime->channels * samples_to_bytes(runtime, 1);
guard(spinlock_irqsave)(&sub->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes,
bytes);
aiodma_ch_set_enable(sub, 1);
sub->running = 1;
break;
case SNDRV_PCM_TRIGGER_STOP:
sub->running = 0;
aiodma_ch_set_enable(sub, 0);
break;
default:
dev_warn(dev, "Unknown trigger(%d) ignored\n", cmd);
break;
}
return 0;
}
static snd_pcm_uframes_t uniphier_aiodma_pointer(
struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
int bytes = runtime->period_size *
runtime->channels * samples_to_bytes(runtime, 1);
snd_pcm_uframes_t pos;
guard(spinlock_irqsave)(&sub->lock);
aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes, bytes);
if (sub->swm->dir == PORT_DIR_OUTPUT)
pos = bytes_to_frames(runtime, sub->rd_offs);
else
pos = bytes_to_frames(runtime, sub->wr_offs);
return pos;
}
static int uniphier_aiodma_mmap(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
return remap_pfn_range(vma, vma->vm_start,
substream->runtime->dma_addr >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
}
static int uniphier_aiodma_new(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd)
{
struct device *dev = rtd->card->snd_card->dev;
struct snd_pcm *pcm = rtd->pcm;
int ret;
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33));
if (ret)
return ret;
snd_pcm_set_managed_buffer_all(pcm,
SNDRV_DMA_TYPE_DEV, dev,
uniphier_aiodma_hw.buffer_bytes_max,
uniphier_aiodma_hw.buffer_bytes_max);
return 0;
}
static const struct snd_soc_component_driver uniphier_soc_platform = {
.open = uniphier_aiodma_open,
.prepare = uniphier_aiodma_prepare,
.trigger = uniphier_aiodma_trigger,
.pointer = uniphier_aiodma_pointer,
.mmap = uniphier_aiodma_mmap,
.pcm_new = uniphier_aiodma_new,
.compress_ops = &uniphier_aio_compress_ops,
};
static const struct regmap_config aiodma_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0x7fffc,
.cache_type = REGCACHE_NONE,
};
/**
* uniphier_aiodma_soc_register_platform - register the AIO DMA
* @pdev: the platform device
*
* Register and setup the DMA of AIO to transfer the sound data to device.
* This function need to call once at driver startup and need NOT to call
* unregister function.
*
* Return: Zero if successful, otherwise a negative value on error.
*/
int uniphier_aiodma_soc_register_platform(struct platform_device *pdev)
{
struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
void __iomem *preg;
int irq, ret;
preg = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(preg))
return PTR_ERR(preg);
chip->regmap = devm_regmap_init_mmio(dev, preg,
&aiodma_regmap_config);
if (IS_ERR(chip->regmap))
return PTR_ERR(chip->regmap);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = devm_request_irq(dev, irq, aiodma_irq,
IRQF_SHARED, dev_name(dev), pdev);
if (ret)
return ret;
return devm_snd_soc_register_component(dev, &uniphier_soc_platform,
NULL, 0);
}
EXPORT_SYMBOL_GPL(uniphier_aiodma_soc_register_platform);
+356
View File
@@ -0,0 +1,356 @@
// SPDX-License-Identifier: GPL-2.0
//
// Socionext UniPhier AIO ALSA driver for LD11/LD20.
//
// Copyright (c) 2016-2018 Socionext Inc.
#include <linux/module.h>
#include "aio.h"
static const struct uniphier_aio_spec uniphier_aio_ld11[] = {
/* for HDMI PCM In, Pin:AI1Dx */
{
.name = AUD_NAME_PCMIN1,
.gname = AUD_GNAME_HDMI,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_INPUT,
.rb = { 21, 14, },
.ch = { 21, 14, },
.iif = { 5, 3, },
.iport = { 0, AUD_HW_PCMIN1, },
},
},
/* for SIF In, Pin:AI2Dx */
{
.name = AUD_NAME_PCMIN2,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_INPUT,
.rb = { 22, 15, },
.ch = { 22, 15, },
.iif = { 6, 4, },
.iport = { 1, AUD_HW_PCMIN2, },
},
},
/* for Line In, Pin:AI3Dx */
{
.name = AUD_NAME_PCMIN3,
.gname = AUD_GNAME_LINE,
.swm = {
.type = PORT_TYPE_EVE,
.dir = PORT_DIR_INPUT,
.rb = { 23, 16, },
.ch = { 23, 16, },
.iif = { 7, 5, },
.iport = { 2, AUD_HW_PCMIN3, },
},
},
/* for S/PDIF In, Pin:AI1IEC */
{
.name = AUD_NAME_IECIN1,
.gname = AUD_GNAME_IEC,
.swm = {
.type = PORT_TYPE_SPDIF,
.dir = PORT_DIR_INPUT,
.rb = { 26, 17, },
.ch = { 26, 17, },
.iif = { 10, 6, },
.iport = { 3, AUD_HW_IECIN1, },
},
},
/* for Speaker, Pin:AO1Dx */
{
.name = AUD_NAME_HPCMOUT1,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_OUTPUT,
.rb = { 0, 0, },
.ch = { 0, 0, },
.oif = { 0, 0, },
.oport = { 0, AUD_HW_HPCMOUT1, },
},
},
/* for HDMI PCM, Pin:AO2Dx */
{
.name = AUD_NAME_PCMOUT1,
.gname = AUD_GNAME_HDMI,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_OUTPUT,
.rb = { 0, 0, },
.ch = { 0, 0, },
.oif = { 0, 0, },
.oport = { 3, AUD_HW_PCMOUT1, },
},
},
/* for Line Out, Pin:LO2_x */
{
.name = AUD_NAME_PCMOUT2,
.gname = AUD_GNAME_LINE,
.swm = {
.type = PORT_TYPE_EVE,
.dir = PORT_DIR_OUTPUT,
.rb = { 2, 2, },
.ch = { 2, 2, },
.oif = { 2, 2, },
.oport = { 1, AUD_HW_PCMOUT2, },
},
},
/* for Headphone, Pin:HP1_x */
{
.name = AUD_NAME_PCMOUT3,
.swm = {
.type = PORT_TYPE_EVE,
.dir = PORT_DIR_OUTPUT,
.rb = { 3, 3, },
.ch = { 3, 3, },
.oif = { 3, 3, },
.oport = { 2, AUD_HW_PCMOUT3, },
},
},
/* for HW Sampling Rate Converter */
{
.name = AUD_NAME_EPCMOUT2,
.swm = {
.type = PORT_TYPE_CONV,
.dir = PORT_DIR_OUTPUT,
.rb = { 7, 5, },
.ch = { 7, 5, },
.oif = { 7, 5, },
.oport = { 6, AUD_HW_EPCMOUT2, },
.och = { 17, 12, },
.iif = { 1, 1, },
},
},
/* for HW Sampling Rate Converter 2 */
{
.name = AUD_NAME_EPCMOUT3,
.swm = {
.type = PORT_TYPE_CONV,
.dir = PORT_DIR_OUTPUT,
.rb = { 8, 6, },
.ch = { 8, 6, },
.oif = { 8, 6, },
.oport = { 7, AUD_HW_EPCMOUT3, },
.och = { 18, 13, },
.iif = { 2, 2, },
},
},
/* for S/PDIF Out, Pin:AO1IEC */
{
.name = AUD_NAME_HIECOUT1,
.gname = AUD_GNAME_IEC,
.swm = {
.type = PORT_TYPE_SPDIF,
.dir = PORT_DIR_OUTPUT,
.rb = { 1, 1, },
.ch = { 1, 1, },
.oif = { 1, 1, },
.oport = { 12, AUD_HW_HIECOUT1, },
},
},
/* for S/PDIF Out, Pin:AO1IEC, Compress */
{
.name = AUD_NAME_HIECCOMPOUT1,
.gname = AUD_GNAME_IEC,
.swm = {
.type = PORT_TYPE_SPDIF,
.dir = PORT_DIR_OUTPUT,
.rb = { 1, 1, },
.ch = { 1, 1, },
.oif = { 1, 1, },
.oport = { 12, AUD_HW_HIECOUT1, },
},
},
};
static const struct uniphier_aio_pll uniphier_aio_pll_ld11[] = {
[AUD_PLL_A1] = { .enable = true, },
[AUD_PLL_F1] = { .enable = true, },
[AUD_PLL_A2] = { .enable = true, },
[AUD_PLL_F2] = { .enable = true, },
[AUD_PLL_APLL] = { .enable = true, },
[AUD_PLL_RX0] = { .enable = true, },
[AUD_PLL_USB0] = { .enable = true, },
[AUD_PLL_HSC0] = { .enable = true, },
};
static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = {
{
.name = AUD_GNAME_HDMI,
.playback = {
.stream_name = AUD_NAME_PCMOUT1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.capture = {
.stream_name = AUD_NAME_PCMIN1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_32000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_ld11_ops,
},
{
.name = AUD_NAME_PCMIN2,
.capture = {
.stream_name = AUD_NAME_PCMIN2,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_ld11_ops,
},
{
.name = AUD_GNAME_LINE,
.playback = {
.stream_name = AUD_NAME_PCMOUT2,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.capture = {
.stream_name = AUD_NAME_PCMIN3,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_ld11_ops,
},
{
.name = AUD_NAME_HPCMOUT1,
.playback = {
.stream_name = AUD_NAME_HPCMOUT1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 8,
},
.ops = &uniphier_aio_i2s_ld11_ops,
},
{
.name = AUD_NAME_PCMOUT3,
.playback = {
.stream_name = AUD_NAME_PCMOUT3,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_ld11_ops,
},
{
.name = AUD_NAME_HIECOUT1,
.playback = {
.stream_name = AUD_NAME_HIECOUT1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_spdif_ld11_ops,
},
{
.name = AUD_NAME_EPCMOUT2,
.playback = {
.stream_name = AUD_NAME_EPCMOUT2,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_32000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_ld11_ops,
},
{
.name = AUD_NAME_EPCMOUT3,
.playback = {
.stream_name = AUD_NAME_EPCMOUT3,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_32000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_ld11_ops,
},
{
.name = AUD_NAME_HIECCOMPOUT1,
.playback = {
.stream_name = AUD_NAME_HIECCOMPOUT1,
.channels_min = 1,
.channels_max = 1,
},
.ops = &uniphier_aio_spdif_ld11_ops2,
},
};
static const struct uniphier_aio_chip_spec uniphier_aio_ld11_spec = {
.specs = uniphier_aio_ld11,
.num_specs = ARRAY_SIZE(uniphier_aio_ld11),
.dais = uniphier_aio_dai_ld11,
.num_dais = ARRAY_SIZE(uniphier_aio_dai_ld11),
.plls = uniphier_aio_pll_ld11,
.num_plls = ARRAY_SIZE(uniphier_aio_pll_ld11),
.addr_ext = 0,
};
static const struct uniphier_aio_chip_spec uniphier_aio_ld20_spec = {
.specs = uniphier_aio_ld11,
.num_specs = ARRAY_SIZE(uniphier_aio_ld11),
.dais = uniphier_aio_dai_ld11,
.num_dais = ARRAY_SIZE(uniphier_aio_dai_ld11),
.plls = uniphier_aio_pll_ld11,
.num_plls = ARRAY_SIZE(uniphier_aio_pll_ld11),
.addr_ext = 1,
};
static const struct of_device_id uniphier_aio_of_match[] __maybe_unused = {
{
.compatible = "socionext,uniphier-ld11-aio",
.data = &uniphier_aio_ld11_spec,
},
{
.compatible = "socionext,uniphier-ld20-aio",
.data = &uniphier_aio_ld20_spec,
},
{},
};
MODULE_DEVICE_TABLE(of, uniphier_aio_of_match);
static struct platform_driver uniphier_aio_driver = {
.driver = {
.name = "snd-uniphier-aio-ld11",
.of_match_table = of_match_ptr(uniphier_aio_of_match),
},
.probe = uniphier_aio_probe,
.remove = uniphier_aio_remove,
};
module_platform_driver(uniphier_aio_driver);
MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
MODULE_DESCRIPTION("UniPhier LD11/LD20 AIO driver.");
MODULE_LICENSE("GPL v2");
+265
View File
@@ -0,0 +1,265 @@
// SPDX-License-Identifier: GPL-2.0
//
// Socionext UniPhier AIO ALSA driver for PXs2.
//
// Copyright (c) 2018 Socionext Inc.
#include <linux/module.h>
#include "aio.h"
static const struct uniphier_aio_spec uniphier_aio_pxs2[] = {
/* for Line PCM In, Pin:AI1Dx */
{
.name = AUD_NAME_PCMIN1,
.gname = AUD_GNAME_LINE,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_INPUT,
.rb = { 16, 11, },
.ch = { 16, 11, },
.iif = { 0, 0, },
.iport = { 0, AUD_HW_PCMIN1, },
},
},
/* for Speaker/Headphone/Mic PCM In, Pin:AI2Dx */
{
.name = AUD_NAME_PCMIN2,
.gname = AUD_GNAME_AUX,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_INPUT,
.rb = { 17, 12, },
.ch = { 17, 12, },
.iif = { 1, 1, },
.iport = { 1, AUD_HW_PCMIN2, },
},
},
/* for HDMI PCM Out, Pin:AO1Dx (inner) */
{
.name = AUD_NAME_HPCMOUT1,
.gname = AUD_GNAME_HDMI,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_OUTPUT,
.rb = { 0, 0, },
.ch = { 0, 0, },
.oif = { 0, 0, },
.oport = { 3, AUD_HW_HPCMOUT1, },
},
},
/* for Line PCM Out, Pin:AO2Dx */
{
.name = AUD_NAME_PCMOUT1,
.gname = AUD_GNAME_LINE,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_OUTPUT,
.rb = { 1, 1, },
.ch = { 1, 1, },
.oif = { 1, 1, },
.oport = { 0, AUD_HW_PCMOUT1, },
},
},
/* for Speaker/Headphone/Mic PCM Out, Pin:AO3Dx */
{
.name = AUD_NAME_PCMOUT2,
.gname = AUD_GNAME_AUX,
.swm = {
.type = PORT_TYPE_I2S,
.dir = PORT_DIR_OUTPUT,
.rb = { 2, 2, },
.ch = { 2, 2, },
.oif = { 2, 2, },
.oport = { 1, AUD_HW_PCMOUT2, },
},
},
/* for HDMI Out, Pin:AO1IEC */
{
.name = AUD_NAME_HIECOUT1,
.swm = {
.type = PORT_TYPE_SPDIF,
.dir = PORT_DIR_OUTPUT,
.rb = { 6, 4, },
.ch = { 6, 4, },
.oif = { 6, 4, },
.oport = { 12, AUD_HW_HIECOUT1, },
},
},
/* for HDMI Out, Pin:AO1IEC, Compress */
{
.name = AUD_NAME_HIECCOMPOUT1,
.swm = {
.type = PORT_TYPE_SPDIF,
.dir = PORT_DIR_OUTPUT,
.rb = { 6, 4, },
.ch = { 6, 4, },
.oif = { 6, 4, },
.oport = { 12, AUD_HW_HIECOUT1, },
},
},
/* for S/PDIF Out, Pin:AO2IEC */
{
.name = AUD_NAME_IECOUT1,
.swm = {
.type = PORT_TYPE_SPDIF,
.dir = PORT_DIR_OUTPUT,
.rb = { 7, 5, },
.ch = { 7, 5, },
.oif = { 7, 5, },
.oport = { 13, AUD_HW_IECOUT1, },
},
},
/* for S/PDIF Out, Pin:AO2IEC */
{
.name = AUD_NAME_IECCOMPOUT1,
.swm = {
.type = PORT_TYPE_SPDIF,
.dir = PORT_DIR_OUTPUT,
.rb = { 7, 5, },
.ch = { 7, 5, },
.oif = { 7, 5, },
.oport = { 13, AUD_HW_IECOUT1, },
},
},
};
static const struct uniphier_aio_pll uniphier_aio_pll_pxs2[] = {
[AUD_PLL_A1] = { .enable = true, },
[AUD_PLL_F1] = { .enable = true, },
[AUD_PLL_A2] = { .enable = true, },
[AUD_PLL_F2] = { .enable = true, },
[AUD_PLL_APLL] = { .enable = true, },
[AUD_PLL_HSC0] = { .enable = true, },
};
static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = {
{
.name = AUD_GNAME_HDMI,
.playback = {
.stream_name = AUD_NAME_HPCMOUT1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_pxs2_ops,
},
{
.name = AUD_GNAME_LINE,
.playback = {
.stream_name = AUD_NAME_PCMOUT1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.capture = {
.stream_name = AUD_NAME_PCMIN1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_pxs2_ops,
},
{
.name = AUD_GNAME_AUX,
.playback = {
.stream_name = AUD_NAME_PCMOUT2,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.capture = {
.stream_name = AUD_NAME_PCMIN2,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_i2s_pxs2_ops,
},
{
.name = AUD_NAME_HIECOUT1,
.playback = {
.stream_name = AUD_NAME_HIECOUT1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_spdif_pxs2_ops,
},
{
.name = AUD_NAME_IECOUT1,
.playback = {
.stream_name = AUD_NAME_IECOUT1,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 2,
},
.ops = &uniphier_aio_spdif_pxs2_ops,
},
{
.name = AUD_NAME_HIECCOMPOUT1,
.playback = {
.stream_name = AUD_NAME_HIECCOMPOUT1,
.channels_min = 1,
.channels_max = 1,
},
.ops = &uniphier_aio_spdif_pxs2_ops2,
},
{
.name = AUD_NAME_IECCOMPOUT1,
.playback = {
.stream_name = AUD_NAME_IECCOMPOUT1,
.channels_min = 1,
.channels_max = 1,
},
.ops = &uniphier_aio_spdif_pxs2_ops2,
},
};
static const struct uniphier_aio_chip_spec uniphier_aio_pxs2_spec = {
.specs = uniphier_aio_pxs2,
.num_specs = ARRAY_SIZE(uniphier_aio_pxs2),
.dais = uniphier_aio_dai_pxs2,
.num_dais = ARRAY_SIZE(uniphier_aio_dai_pxs2),
.plls = uniphier_aio_pll_pxs2,
.num_plls = ARRAY_SIZE(uniphier_aio_pll_pxs2),
.addr_ext = 0,
};
static const struct of_device_id uniphier_aio_of_match[] __maybe_unused = {
{
.compatible = "socionext,uniphier-pxs2-aio",
.data = &uniphier_aio_pxs2_spec,
},
{},
};
MODULE_DEVICE_TABLE(of, uniphier_aio_of_match);
static struct platform_driver uniphier_aio_driver = {
.driver = {
.name = "snd-uniphier-aio-pxs2",
.of_match_table = of_match_ptr(uniphier_aio_of_match),
},
.probe = uniphier_aio_probe,
.remove = uniphier_aio_remove,
};
module_platform_driver(uniphier_aio_driver);
MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
MODULE_DESCRIPTION("UniPhier PXs2 AIO driver.");
MODULE_LICENSE("GPL v2");
+476
View File
@@ -0,0 +1,476 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Socionext UniPhier AIO ALSA driver.
*
* Copyright (c) 2016-2018 Socionext Inc.
*/
#ifndef SND_UNIPHIER_AIO_REG_H__
#define SND_UNIPHIER_AIO_REG_H__
#include <linux/bitops.h>
/* soc-glue */
#define SG_AOUTEN 0x1c04
/* SW view */
#define A2CHNMAPCTR0(n) (0x00000 + 0x40 * (n))
#define A2RBNMAPCTR0(n) (0x01000 + 0x40 * (n))
#define A2IPORTNMAPCTR0(n) (0x02000 + 0x40 * (n))
#define A2IPORTNMAPCTR1(n) (0x02004 + 0x40 * (n))
#define A2IIFNMAPCTR0(n) (0x03000 + 0x40 * (n))
#define A2OPORTNMAPCTR0(n) (0x04000 + 0x40 * (n))
#define A2OPORTNMAPCTR1(n) (0x04004 + 0x40 * (n))
#define A2OPORTNMAPCTR2(n) (0x04008 + 0x40 * (n))
#define A2OIFNMAPCTR0(n) (0x05000 + 0x40 * (n))
#define A2ATNMAPCTR0(n) (0x06000 + 0x40 * (n))
#define MAPCTR0_EN 0x80000000
/* CTL */
#define A2APLLCTR0 0x07000
#define A2APLLCTR0_APLLXPOW_MASK GENMASK(3, 0)
#define A2APLLCTR0_APLLXPOW_PWOFF (0x0 << 0)
#define A2APLLCTR0_APLLXPOW_PWON (0xf << 0)
#define A2APLLCTR1 0x07004
#define A2APLLCTR1_APLLX_MASK 0x00010101
#define A2APLLCTR1_APLLX_36MHZ 0x00000000
#define A2APLLCTR1_APLLX_33MHZ 0x00000001
#define A2EXMCLKSEL0 0x07030
#define A2EXMCLKSEL0_EXMCLK_MASK GENMASK(2, 0)
#define A2EXMCLKSEL0_EXMCLK_OUTPUT (0x0 << 0)
#define A2EXMCLKSEL0_EXMCLK_INPUT (0x7 << 0)
#define A2SSIFSW 0x07050
#define A2CH22_2CTR 0x07054
#define A2AIOINPUTSEL 0x070e0
#define A2AIOINPUTSEL_RXSEL_PCMI1_MASK GENMASK(2, 0)
#define A2AIOINPUTSEL_RXSEL_PCMI1_HDMIRX1 (0x2 << 0)
#define A2AIOINPUTSEL_RXSEL_PCMI2_MASK GENMASK(6, 4)
#define A2AIOINPUTSEL_RXSEL_PCMI2_SIF (0x7 << 4)
#define A2AIOINPUTSEL_RXSEL_PCMI3_MASK GENMASK(10, 8)
#define A2AIOINPUTSEL_RXSEL_PCMI3_EVEA (0x1 << 8)
#define A2AIOINPUTSEL_RXSEL_IECI1_MASK GENMASK(14, 12)
#define A2AIOINPUTSEL_RXSEL_IECI1_HDMIRX1 (0x2 << 12)
#define A2AIOINPUTSEL_RXSEL_MASK (A2AIOINPUTSEL_RXSEL_PCMI1_MASK | \
A2AIOINPUTSEL_RXSEL_PCMI2_MASK | \
A2AIOINPUTSEL_RXSEL_PCMI3_MASK | \
A2AIOINPUTSEL_RXSEL_IECI1_HDMIRX1)
/* INTC */
#define INTCHIM(m) (0x9028 + 0x80 * (m))
#define INTRBIM(m) (0x9030 + 0x80 * (m))
#define INTCHID(m) (0xa028 + 0x80 * (m))
#define INTRBID(m) (0xa030 + 0x80 * (m))
/* AIN(PCMINN) */
#define IPORTMXCTR1(n) (0x22000 + 0x400 * (n))
#define IPORTMXCTR1_LRSEL_MASK GENMASK(11, 10)
#define IPORTMXCTR1_LRSEL_RIGHT (0x0 << 10)
#define IPORTMXCTR1_LRSEL_LEFT (0x1 << 10)
#define IPORTMXCTR1_LRSEL_I2S (0x2 << 10)
#define IPORTMXCTR1_OUTBITSEL_MASK (0x800003U << 8)
#define IPORTMXCTR1_OUTBITSEL_32 (0x800000U << 8)
#define IPORTMXCTR1_OUTBITSEL_24 (0x000000U << 8)
#define IPORTMXCTR1_OUTBITSEL_20 (0x000001U << 8)
#define IPORTMXCTR1_OUTBITSEL_16 (0x000002U << 8)
#define IPORTMXCTR1_CHSEL_MASK GENMASK(6, 4)
#define IPORTMXCTR1_CHSEL_ALL (0x0 << 4)
#define IPORTMXCTR1_CHSEL_D0_D2 (0x1 << 4)
#define IPORTMXCTR1_CHSEL_D0 (0x2 << 4)
#define IPORTMXCTR1_CHSEL_D1 (0x3 << 4)
#define IPORTMXCTR1_CHSEL_D2 (0x4 << 4)
#define IPORTMXCTR1_CHSEL_DMIX (0x5 << 4)
#define IPORTMXCTR1_FSSEL_MASK GENMASK(3, 0)
#define IPORTMXCTR1_FSSEL_48 (0x0 << 0)
#define IPORTMXCTR1_FSSEL_96 (0x1 << 0)
#define IPORTMXCTR1_FSSEL_192 (0x2 << 0)
#define IPORTMXCTR1_FSSEL_32 (0x3 << 0)
#define IPORTMXCTR1_FSSEL_44_1 (0x4 << 0)
#define IPORTMXCTR1_FSSEL_88_2 (0x5 << 0)
#define IPORTMXCTR1_FSSEL_176_4 (0x6 << 0)
#define IPORTMXCTR1_FSSEL_16 (0x8 << 0)
#define IPORTMXCTR1_FSSEL_22_05 (0x9 << 0)
#define IPORTMXCTR1_FSSEL_24 (0xa << 0)
#define IPORTMXCTR1_FSSEL_8 (0xb << 0)
#define IPORTMXCTR1_FSSEL_11_025 (0xc << 0)
#define IPORTMXCTR1_FSSEL_12 (0xd << 0)
#define IPORTMXCTR2(n) (0x22004 + 0x400 * (n))
#define IPORTMXCTR2_ACLKSEL_MASK GENMASK(19, 16)
#define IPORTMXCTR2_ACLKSEL_A1 (0x0 << 16)
#define IPORTMXCTR2_ACLKSEL_F1 (0x1 << 16)
#define IPORTMXCTR2_ACLKSEL_A2 (0x2 << 16)
#define IPORTMXCTR2_ACLKSEL_F2 (0x3 << 16)
#define IPORTMXCTR2_ACLKSEL_A2PLL (0x4 << 16)
#define IPORTMXCTR2_ACLKSEL_RX1 (0x5 << 16)
#define IPORTMXCTR2_ACLKSEL_RX2 (0x6 << 16)
#define IPORTMXCTR2_MSSEL_MASK BIT(15)
#define IPORTMXCTR2_MSSEL_SLAVE (0x0 << 15)
#define IPORTMXCTR2_MSSEL_MASTER (0x1 << 15)
#define IPORTMXCTR2_EXTLSIFSSEL_MASK BIT(14)
#define IPORTMXCTR2_EXTLSIFSSEL_36 (0x0 << 14)
#define IPORTMXCTR2_EXTLSIFSSEL_24 (0x1 << 14)
#define IPORTMXCTR2_DACCKSEL_MASK GENMASK(9, 8)
#define IPORTMXCTR2_DACCKSEL_1_2 (0x0 << 8)
#define IPORTMXCTR2_DACCKSEL_1_3 (0x1 << 8)
#define IPORTMXCTR2_DACCKSEL_1_1 (0x2 << 8)
#define IPORTMXCTR2_DACCKSEL_2_3 (0x3 << 8)
#define IPORTMXCTR2_REQEN_MASK BIT(0)
#define IPORTMXCTR2_REQEN_DISABLE (0x0 << 0)
#define IPORTMXCTR2_REQEN_ENABLE (0x1 << 0)
#define IPORTMXCNTCTR(n) (0x22010 + 0x400 * (n))
#define IPORTMXCOUNTER(n) (0x22014 + 0x400 * (n))
#define IPORTMXCNTMONI(n) (0x22018 + 0x400 * (n))
#define IPORTMXACLKSEL0EX(n) (0x22020 + 0x400 * (n))
#define IPORTMXACLKSEL0EX_ACLKSEL0EX_MASK GENMASK(3, 0)
#define IPORTMXACLKSEL0EX_ACLKSEL0EX_INTERNAL (0x0 << 0)
#define IPORTMXACLKSEL0EX_ACLKSEL0EX_EXTERNAL (0xf << 0)
#define IPORTMXEXNOE(n) (0x22070 + 0x400 * (n))
#define IPORTMXEXNOE_PCMINOE_MASK BIT(0)
#define IPORTMXEXNOE_PCMINOE_OUTPUT (0x0 << 0)
#define IPORTMXEXNOE_PCMINOE_INPUT (0x1 << 0)
#define IPORTMXMASK(n) (0x22078 + 0x400 * (n))
#define IPORTMXMASK_IUXCKMSK_MASK GENMASK(18, 16)
#define IPORTMXMASK_IUXCKMSK_ON (0x0 << 16)
#define IPORTMXMASK_IUXCKMSK_OFF (0x7 << 16)
#define IPORTMXMASK_XCKMSK_MASK GENMASK(2, 0)
#define IPORTMXMASK_XCKMSK_ON (0x0 << 0)
#define IPORTMXMASK_XCKMSK_OFF (0x7 << 0)
#define IPORTMXRSTCTR(n) (0x2207c + 0x400 * (n))
#define IPORTMXRSTCTR_RSTPI_MASK BIT(7)
#define IPORTMXRSTCTR_RSTPI_RELEASE (0x0 << 7)
#define IPORTMXRSTCTR_RSTPI_RESET (0x1 << 7)
/* AIN(PBinMX) */
#define PBINMXCTR(n) (0x20200 + 0x40 * (n))
#define PBINMXCTR_NCONNECT_MASK BIT(15)
#define PBINMXCTR_NCONNECT_CONNECT (0x0 << 15)
#define PBINMXCTR_NCONNECT_DISCONNECT (0x1 << 15)
#define PBINMXCTR_INOUTSEL_MASK BIT(14)
#define PBINMXCTR_INOUTSEL_IN (0x0 << 14)
#define PBINMXCTR_INOUTSEL_OUT (0x1 << 14)
#define PBINMXCTR_PBINSEL_SHIFT (8)
#define PBINMXCTR_ENDIAN_MASK GENMASK(5, 4)
#define PBINMXCTR_ENDIAN_3210 (0x0 << 4)
#define PBINMXCTR_ENDIAN_0123 (0x1 << 4)
#define PBINMXCTR_ENDIAN_1032 (0x2 << 4)
#define PBINMXCTR_ENDIAN_2301 (0x3 << 4)
#define PBINMXCTR_MEMFMT_MASK GENMASK(3, 0)
#define PBINMXCTR_MEMFMT_D0 (0x0 << 0)
#define PBINMXCTR_MEMFMT_5_1CH_DMIX (0x1 << 0)
#define PBINMXCTR_MEMFMT_6CH (0x2 << 0)
#define PBINMXCTR_MEMFMT_4CH (0x3 << 0)
#define PBINMXCTR_MEMFMT_DMIX (0x4 << 0)
#define PBINMXCTR_MEMFMT_1CH (0x5 << 0)
#define PBINMXCTR_MEMFMT_16LR (0x6 << 0)
#define PBINMXCTR_MEMFMT_7_1CH (0x7 << 0)
#define PBINMXCTR_MEMFMT_7_1CH_DMIX (0x8 << 0)
#define PBINMXCTR_MEMFMT_STREAM (0xf << 0)
#define PBINMXPAUSECTR0(n) (0x20204 + 0x40 * (n))
#define PBINMXPAUSECTR1(n) (0x20208 + 0x40 * (n))
/* AOUT */
#define AOUTFADECTR0 0x40020
#define AOUTENCTR0 0x40040
#define AOUTENCTR1 0x40044
#define AOUTENCTR2 0x40048
#define AOUTRSTCTR0 0x40060
#define AOUTRSTCTR1 0x40064
#define AOUTRSTCTR2 0x40068
#define AOUTSRCRSTCTR0 0x400c0
#define AOUTSRCRSTCTR1 0x400c4
#define AOUTSRCRSTCTR2 0x400c8
/* AOUT PCMOUT has 5 slots, slot0-3: D0-3, slot4: DMIX */
#define OPORT_SLOT_MAX 5
/* AOUT(PCMOUTN) */
#define OPORTMXCTR1(n) (0x42000 + 0x400 * (n))
#define OPORTMXCTR1_I2SLRSEL_MASK (0x11 << 10)
#define OPORTMXCTR1_I2SLRSEL_RIGHT (0x00 << 10)
#define OPORTMXCTR1_I2SLRSEL_LEFT (0x01 << 10)
#define OPORTMXCTR1_I2SLRSEL_I2S (0x11 << 10)
#define OPORTMXCTR1_OUTBITSEL_MASK (0x800003U << 8)
#define OPORTMXCTR1_OUTBITSEL_32 (0x800000U << 8)
#define OPORTMXCTR1_OUTBITSEL_24 (0x000000U << 8)
#define OPORTMXCTR1_OUTBITSEL_20 (0x000001U << 8)
#define OPORTMXCTR1_OUTBITSEL_16 (0x000002U << 8)
#define OPORTMXCTR1_FSSEL_MASK GENMASK(3, 0)
#define OPORTMXCTR1_FSSEL_48 (0x0 << 0)
#define OPORTMXCTR1_FSSEL_96 (0x1 << 0)
#define OPORTMXCTR1_FSSEL_192 (0x2 << 0)
#define OPORTMXCTR1_FSSEL_32 (0x3 << 0)
#define OPORTMXCTR1_FSSEL_44_1 (0x4 << 0)
#define OPORTMXCTR1_FSSEL_88_2 (0x5 << 0)
#define OPORTMXCTR1_FSSEL_176_4 (0x6 << 0)
#define OPORTMXCTR1_FSSEL_16 (0x8 << 0)
#define OPORTMXCTR1_FSSEL_22_05 (0x9 << 0)
#define OPORTMXCTR1_FSSEL_24 (0xa << 0)
#define OPORTMXCTR1_FSSEL_8 (0xb << 0)
#define OPORTMXCTR1_FSSEL_11_025 (0xc << 0)
#define OPORTMXCTR1_FSSEL_12 (0xd << 0)
#define OPORTMXCTR2(n) (0x42004 + 0x400 * (n))
#define OPORTMXCTR2_ACLKSEL_MASK GENMASK(19, 16)
#define OPORTMXCTR2_ACLKSEL_A1 (0x0 << 16)
#define OPORTMXCTR2_ACLKSEL_F1 (0x1 << 16)
#define OPORTMXCTR2_ACLKSEL_A2 (0x2 << 16)
#define OPORTMXCTR2_ACLKSEL_F2 (0x3 << 16)
#define OPORTMXCTR2_ACLKSEL_A2PLL (0x4 << 16)
#define OPORTMXCTR2_ACLKSEL_RX1 (0x5 << 16)
#define OPORTMXCTR2_ACLKSEL_RX2 (0x6 << 16)
#define OPORTMXCTR2_MSSEL_MASK BIT(15)
#define OPORTMXCTR2_MSSEL_SLAVE (0x0 << 15)
#define OPORTMXCTR2_MSSEL_MASTER (0x1 << 15)
#define OPORTMXCTR2_EXTLSIFSSEL_MASK BIT(14)
#define OPORTMXCTR2_EXTLSIFSSEL_36 (0x0 << 14)
#define OPORTMXCTR2_EXTLSIFSSEL_24 (0x1 << 14)
#define OPORTMXCTR2_DACCKSEL_MASK GENMASK(9, 8)
#define OPORTMXCTR2_DACCKSEL_1_2 (0x0 << 8)
#define OPORTMXCTR2_DACCKSEL_1_3 (0x1 << 8)
#define OPORTMXCTR2_DACCKSEL_1_1 (0x2 << 8)
#define OPORTMXCTR2_DACCKSEL_2_3 (0x3 << 8)
#define OPORTMXCTR3(n) (0x42008 + 0x400 * (n))
#define OPORTMXCTR3_IECTHUR_MASK BIT(19)
#define OPORTMXCTR3_IECTHUR_IECOUT (0x0 << 19)
#define OPORTMXCTR3_IECTHUR_IECIN (0x1 << 19)
#define OPORTMXCTR3_SRCSEL_MASK GENMASK(18, 16)
#define OPORTMXCTR3_SRCSEL_PCM (0x0 << 16)
#define OPORTMXCTR3_SRCSEL_STREAM (0x1 << 16)
#define OPORTMXCTR3_SRCSEL_CDDTS (0x2 << 16)
#define OPORTMXCTR3_VALID_MASK BIT(12)
#define OPORTMXCTR3_VALID_PCM (0x0 << 12)
#define OPORTMXCTR3_VALID_STREAM (0x1 << 12)
#define OPORTMXCTR3_PMSEL_MASK BIT(3)
#define OPORTMXCTR3_PMSEL_MUTE (0x0 << 3)
#define OPORTMXCTR3_PMSEL_PAUSE (0x1 << 3)
#define OPORTMXCTR3_PMSW_MASK BIT(2)
#define OPORTMXCTR3_PMSW_MUTE_OFF (0x0 << 2)
#define OPORTMXCTR3_PMSW_MUTE_ON (0x1 << 2)
#define OPORTMXSRC1CTR(n) (0x4200c + 0x400 * (n))
#define OPORTMXSRC1CTR_FSIIPNUM_SHIFT (24)
#define OPORTMXSRC1CTR_THMODE_MASK BIT(23)
#define OPORTMXSRC1CTR_THMODE_SRC (0x0 << 23)
#define OPORTMXSRC1CTR_THMODE_BYPASS (0x1 << 23)
#define OPORTMXSRC1CTR_LOCK_MASK BIT(16)
#define OPORTMXSRC1CTR_LOCK_UNLOCK (0x0 << 16)
#define OPORTMXSRC1CTR_LOCK_LOCK (0x1 << 16)
#define OPORTMXSRC1CTR_SRCPATH_MASK BIT(15)
#define OPORTMXSRC1CTR_SRCPATH_BYPASS (0x0 << 15)
#define OPORTMXSRC1CTR_SRCPATH_CALC (0x1 << 15)
#define OPORTMXSRC1CTR_SYNC_MASK BIT(14)
#define OPORTMXSRC1CTR_SYNC_ASYNC (0x0 << 14)
#define OPORTMXSRC1CTR_SYNC_SYNC (0x1 << 14)
#define OPORTMXSRC1CTR_FSOCK_MASK GENMASK(11, 10)
#define OPORTMXSRC1CTR_FSOCK_44_1 (0x0 << 10)
#define OPORTMXSRC1CTR_FSOCK_48 (0x1 << 10)
#define OPORTMXSRC1CTR_FSOCK_32 (0x2 << 10)
#define OPORTMXSRC1CTR_FSICK_MASK GENMASK(9, 8)
#define OPORTMXSRC1CTR_FSICK_44_1 (0x0 << 8)
#define OPORTMXSRC1CTR_FSICK_48 (0x1 << 8)
#define OPORTMXSRC1CTR_FSICK_32 (0x2 << 8)
#define OPORTMXSRC1CTR_FSIIPSEL_MASK GENMASK(5, 4)
#define OPORTMXSRC1CTR_FSIIPSEL_INNER (0x0 << 4)
#define OPORTMXSRC1CTR_FSIIPSEL_OUTER (0x1 << 4)
#define OPORTMXSRC1CTR_FSISEL_MASK GENMASK(3, 0)
#define OPORTMXSRC1CTR_FSISEL_ACLK (0x0 << 0)
#define OPORTMXSRC1CTR_FSISEL_DD (0x1 << 0)
#define OPORTMXDSDMUTEDAT(n) (0x42020 + 0x400 * (n))
#define OPORTMXDXDFREQMODE(n) (0x42024 + 0x400 * (n))
#define OPORTMXDSDSEL(n) (0x42028 + 0x400 * (n))
#define OPORTMXDSDPORT(n) (0x4202c + 0x400 * (n))
#define OPORTMXACLKSEL0EX(n) (0x42030 + 0x400 * (n))
#define OPORTMXPATH(n) (0x42040 + 0x400 * (n))
#define OPORTMXSYNC(n) (0x42044 + 0x400 * (n))
#define OPORTMXREPET(n) (0x42050 + 0x400 * (n))
#define OPORTMXREPET_STRLENGTH_AC3 SBF_(IEC61937_FRM_STR_AC3, 16)
#define OPORTMXREPET_STRLENGTH_MPA SBF_(IEC61937_FRM_STR_MPA, 16)
#define OPORTMXREPET_STRLENGTH_MP3 SBF_(IEC61937_FRM_STR_MP3, 16)
#define OPORTMXREPET_STRLENGTH_DTS1 SBF_(IEC61937_FRM_STR_DTS1, 16)
#define OPORTMXREPET_STRLENGTH_DTS2 SBF_(IEC61937_FRM_STR_DTS2, 16)
#define OPORTMXREPET_STRLENGTH_DTS3 SBF_(IEC61937_FRM_STR_DTS3, 16)
#define OPORTMXREPET_STRLENGTH_AAC SBF_(IEC61937_FRM_STR_AAC, 16)
#define OPORTMXREPET_PMLENGTH_AC3 SBF_(IEC61937_FRM_PAU_AC3, 0)
#define OPORTMXREPET_PMLENGTH_MPA SBF_(IEC61937_FRM_PAU_MPA, 0)
#define OPORTMXREPET_PMLENGTH_MP3 SBF_(IEC61937_FRM_PAU_MP3, 0)
#define OPORTMXREPET_PMLENGTH_DTS1 SBF_(IEC61937_FRM_PAU_DTS1, 0)
#define OPORTMXREPET_PMLENGTH_DTS2 SBF_(IEC61937_FRM_PAU_DTS2, 0)
#define OPORTMXREPET_PMLENGTH_DTS3 SBF_(IEC61937_FRM_PAU_DTS3, 0)
#define OPORTMXREPET_PMLENGTH_AAC SBF_(IEC61937_FRM_PAU_AAC, 0)
#define OPORTMXPAUDAT(n) (0x42054 + 0x400 * (n))
#define OPORTMXPAUDAT_PAUSEPC_CMN (IEC61937_PC_PAUSE << 16)
#define OPORTMXPAUDAT_PAUSEPD_AC3 (IEC61937_FRM_PAU_AC3 * 4)
#define OPORTMXPAUDAT_PAUSEPD_MPA (IEC61937_FRM_PAU_MPA * 4)
#define OPORTMXPAUDAT_PAUSEPD_MP3 (IEC61937_FRM_PAU_MP3 * 4)
#define OPORTMXPAUDAT_PAUSEPD_DTS1 (IEC61937_FRM_PAU_DTS1 * 4)
#define OPORTMXPAUDAT_PAUSEPD_DTS2 (IEC61937_FRM_PAU_DTS2 * 4)
#define OPORTMXPAUDAT_PAUSEPD_DTS3 (IEC61937_FRM_PAU_DTS3 * 4)
#define OPORTMXPAUDAT_PAUSEPD_AAC (IEC61937_FRM_PAU_AAC * 4)
#define OPORTMXRATE_I(n) (0x420e4 + 0x400 * (n))
#define OPORTMXRATE_I_EQU_MASK BIT(31)
#define OPORTMXRATE_I_EQU_NOTEQUAL (0x0 << 31)
#define OPORTMXRATE_I_EQU_EQUAL (0x1 << 31)
#define OPORTMXRATE_I_SRCBPMD_MASK BIT(29)
#define OPORTMXRATE_I_SRCBPMD_BYPASS (0x0 << 29)
#define OPORTMXRATE_I_SRCBPMD_SRC (0x1 << 29)
#define OPORTMXRATE_I_LRCKSTP_MASK BIT(24)
#define OPORTMXRATE_I_LRCKSTP_START (0x0 << 24)
#define OPORTMXRATE_I_LRCKSTP_STOP (0x1 << 24)
#define OPORTMXRATE_I_ACLKSRC_MASK GENMASK(15, 12)
#define OPORTMXRATE_I_ACLKSRC_APLL (0x0 << 12)
#define OPORTMXRATE_I_ACLKSRC_USB (0x1 << 12)
#define OPORTMXRATE_I_ACLKSRC_HSC (0x3 << 12)
/* if OPORTMXRATE_I_ACLKSRC_APLL */
#define OPORTMXRATE_I_ACLKSEL_MASK GENMASK(11, 8)
#define OPORTMXRATE_I_ACLKSEL_APLLA1 (0x0 << 8)
#define OPORTMXRATE_I_ACLKSEL_APLLF1 (0x1 << 8)
#define OPORTMXRATE_I_ACLKSEL_APLLA2 (0x2 << 8)
#define OPORTMXRATE_I_ACLKSEL_APLLF2 (0x3 << 8)
#define OPORTMXRATE_I_ACLKSEL_APLL (0x4 << 8)
#define OPORTMXRATE_I_ACLKSEL_HDMI1 (0x5 << 8)
#define OPORTMXRATE_I_ACLKSEL_HDMI2 (0x6 << 8)
#define OPORTMXRATE_I_ACLKSEL_AI1ADCCK (0xc << 8)
#define OPORTMXRATE_I_ACLKSEL_AI2ADCCK (0xd << 8)
#define OPORTMXRATE_I_ACLKSEL_AI3ADCCK (0xe << 8)
#define OPORTMXRATE_I_MCKSEL_MASK GENMASK(7, 4)
#define OPORTMXRATE_I_MCKSEL_36 (0x0 << 4)
#define OPORTMXRATE_I_MCKSEL_33 (0x1 << 4)
#define OPORTMXRATE_I_MCKSEL_HSC27 (0xb << 4)
#define OPORTMXRATE_I_FSSEL_MASK GENMASK(3, 0)
#define OPORTMXRATE_I_FSSEL_48 (0x0 << 0)
#define OPORTMXRATE_I_FSSEL_96 (0x1 << 0)
#define OPORTMXRATE_I_FSSEL_192 (0x2 << 0)
#define OPORTMXRATE_I_FSSEL_32 (0x3 << 0)
#define OPORTMXRATE_I_FSSEL_44_1 (0x4 << 0)
#define OPORTMXRATE_I_FSSEL_88_2 (0x5 << 0)
#define OPORTMXRATE_I_FSSEL_176_4 (0x6 << 0)
#define OPORTMXRATE_I_FSSEL_16 (0x8 << 0)
#define OPORTMXRATE_I_FSSEL_22_05 (0x9 << 0)
#define OPORTMXRATE_I_FSSEL_24 (0xa << 0)
#define OPORTMXRATE_I_FSSEL_8 (0xb << 0)
#define OPORTMXRATE_I_FSSEL_11_025 (0xc << 0)
#define OPORTMXRATE_I_FSSEL_12 (0xd << 0)
#define OPORTMXEXNOE(n) (0x420f0 + 0x400 * (n))
#define OPORTMXMASK(n) (0x420f8 + 0x400 * (n))
#define OPORTMXMASK_IUDXMSK_MASK GENMASK(28, 24)
#define OPORTMXMASK_IUDXMSK_ON (0x00 << 24)
#define OPORTMXMASK_IUDXMSK_OFF (0x1f << 24)
#define OPORTMXMASK_IUXCKMSK_MASK GENMASK(18, 16)
#define OPORTMXMASK_IUXCKMSK_ON (0x0 << 16)
#define OPORTMXMASK_IUXCKMSK_OFF (0x7 << 16)
#define OPORTMXMASK_DXMSK_MASK GENMASK(12, 8)
#define OPORTMXMASK_DXMSK_ON (0x00 << 8)
#define OPORTMXMASK_DXMSK_OFF (0x1f << 8)
#define OPORTMXMASK_XCKMSK_MASK GENMASK(2, 0)
#define OPORTMXMASK_XCKMSK_ON (0x0 << 0)
#define OPORTMXMASK_XCKMSK_OFF (0x7 << 0)
#define OPORTMXDEBUG(n) (0x420fc + 0x400 * (n))
#define OPORTMXTYVOLPARA1(n, m) (0x42100 + 0x400 * (n) + 0x20 * (m))
#define OPORTMXTYVOLPARA1_SLOPEU_MASK GENMASK(31, 16)
#define OPORTMXTYVOLPARA2(n, m) (0x42104 + 0x400 * (n) + 0x20 * (m))
#define OPORTMXTYVOLPARA2_FADE_MASK GENMASK(17, 16)
#define OPORTMXTYVOLPARA2_FADE_NOOP (0x0 << 16)
#define OPORTMXTYVOLPARA2_FADE_FADEOUT (0x1 << 16)
#define OPORTMXTYVOLPARA2_FADE_FADEIN (0x2 << 16)
#define OPORTMXTYVOLPARA2_TARGET_MASK GENMASK(15, 0)
#define OPORTMXTYVOLGAINSTATUS(n, m) (0x42108 + 0x400 * (n) + 0x20 * (m))
#define OPORTMXTYVOLGAINSTATUS_CUR_MASK GENMASK(15, 0)
#define OPORTMXTYSLOTCTR(n, m) (0x42114 + 0x400 * (n) + 0x20 * (m))
#define OPORTMXTYSLOTCTR_MODE BIT(15)
#define OPORTMXTYSLOTCTR_SLOTSEL_MASK GENMASK(11, 8)
#define OPORTMXTYSLOTCTR_SLOTSEL_SLOT0 (0x8 << 8)
#define OPORTMXTYSLOTCTR_SLOTSEL_SLOT1 (0x9 << 8)
#define OPORTMXTYSLOTCTR_SLOTSEL_SLOT2 (0xa << 8)
#define OPORTMXTYSLOTCTR_SLOTSEL_SLOT3 (0xb << 8)
#define OPORTMXTYSLOTCTR_SLOTSEL_SLOT4 (0xc << 8)
#define OPORTMXT0SLOTCTR_MUTEOFF_MASK BIT(1)
#define OPORTMXT0SLOTCTR_MUTEOFF_MUTE (0x0 << 1)
#define OPORTMXT0SLOTCTR_MUTEOFF_UNMUTE (0x1 << 1)
#define OPORTMXTYRSTCTR(n, m) (0x4211c + 0x400 * (n) + 0x20 * (m))
#define OPORTMXT0RSTCTR_RST_MASK BIT(1)
#define OPORTMXT0RSTCTR_RST_OFF (0x0 << 1)
#define OPORTMXT0RSTCTR_RST_ON (0x1 << 1)
#define SBF_(frame, shift) (((frame) * 2 - 1) << shift)
/* AOUT(PBoutMX) */
#define PBOUTMXCTR0(n) (0x40200 + 0x40 * (n))
#define PBOUTMXCTR0_ENDIAN_MASK GENMASK(5, 4)
#define PBOUTMXCTR0_ENDIAN_3210 (0x0 << 4)
#define PBOUTMXCTR0_ENDIAN_0123 (0x1 << 4)
#define PBOUTMXCTR0_ENDIAN_1032 (0x2 << 4)
#define PBOUTMXCTR0_ENDIAN_2301 (0x3 << 4)
#define PBOUTMXCTR0_MEMFMT_MASK GENMASK(3, 0)
#define PBOUTMXCTR0_MEMFMT_10CH (0x0 << 0)
#define PBOUTMXCTR0_MEMFMT_8CH (0x1 << 0)
#define PBOUTMXCTR0_MEMFMT_6CH (0x2 << 0)
#define PBOUTMXCTR0_MEMFMT_4CH (0x3 << 0)
#define PBOUTMXCTR0_MEMFMT_2CH (0x4 << 0)
#define PBOUTMXCTR0_MEMFMT_STREAM (0x5 << 0)
#define PBOUTMXCTR0_MEMFMT_1CH (0x6 << 0)
#define PBOUTMXCTR1(n) (0x40204 + 0x40 * (n))
#define PBOUTMXINTCTR(n) (0x40208 + 0x40 * (n))
/* A2D(subsystem) */
#define CDA2D_STRT0 0x10000
#define CDA2D_STRT0_STOP_MASK BIT(31)
#define CDA2D_STRT0_STOP_START (0x0 << 31)
#define CDA2D_STRT0_STOP_STOP (0x1 << 31)
#define CDA2D_STAT0 0x10020
#define CDA2D_TEST 0x100a0
#define CDA2D_TEST_DDR_MODE_MASK GENMASK(3, 2)
#define CDA2D_TEST_DDR_MODE_EXTON0 (0x0 << 2)
#define CDA2D_TEST_DDR_MODE_EXTOFF1 (0x3 << 2)
#define CDA2D_STRTADRSLOAD 0x100b0
#define CDA2D_CHMXCTRL1(n) (0x12000 + 0x80 * (n))
#define CDA2D_CHMXCTRL1_INDSIZE_MASK BIT(0)
#define CDA2D_CHMXCTRL1_INDSIZE_FINITE (0x0 << 0)
#define CDA2D_CHMXCTRL1_INDSIZE_INFINITE (0x1 << 0)
#define CDA2D_CHMXCTRL2(n) (0x12004 + 0x80 * (n))
#define CDA2D_CHMXSRCAMODE(n) (0x12020 + 0x80 * (n))
#define CDA2D_CHMXDSTAMODE(n) (0x12024 + 0x80 * (n))
#define CDA2D_CHMXAMODE_ENDIAN_MASK GENMASK(17, 16)
#define CDA2D_CHMXAMODE_ENDIAN_3210 (0x0 << 16)
#define CDA2D_CHMXAMODE_ENDIAN_0123 (0x1 << 16)
#define CDA2D_CHMXAMODE_ENDIAN_1032 (0x2 << 16)
#define CDA2D_CHMXAMODE_ENDIAN_2301 (0x3 << 16)
#define CDA2D_CHMXAMODE_RSSEL_SHIFT (8)
#define CDA2D_CHMXAMODE_AUPDT_MASK GENMASK(5, 4)
#define CDA2D_CHMXAMODE_AUPDT_INC (0x0 << 4)
#define CDA2D_CHMXAMODE_AUPDT_FIX (0x2 << 4)
#define CDA2D_CHMXAMODE_TYPE_MASK GENMASK(3, 2)
#define CDA2D_CHMXAMODE_TYPE_NORMAL (0x0 << 2)
#define CDA2D_CHMXAMODE_TYPE_RING (0x1 << 2)
#define CDA2D_CHMXSRCSTRTADRS(n) (0x12030 + 0x80 * (n))
#define CDA2D_CHMXSRCSTRTADRSU(n) (0x12034 + 0x80 * (n))
#define CDA2D_CHMXDSTSTRTADRS(n) (0x12038 + 0x80 * (n))
#define CDA2D_CHMXDSTSTRTADRSU(n) (0x1203c + 0x80 * (n))
/* A2D(ring buffer) */
#define CDA2D_RBFLUSH0 0x10040
#define CDA2D_RBADRSLOAD 0x100b4
#define CDA2D_RDPTRLOAD 0x100b8
#define CDA2D_RDPTRLOAD_LSFLAG_LOAD (0x0 << 31)
#define CDA2D_RDPTRLOAD_LSFLAG_STORE (0x1 << 31)
#define CDA2D_WRPTRLOAD 0x100bc
#define CDA2D_WRPTRLOAD_LSFLAG_LOAD (0x0 << 31)
#define CDA2D_WRPTRLOAD_LSFLAG_STORE (0x1 << 31)
#define CDA2D_RBMXBGNADRS(n) (0x14000 + 0x80 * (n))
#define CDA2D_RBMXBGNADRSU(n) (0x14004 + 0x80 * (n))
#define CDA2D_RBMXENDADRS(n) (0x14008 + 0x80 * (n))
#define CDA2D_RBMXENDADRSU(n) (0x1400c + 0x80 * (n))
#define CDA2D_RBMXBTH(n) (0x14038 + 0x80 * (n))
#define CDA2D_RBMXRTH(n) (0x1403c + 0x80 * (n))
#define CDA2D_RBMXRDPTR(n) (0x14020 + 0x80 * (n))
#define CDA2D_RBMXRDPTRU(n) (0x14024 + 0x80 * (n))
#define CDA2D_RBMXWRPTR(n) (0x14028 + 0x80 * (n))
#define CDA2D_RBMXWRPTRU(n) (0x1402c + 0x80 * (n))
#define CDA2D_RBMXPTRU_PTRU_MASK GENMASK(1, 0)
#define CDA2D_RBMXCNFG(n) (0x14030 + 0x80 * (n))
#define CDA2D_RBMXIR(n) (0x14014 + 0x80 * (n))
#define CDA2D_RBMXIE(n) (0x14018 + 0x80 * (n))
#define CDA2D_RBMXID(n) (0x1401c + 0x80 * (n))
#define CDA2D_RBMXIX_SPACE BIT(3)
#define CDA2D_RBMXIX_REMAIN BIT(4)
#endif /* SND_UNIPHIER_AIO_REG_H__ */
+354
View File
@@ -0,0 +1,354 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Socionext UniPhier AIO ALSA driver.
*
* Copyright (c) 2016-2018 Socionext Inc.
*/
#ifndef SND_UNIPHIER_AIO_H__
#define SND_UNIPHIER_AIO_H__
#include <linux/spinlock.h>
#include <linux/types.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
struct platform_device;
enum ID_PORT_TYPE {
PORT_TYPE_UNKNOWN,
PORT_TYPE_I2S,
PORT_TYPE_SPDIF,
PORT_TYPE_EVE,
PORT_TYPE_CONV,
};
enum ID_PORT_DIR {
PORT_DIR_OUTPUT,
PORT_DIR_INPUT,
};
enum IEC61937_PC {
IEC61937_PC_AC3 = 0x0001,
IEC61937_PC_PAUSE = 0x0003,
IEC61937_PC_MPA = 0x0004,
IEC61937_PC_MP3 = 0x0005,
IEC61937_PC_DTS1 = 0x000b,
IEC61937_PC_DTS2 = 0x000c,
IEC61937_PC_DTS3 = 0x000d,
IEC61937_PC_AAC = 0x0007,
};
/* IEC61937 Repetition period of data-burst in IEC60958 frames */
#define IEC61937_FRM_STR_AC3 1536
#define IEC61937_FRM_STR_MPA 1152
#define IEC61937_FRM_STR_MP3 1152
#define IEC61937_FRM_STR_DTS1 512
#define IEC61937_FRM_STR_DTS2 1024
#define IEC61937_FRM_STR_DTS3 2048
#define IEC61937_FRM_STR_AAC 1024
/* IEC61937 Repetition period of Pause data-burst in IEC60958 frames */
#define IEC61937_FRM_PAU_AC3 3
#define IEC61937_FRM_PAU_MPA 32
#define IEC61937_FRM_PAU_MP3 32
#define IEC61937_FRM_PAU_DTS1 3
#define IEC61937_FRM_PAU_DTS2 3
#define IEC61937_FRM_PAU_DTS3 3
#define IEC61937_FRM_PAU_AAC 32
/* IEC61937 Pa and Pb */
#define IEC61937_HEADER_SIGN 0x1f4e72f8
#define AUD_HW_PCMIN1 0
#define AUD_HW_PCMIN2 1
#define AUD_HW_PCMIN3 2
#define AUD_HW_IECIN1 3
#define AUD_HW_DIECIN1 4
#define AUD_NAME_PCMIN1 "aio-pcmin1"
#define AUD_NAME_PCMIN2 "aio-pcmin2"
#define AUD_NAME_PCMIN3 "aio-pcmin3"
#define AUD_NAME_IECIN1 "aio-iecin1"
#define AUD_NAME_DIECIN1 "aio-diecin1"
#define AUD_HW_HPCMOUT1 0
#define AUD_HW_PCMOUT1 1
#define AUD_HW_PCMOUT2 2
#define AUD_HW_PCMOUT3 3
#define AUD_HW_EPCMOUT1 4
#define AUD_HW_EPCMOUT2 5
#define AUD_HW_EPCMOUT3 6
#define AUD_HW_EPCMOUT6 9
#define AUD_HW_HIECOUT1 10
#define AUD_HW_IECOUT1 11
#define AUD_HW_CMASTER 31
#define AUD_NAME_HPCMOUT1 "aio-hpcmout1"
#define AUD_NAME_PCMOUT1 "aio-pcmout1"
#define AUD_NAME_PCMOUT2 "aio-pcmout2"
#define AUD_NAME_PCMOUT3 "aio-pcmout3"
#define AUD_NAME_EPCMOUT1 "aio-epcmout1"
#define AUD_NAME_EPCMOUT2 "aio-epcmout2"
#define AUD_NAME_EPCMOUT3 "aio-epcmout3"
#define AUD_NAME_EPCMOUT6 "aio-epcmout6"
#define AUD_NAME_HIECOUT1 "aio-hiecout1"
#define AUD_NAME_IECOUT1 "aio-iecout1"
#define AUD_NAME_CMASTER "aio-cmaster"
#define AUD_NAME_HIECCOMPOUT1 "aio-hieccompout1"
#define AUD_NAME_IECCOMPOUT1 "aio-ieccompout1"
#define AUD_GNAME_HDMI "aio-hdmi"
#define AUD_GNAME_LINE "aio-line"
#define AUD_GNAME_AUX "aio-aux"
#define AUD_GNAME_IEC "aio-iec"
#define AUD_CLK_IO 0
#define AUD_CLK_A1 1
#define AUD_CLK_F1 2
#define AUD_CLK_A2 3
#define AUD_CLK_F2 4
#define AUD_CLK_A 5
#define AUD_CLK_F 6
#define AUD_CLK_APLL 7
#define AUD_CLK_RX0 8
#define AUD_CLK_USB0 9
#define AUD_CLK_HSC0 10
#define AUD_PLL_A1 0
#define AUD_PLL_F1 1
#define AUD_PLL_A2 2
#define AUD_PLL_F2 3
#define AUD_PLL_APLL 4
#define AUD_PLL_RX0 5
#define AUD_PLL_USB0 6
#define AUD_PLL_HSC0 7
#define AUD_PLLDIV_1_2 0
#define AUD_PLLDIV_1_3 1
#define AUD_PLLDIV_1_1 2
#define AUD_PLLDIV_2_3 3
#define AUD_VOL_INIT 0x4000 /* +0dB */
#define AUD_VOL_MAX 0xffff /* +6dB */
#define AUD_VOL_FADE_TIME 20 /* 20ms */
#define AUD_RING_SIZE (128 * 1024)
#define AUD_MIN_FRAGMENT 4
#define AUD_MAX_FRAGMENT 8
#define AUD_MIN_FRAGMENT_SIZE (4 * 1024)
#define AUD_MAX_FRAGMENT_SIZE (16 * 1024)
/* max 5 slots, 10 channels, 2 channel in 1 slot */
#define AUD_MAX_SLOTSEL 5
/*
* This is a selector for virtual register map of AIO.
*
* map: Specify the index of virtual register map.
* hw : Specify the ID of real register map, selector uses this value.
* A meaning of this value depends specification of SoC.
*/
struct uniphier_aio_selector {
int map;
int hw;
};
/**
* 'SoftWare MAPping' setting of UniPhier AIO registers.
*
* We have to setup 'virtual' register maps to access 'real' registers of AIO.
* This feature is legacy and meaningless but AIO needs this to work.
*
* Each hardware blocks have own virtual register maps as following:
*
* Address Virtual Real
* ------- --------- ---------------
* 0x12000 DMAC map0 --> [selector] --> DMAC hardware 3
* 0x12080 DMAC map1 --> [selector] --> DMAC hardware 1
* ...
* 0x42000 Port map0 --> [selector] --> Port hardware 1
* 0x42400 Port map1 --> [selector] --> Port hardware 2
* ...
*
* ch : Input or output channel of DMAC
* rb : Ring buffer
* iport: PCM input port
* iif : Input interface
* oport: PCM output port
* oif : Output interface
* och : Output channel of DMAC for sampling rate converter
*
* These are examples for sound data paths:
*
* For caputure device:
* (outer of AIO) -> iport -> iif -> ch -> rb -> (CPU)
* For playback device:
* (CPU) -> rb -> ch -> oif -> oport -> (outer of AIO)
* For sampling rate converter device:
* (CPU) -> rb -> ch -> oif -> (HW SRC) -> iif -> och -> orb -> (CPU)
*/
struct uniphier_aio_swmap {
int type;
int dir;
struct uniphier_aio_selector ch;
struct uniphier_aio_selector rb;
struct uniphier_aio_selector iport;
struct uniphier_aio_selector iif;
struct uniphier_aio_selector oport;
struct uniphier_aio_selector oif;
struct uniphier_aio_selector och;
};
struct uniphier_aio_spec {
const char *name;
const char *gname;
struct uniphier_aio_swmap swm;
};
struct uniphier_aio_pll {
bool enable;
unsigned int freq;
};
struct uniphier_aio_chip_spec {
const struct uniphier_aio_spec *specs;
int num_specs;
const struct uniphier_aio_pll *plls;
int num_plls;
struct snd_soc_dai_driver *dais;
int num_dais;
/* DMA access mode, this is workaround for DMA hungup */
int addr_ext;
};
struct uniphier_aio_sub {
struct uniphier_aio *aio;
/* Guard sub->rd_offs and wr_offs from IRQ handler. */
spinlock_t lock;
const struct uniphier_aio_swmap *swm;
const struct uniphier_aio_spec *spec;
/* For PCM audio */
struct snd_pcm_substream *substream;
struct snd_pcm_hw_params params;
int vol;
/* For compress audio */
struct snd_compr_stream *cstream;
struct snd_compr_params cparams;
unsigned char *compr_area;
dma_addr_t compr_addr;
size_t compr_bytes;
int pass_through;
enum IEC61937_PC iec_pc;
bool iec_header;
/* Both PCM and compress audio */
bool use_mmap;
int setting;
int running;
u64 rd_offs;
u64 wr_offs;
u32 threshold;
u64 rd_org;
u64 wr_org;
u64 rd_total;
u64 wr_total;
};
struct uniphier_aio {
struct uniphier_aio_chip *chip;
struct uniphier_aio_sub sub[2];
unsigned int fmt;
/* Set one of AUD_CLK_X */
int clk_in;
int clk_out;
/* Set one of AUD_PLL_X */
int pll_in;
int pll_out;
/* Set one of AUD_PLLDIV_X */
int plldiv;
};
struct uniphier_aio_chip {
struct platform_device *pdev;
const struct uniphier_aio_chip_spec *chip_spec;
struct uniphier_aio *aios;
int num_aios;
int num_wup_aios;
struct uniphier_aio_pll *plls;
int num_plls;
struct clk *clk;
struct reset_control *rst;
struct regmap *regmap;
struct regmap *regmap_sg;
int active;
};
static inline struct uniphier_aio *uniphier_priv(struct snd_soc_dai *dai)
{
struct uniphier_aio_chip *chip = snd_soc_dai_get_drvdata(dai);
return &chip->aios[dai->id];
}
int uniphier_aiodma_soc_register_platform(struct platform_device *pdev);
extern const struct snd_compress_ops uniphier_aio_compress_ops;
int uniphier_aio_probe(struct platform_device *pdev);
void uniphier_aio_remove(struct platform_device *pdev);
extern const struct snd_soc_dai_ops uniphier_aio_i2s_ld11_ops;
extern const struct snd_soc_dai_ops uniphier_aio_i2s_pxs2_ops;
extern const struct snd_soc_dai_ops uniphier_aio_spdif_ld11_ops;
extern const struct snd_soc_dai_ops uniphier_aio_spdif_ld11_ops2;
extern const struct snd_soc_dai_ops uniphier_aio_spdif_pxs2_ops;
extern const struct snd_soc_dai_ops uniphier_aio_spdif_pxs2_ops2;
u64 aio_rb_cnt(struct uniphier_aio_sub *sub);
u64 aio_rbt_cnt_to_end(struct uniphier_aio_sub *sub);
u64 aio_rb_space(struct uniphier_aio_sub *sub);
u64 aio_rb_space_to_end(struct uniphier_aio_sub *sub);
void aio_iecout_set_enable(struct uniphier_aio_chip *chip, bool enable);
int aio_chip_set_pll(struct uniphier_aio_chip *chip, int pll_id,
unsigned int freq);
void aio_chip_init(struct uniphier_aio_chip *chip);
int aio_init(struct uniphier_aio_sub *sub);
void aio_port_reset(struct uniphier_aio_sub *sub);
int aio_port_set_param(struct uniphier_aio_sub *sub, int pass_through,
const struct snd_pcm_hw_params *params);
void aio_port_set_enable(struct uniphier_aio_sub *sub, int enable);
int aio_port_get_volume(struct uniphier_aio_sub *sub);
void aio_port_set_volume(struct uniphier_aio_sub *sub, int vol);
int aio_if_set_param(struct uniphier_aio_sub *sub, int pass_through);
int aio_oport_set_stream_type(struct uniphier_aio_sub *sub,
enum IEC61937_PC pc);
void aio_src_reset(struct uniphier_aio_sub *sub);
int aio_src_set_param(struct uniphier_aio_sub *sub,
const struct snd_pcm_hw_params *params);
int aio_srcif_set_param(struct uniphier_aio_sub *sub);
int aio_srcch_set_param(struct uniphier_aio_sub *sub);
void aio_srcch_set_enable(struct uniphier_aio_sub *sub, int enable);
int aiodma_ch_set_param(struct uniphier_aio_sub *sub);
void aiodma_ch_set_enable(struct uniphier_aio_sub *sub, int enable);
int aiodma_rb_set_threshold(struct uniphier_aio_sub *sub, u64 size, u32 th);
int aiodma_rb_set_buffer(struct uniphier_aio_sub *sub, u64 start, u64 end,
int period);
void aiodma_rb_sync(struct uniphier_aio_sub *sub, u64 start, u64 size,
int period);
bool aiodma_rb_is_irq(struct uniphier_aio_sub *sub);
void aiodma_rb_clear_irq(struct uniphier_aio_sub *sub);
#endif /* SND_UNIPHIER_AIO_H__ */
+569
View File
@@ -0,0 +1,569 @@
// SPDX-License-Identifier: GPL-2.0
//
// Socionext UniPhier EVEA ADC/DAC codec driver.
//
// Copyright (c) 2016-2017 Socionext Inc.
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#define DRV_NAME "evea"
#define EVEA_RATES SNDRV_PCM_RATE_48000
#define EVEA_FORMATS SNDRV_PCM_FMTBIT_S32_LE
#define AADCPOW(n) (0x0078 + 0x04 * (n))
#define AADCPOW_AADC_POWD BIT(0)
#define ALINSW1 0x0088
#define ALINSW1_SEL1_SHIFT 3
#define AHPOUTPOW 0x0098
#define AHPOUTPOW_HP_ON BIT(4)
#define ALINEPOW 0x009c
#define ALINEPOW_LIN2_POWD BIT(3)
#define ALINEPOW_LIN1_POWD BIT(4)
#define ALO1OUTPOW 0x00a8
#define ALO1OUTPOW_LO1_ON BIT(4)
#define ALO2OUTPOW 0x00ac
#define ALO2OUTPOW_ADAC2_MUTE BIT(0)
#define ALO2OUTPOW_LO2_ON BIT(4)
#define AANAPOW 0x00b8
#define AANAPOW_A_POWD BIT(4)
#define ADACSEQ1(n) (0x0144 + 0x40 * (n))
#define ADACSEQ1_MMUTE BIT(1)
#define ADACSEQ2(n) (0x0160 + 0x40 * (n))
#define ADACSEQ2_ADACIN_FIX BIT(0)
#define ADAC1ODC 0x0200
#define ADAC1ODC_HP_DIS_RES_MASK GENMASK(2, 1)
#define ADAC1ODC_HP_DIS_RES_OFF (0x0 << 1)
#define ADAC1ODC_HP_DIS_RES_ON (0x3 << 1)
#define ADAC1ODC_ADAC_RAMPCLT_MASK GENMASK(8, 7)
#define ADAC1ODC_ADAC_RAMPCLT_NORMAL (0x0 << 7)
#define ADAC1ODC_ADAC_RAMPCLT_REDUCE (0x1 << 7)
struct evea_priv {
struct clk *clk, *clk_exiv;
struct reset_control *rst, *rst_exiv, *rst_adamv;
struct regmap *regmap;
int switch_lin;
int switch_lo;
int switch_hp;
};
static const char * const linsw1_sel1_text[] = {
"LIN1", "LIN2", "LIN3"
};
static SOC_ENUM_SINGLE_DECL(linsw1_sel1_enum,
ALINSW1, ALINSW1_SEL1_SHIFT,
linsw1_sel1_text);
static const struct snd_kcontrol_new linesw1_mux[] = {
SOC_DAPM_ENUM("Line In 1 Source", linsw1_sel1_enum),
};
static const struct snd_soc_dapm_widget evea_widgets[] = {
SND_SOC_DAPM_ADC("ADC", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("Line In 1 Mux", SND_SOC_NOPM, 0, 0, linesw1_mux),
SND_SOC_DAPM_INPUT("LIN1_LP"),
SND_SOC_DAPM_INPUT("LIN1_RP"),
SND_SOC_DAPM_INPUT("LIN2_LP"),
SND_SOC_DAPM_INPUT("LIN2_RP"),
SND_SOC_DAPM_INPUT("LIN3_LP"),
SND_SOC_DAPM_INPUT("LIN3_RP"),
SND_SOC_DAPM_DAC("DAC HP", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DAC LO1", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DAC LO2", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_OUTPUT("HP1_L"),
SND_SOC_DAPM_OUTPUT("HP1_R"),
SND_SOC_DAPM_OUTPUT("LO2_L"),
SND_SOC_DAPM_OUTPUT("LO2_R"),
};
static const struct snd_soc_dapm_route evea_routes[] = {
{ "Line In 1", NULL, "ADC" },
{ "ADC", NULL, "Line In 1 Mux" },
{ "Line In 1 Mux", "LIN1", "LIN1_LP" },
{ "Line In 1 Mux", "LIN1", "LIN1_RP" },
{ "Line In 1 Mux", "LIN2", "LIN2_LP" },
{ "Line In 1 Mux", "LIN2", "LIN2_RP" },
{ "Line In 1 Mux", "LIN3", "LIN3_LP" },
{ "Line In 1 Mux", "LIN3", "LIN3_RP" },
{ "DAC HP", NULL, "Headphone 1" },
{ "DAC LO1", NULL, "Line Out 1" },
{ "DAC LO2", NULL, "Line Out 2" },
{ "HP1_L", NULL, "DAC HP" },
{ "HP1_R", NULL, "DAC HP" },
{ "LO2_L", NULL, "DAC LO2" },
{ "LO2_R", NULL, "DAC LO2" },
};
static void evea_set_power_state_on(struct evea_priv *evea)
{
struct regmap *map = evea->regmap;
regmap_update_bits(map, AANAPOW, AANAPOW_A_POWD,
AANAPOW_A_POWD);
regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK,
ADAC1ODC_HP_DIS_RES_ON);
regmap_update_bits(map, ADAC1ODC, ADAC1ODC_ADAC_RAMPCLT_MASK,
ADAC1ODC_ADAC_RAMPCLT_REDUCE);
regmap_update_bits(map, ADACSEQ2(0), ADACSEQ2_ADACIN_FIX, 0);
regmap_update_bits(map, ADACSEQ2(1), ADACSEQ2_ADACIN_FIX, 0);
regmap_update_bits(map, ADACSEQ2(2), ADACSEQ2_ADACIN_FIX, 0);
}
static void evea_set_power_state_off(struct evea_priv *evea)
{
struct regmap *map = evea->regmap;
regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK,
ADAC1ODC_HP_DIS_RES_ON);
regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE,
ADACSEQ1_MMUTE);
regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE,
ADACSEQ1_MMUTE);
regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE,
ADACSEQ1_MMUTE);
regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, 0);
regmap_update_bits(map, ALO2OUTPOW, ALO2OUTPOW_LO2_ON, 0);
regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, 0);
}
static int evea_update_switch_lin(struct evea_priv *evea)
{
struct regmap *map = evea->regmap;
if (evea->switch_lin) {
regmap_update_bits(map, ALINEPOW,
ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD,
ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD);
regmap_update_bits(map, AADCPOW(0), AADCPOW_AADC_POWD,
AADCPOW_AADC_POWD);
regmap_update_bits(map, AADCPOW(1), AADCPOW_AADC_POWD,
AADCPOW_AADC_POWD);
} else {
regmap_update_bits(map, AADCPOW(0), AADCPOW_AADC_POWD, 0);
regmap_update_bits(map, AADCPOW(1), AADCPOW_AADC_POWD, 0);
regmap_update_bits(map, ALINEPOW,
ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD, 0);
}
return 0;
}
static int evea_update_switch_lo(struct evea_priv *evea)
{
struct regmap *map = evea->regmap;
if (evea->switch_lo) {
regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE, 0);
regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE, 0);
regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON,
ALO1OUTPOW_LO1_ON);
regmap_update_bits(map, ALO2OUTPOW,
ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON,
ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON);
} else {
regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE,
ADACSEQ1_MMUTE);
regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE,
ADACSEQ1_MMUTE);
regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, 0);
regmap_update_bits(map, ALO2OUTPOW,
ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON,
0);
}
return 0;
}
static int evea_update_switch_hp(struct evea_priv *evea)
{
struct regmap *map = evea->regmap;
if (evea->switch_hp) {
regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE, 0);
regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON,
AHPOUTPOW_HP_ON);
regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK,
ADAC1ODC_HP_DIS_RES_OFF);
} else {
regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK,
ADAC1ODC_HP_DIS_RES_ON);
regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE,
ADACSEQ1_MMUTE);
regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, 0);
}
return 0;
}
static void evea_update_switch_all(struct evea_priv *evea)
{
evea_update_switch_lin(evea);
evea_update_switch_lo(evea);
evea_update_switch_hp(evea);
}
static int evea_get_switch_lin(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = evea->switch_lin;
return 0;
}
static int evea_set_switch_lin(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
if (evea->switch_lin == ucontrol->value.integer.value[0])
return 0;
evea->switch_lin = ucontrol->value.integer.value[0];
return evea_update_switch_lin(evea);
}
static int evea_get_switch_lo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = evea->switch_lo;
return 0;
}
static int evea_set_switch_lo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
if (evea->switch_lo == ucontrol->value.integer.value[0])
return 0;
evea->switch_lo = ucontrol->value.integer.value[0];
return evea_update_switch_lo(evea);
}
static int evea_get_switch_hp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = evea->switch_hp;
return 0;
}
static int evea_set_switch_hp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
if (evea->switch_hp == ucontrol->value.integer.value[0])
return 0;
evea->switch_hp = ucontrol->value.integer.value[0];
return evea_update_switch_hp(evea);
}
static const struct snd_kcontrol_new evea_controls[] = {
SOC_SINGLE_BOOL_EXT("Line Capture Switch", 0,
evea_get_switch_lin, evea_set_switch_lin),
SOC_SINGLE_BOOL_EXT("Line Playback Switch", 0,
evea_get_switch_lo, evea_set_switch_lo),
SOC_SINGLE_BOOL_EXT("Headphone Playback Switch", 0,
evea_get_switch_hp, evea_set_switch_hp),
};
static int evea_codec_probe(struct snd_soc_component *component)
{
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
evea->switch_lin = 1;
evea->switch_lo = 1;
evea->switch_hp = 1;
evea_set_power_state_on(evea);
evea_update_switch_all(evea);
return 0;
}
static int evea_codec_suspend(struct snd_soc_component *component)
{
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
evea_set_power_state_off(evea);
reset_control_assert(evea->rst_adamv);
reset_control_assert(evea->rst_exiv);
reset_control_assert(evea->rst);
clk_disable_unprepare(evea->clk_exiv);
clk_disable_unprepare(evea->clk);
return 0;
}
static int evea_codec_resume(struct snd_soc_component *component)
{
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
int ret;
ret = clk_prepare_enable(evea->clk);
if (ret)
return ret;
ret = clk_prepare_enable(evea->clk_exiv);
if (ret)
goto err_out_clock;
ret = reset_control_deassert(evea->rst);
if (ret)
goto err_out_clock_exiv;
ret = reset_control_deassert(evea->rst_exiv);
if (ret)
goto err_out_reset;
ret = reset_control_deassert(evea->rst_adamv);
if (ret)
goto err_out_reset_exiv;
evea_set_power_state_on(evea);
evea_update_switch_all(evea);
return 0;
err_out_reset_exiv:
reset_control_assert(evea->rst_exiv);
err_out_reset:
reset_control_assert(evea->rst);
err_out_clock_exiv:
clk_disable_unprepare(evea->clk_exiv);
err_out_clock:
clk_disable_unprepare(evea->clk);
return ret;
}
static const struct snd_soc_component_driver soc_codec_evea = {
.probe = evea_codec_probe,
.suspend = evea_codec_suspend,
.resume = evea_codec_resume,
.dapm_widgets = evea_widgets,
.num_dapm_widgets = ARRAY_SIZE(evea_widgets),
.dapm_routes = evea_routes,
.num_dapm_routes = ARRAY_SIZE(evea_routes),
.controls = evea_controls,
.num_controls = ARRAY_SIZE(evea_controls),
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
};
static struct snd_soc_dai_driver soc_dai_evea[] = {
{
.name = DRV_NAME "-line1",
.playback = {
.stream_name = "Line Out 1",
.formats = EVEA_FORMATS,
.rates = EVEA_RATES,
.channels_min = 2,
.channels_max = 2,
},
.capture = {
.stream_name = "Line In 1",
.formats = EVEA_FORMATS,
.rates = EVEA_RATES,
.channels_min = 2,
.channels_max = 2,
},
},
{
.name = DRV_NAME "-hp1",
.playback = {
.stream_name = "Headphone 1",
.formats = EVEA_FORMATS,
.rates = EVEA_RATES,
.channels_min = 2,
.channels_max = 2,
},
},
{
.name = DRV_NAME "-lo2",
.playback = {
.stream_name = "Line Out 2",
.formats = EVEA_FORMATS,
.rates = EVEA_RATES,
.channels_min = 2,
.channels_max = 2,
},
},
};
static const struct regmap_config evea_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0xffc,
.cache_type = REGCACHE_NONE,
};
static int evea_probe(struct platform_device *pdev)
{
struct evea_priv *evea;
void __iomem *preg;
int ret;
evea = devm_kzalloc(&pdev->dev, sizeof(struct evea_priv), GFP_KERNEL);
if (!evea)
return -ENOMEM;
evea->clk = devm_clk_get(&pdev->dev, "evea");
if (IS_ERR(evea->clk))
return PTR_ERR(evea->clk);
evea->clk_exiv = devm_clk_get(&pdev->dev, "exiv");
if (IS_ERR(evea->clk_exiv))
return PTR_ERR(evea->clk_exiv);
evea->rst = devm_reset_control_get_shared(&pdev->dev, "evea");
if (IS_ERR(evea->rst))
return PTR_ERR(evea->rst);
evea->rst_exiv = devm_reset_control_get_shared(&pdev->dev, "exiv");
if (IS_ERR(evea->rst_exiv))
return PTR_ERR(evea->rst_exiv);
preg = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(preg))
return PTR_ERR(preg);
evea->regmap = devm_regmap_init_mmio(&pdev->dev, preg,
&evea_regmap_config);
if (IS_ERR(evea->regmap))
return PTR_ERR(evea->regmap);
ret = clk_prepare_enable(evea->clk);
if (ret)
return ret;
ret = clk_prepare_enable(evea->clk_exiv);
if (ret)
goto err_out_clock;
ret = reset_control_deassert(evea->rst);
if (ret)
goto err_out_clock_exiv;
ret = reset_control_deassert(evea->rst_exiv);
if (ret)
goto err_out_reset;
/* ADAMV will hangup if EXIV reset is asserted */
evea->rst_adamv = devm_reset_control_get_shared(&pdev->dev, "adamv");
if (IS_ERR(evea->rst_adamv)) {
ret = PTR_ERR(evea->rst_adamv);
goto err_out_reset_exiv;
}
ret = reset_control_deassert(evea->rst_adamv);
if (ret)
goto err_out_reset_exiv;
platform_set_drvdata(pdev, evea);
ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_evea,
soc_dai_evea, ARRAY_SIZE(soc_dai_evea));
if (ret)
goto err_out_reset_adamv;
return 0;
err_out_reset_adamv:
reset_control_assert(evea->rst_adamv);
err_out_reset_exiv:
reset_control_assert(evea->rst_exiv);
err_out_reset:
reset_control_assert(evea->rst);
err_out_clock_exiv:
clk_disable_unprepare(evea->clk_exiv);
err_out_clock:
clk_disable_unprepare(evea->clk);
return ret;
}
static void evea_remove(struct platform_device *pdev)
{
struct evea_priv *evea = platform_get_drvdata(pdev);
reset_control_assert(evea->rst_adamv);
reset_control_assert(evea->rst_exiv);
reset_control_assert(evea->rst);
clk_disable_unprepare(evea->clk_exiv);
clk_disable_unprepare(evea->clk);
}
static const struct of_device_id evea_of_match[] __maybe_unused = {
{ .compatible = "socionext,uniphier-evea", },
{}
};
MODULE_DEVICE_TABLE(of, evea_of_match);
static struct platform_driver evea_codec_driver = {
.driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(evea_of_match),
},
.probe = evea_probe,
.remove = evea_remove,
};
module_platform_driver(evea_codec_driver);
MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
MODULE_DESCRIPTION("UniPhier EVEA codec driver");
MODULE_LICENSE("GPL v2");